Rollup merge of #75026 - JulianKnodt:array_windows, r=Amanieu
Add array_windows fn This mimicks the functionality added by array_chunks, and implements a const-generic form of `windows`. It makes egregious use of `unsafe`, but by necessity because the array must be re-interpreted as a slice of arrays, and unlike array_chunks this cannot be done by casting the original array once, since each time the index is advanced it needs to move one element, not `N`. I'm planning on adding more tests, but this should be good enough as a premise for the functionality. Notably: should there be more functions overwritten for the iterator implementation/in general? ~~I've marked the issue as #74985 as there is no corresponding exact issue for `array_windows`, but it's based of off `array_chunks`.~~ Edit: See Issue #75027 created by @lcnr for tracking issue ~~Do not merge until I add more tests, please.~~ r? @lcnr
This commit is contained in:
commit
23a677787e
@ -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)]
|
||||
|
@ -97,6 +97,8 @@ pub use core::slice::check_range;
|
||||
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")]
|
||||
|
@ -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<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)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[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<T, const N: usize> 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.
|
||||
///
|
||||
|
@ -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};
|
||||
|
||||
@ -1026,6 +1029,40 @@ impl<T> [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<const N: usize>(&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.
|
||||
///
|
||||
|
@ -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)]
|
||||
|
@ -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::<i32>();
|
||||
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];
|
||||
|
@ -5,21 +5,21 @@
|
||||
#![cfg_attr(min, feature(min_const_generics))]
|
||||
|
||||
trait SliceExt<T: Clone> {
|
||||
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 <T: Clone> SliceExt<T> 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<Self::Item> {
|
||||
// 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<usize> = vec![0; 100];
|
||||
|
||||
for array in v.as_slice().array_windows::<FOUR>() {
|
||||
for array in v.as_slice().array_windows_example::<FOUR>() {
|
||||
assert_eq!(array, [0, 0, 0, 0])
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user