Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

????? add slice::array_chunks #72334

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
#![cfg_attr(not(test), feature(generator_trait))]
#![cfg_attr(test, feature(test))]
#![feature(allocator_api)]
#![feature(array_chunks)]
#![feature(allow_internal_unstable)]
#![feature(arbitrary_self_types)]
#![feature(box_patterns)]
Expand Down
2 changes: 2 additions & 0 deletions src/liballoc/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ use crate::borrow::ToOwned;
use crate::boxed::Box;
use crate::vec::Vec;

#[unstable(feature = "array_chunks", issue = "none")]
pub use core::slice::ArrayChunks;
#[stable(feature = "slice_get_slice", since = "1.28.0")]
pub use core::slice::SliceIndex;
#[stable(feature = "from_ref", since = "1.28.0")]
Expand Down
189 changes: 184 additions & 5 deletions src/libcore/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn windows(&self, size: usize) -> Windows<'_, T> {
assert!(size != 0);
assert_ne!(size, 0);
Windows { v: self, size }
}

Expand Down Expand Up @@ -708,7 +708,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
Chunks { v: self, chunk_size }
}

Expand Down Expand Up @@ -746,7 +746,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
ChunksMut { v: self, chunk_size }
}

Expand Down Expand Up @@ -783,7 +783,7 @@ impl<T> [T] {
#[stable(feature = "chunks_exact", since = "1.31.0")]
#[inline]
pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
let rem = self.len() % chunk_size;
let len = self.len() - rem;
let (fst, snd) = self.split_at(len);
Expand Down Expand Up @@ -828,13 +828,47 @@ impl<T> [T] {
#[stable(feature = "chunks_exact", since = "1.31.0")]
#[inline]
pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
let rem = self.len() % chunk_size;
let len = self.len() - rem;
let (fst, snd) = self.split_at_mut(len);
ChunksExactMut { v: fst, rem: snd, chunk_size }
}

/// Returns an iterator over `N` elements of the slice at a time, starting at the
/// beginning of the slice.
///
/// The chunks are slices and do not overlap. If `N` does not divide the length of the
/// slice, then the last up to `N-1` elements will be omitted and can be retrieved
/// from the `remainder` function of the iterator.
///
/// # Panics
///
/// Panics if `N` is 0.
///
/// # Examples
///
/// ```
/// #![feature(array_chunks)]
/// let slice = ['l', 'o', 'r', 'e', 'm'];
/// let mut iter = slice.array_chunks();
/// assert_eq!(iter.next().unwrap(), &['l', 'o']);
/// assert_eq!(iter.next().unwrap(), &['r', 'e']);
/// assert!(iter.next().is_none());
/// assert_eq!(iter.remainder(), &['m']);
/// ```
///
/// [`chunks`]: #method.chunks
#[unstable(feature = "array_chunks", issue = "none")]
#[inline]
pub fn array_chunks<const N: usize>(&self) -> ArrayChunks<'_, T, N> {
assert_ne!(N, 0);
let rem = self.len() % N;
let len = self.len() - rem;
let (fst, snd) = self.split_at(len);
ArrayChunks { v: fst, rem: snd }
}

/// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end
/// of the slice.
///
Expand Down Expand Up @@ -5152,6 +5186,151 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> {
}
}

/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a
/// time), starting at the beginning of the slice.
///
/// When the slice len is not evenly divided by the chunk size, the last
/// up to `chunk_size-1` elements will be omitted but can be retrieved from
/// the [`remainder`] function from the iterator.
///
/// This struct is created by the [`array_chunks`] method on [slices].
///
/// [`array_chunks`]: ../../std/primitive.slice.html#method.array_chunks
/// [`remainder`]: ../../std/slice/struct.ArrayChunks.html#method.remainder
/// [slices]: ../../std/primitive.slice.html
#[derive(Debug)]
#[unstable(feature = "array_chunks", issue = "none")]
pub struct ArrayChunks<'a, T: 'a, const N: usize> {
v: &'a [T],
rem: &'a [T],
}

impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
/// Returns the remainder of the original slice that is not going to be
/// returned by the iterator. The returned slice has at most `chunk_size-1`
/// elements.
#[unstable(feature = "array_chunks", issue = "none")]
pub fn remainder(&self) -> &'a [T] {
self.rem
}
}

// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
#[unstable(feature = "array_chunks", issue = "none")]
impl<T, const N: usize> Clone for ArrayChunks<'_, T, N> {
fn clone(&self) -> Self {
ArrayChunks { v: self.v, rem: self.rem }
}
}

#[unstable(feature = "array_chunks", issue = "none")]
impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> {
type Item = &'a [T; N];

#[inline]
fn next(&mut self) -> Option<&'a [T; N]> {
if self.v.len() < N {
None
} else {
let (fst, snd) = self.v.split_at(N);
self.v = snd;
// SAFETY: This is safe as fst is exactly N elements long.
let ptr = fst.as_ptr() as *const [T; N];
unsafe { Some(&*ptr) }
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.v.len() / N;
(n, Some(n))
}

#[inline]
fn count(self) -> usize {
self.len()
}

#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let (start, overflow) = n.overflowing_mul(N);
if start >= self.v.len() || overflow {
self.v = &[];
None
} else {
let (_, snd) = self.v.split_at(start);
self.v = snd;
self.next()
}
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[unstable(feature = "array_chunks", issue = "none")]
impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> {
#[inline]
fn next_back(&mut self) -> Option<&'a [T; N]> {
if self.v.len() < N {
None
} else {
let (fst, snd) = self.v.split_at(self.v.len() - N);
self.v = fst;
// SAFETY: This is safe as snd is exactly N elements long.
let ptr = snd.as_ptr() as *const [T; N];
unsafe { Some(&*ptr) }
}
}

#[inline]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let len = self.len();
if n >= len {
self.v = &[];
None
} else {
let start = (len - 1 - n) * N;
let end = start + N;
let nth_back = &self.v[start..end];
self.v = &self.v[..start];
// SAFETY: This is safe as snd is exactly N elements long.
let ptr = nth_back.as_ptr() as *const [T; N];
unsafe { Some(&*ptr) }
}
}
}

#[unstable(feature = "array_chunks", issue = "none")]
impl<T, const N: usize> ExactSizeIterator for ArrayChunks<'_, T, N> {
fn is_empty(&self) -> bool {
self.v.is_empty()
}
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<T, const N: usize> TrustedLen for ArrayChunks<'_, T, N> {}

#[unstable(feature = "array_chunks", issue = "none")]
impl<T, const N: usize> FusedIterator for ArrayChunks<'_, T, N> {}

#[doc(hidden)]
#[unstable(feature = "array_chunks", issue = "none")]
unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> {
unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T; N] {
let start = i * N;
let segment = from_raw_parts(self.v.as_ptr().add(start), N);
// SAFETY: This is safe as segment is exactly N elements long.
let ptr = segment.as_ptr() as *const [T; N];
&*ptr
}
fn may_have_side_effect() -> bool {
false
}
}

/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time), starting at the end of the slice.
///
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
#![feature(bool_to_option)]
#![feature(bound_cloned)]
#![feature(box_syntax)]
Expand Down
22 changes: 22 additions & 0 deletions src/libcore/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,28 @@ fn test_chunks_exact_mut_zip() {
assert_eq!(v1, [13, 14, 19, 20, 4]);
}

// FIXME(#71154)
// FIXME(const_generics)
// We can't yet use `v.array_chunks::<3>()`, so until either #71154 or a
// different PR implementing const arguments in type dependent paths lands,
// we can't yet test many uses.
#[test]
fn test_array_chunks_simple() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let mut c = <[i32]>::array_chunks(&v);
assert!(matches!(c.next(), Some(&[0, 1, 2])));
assert!(matches!(c.next(), Some(&[3, 4, 5])));
assert!(matches!(c.next(), None));

let v2: &[i32] = &[0, 1, 2, 3, 4];
let c2 = <[i32]>::array_chunks(&v2);
for &[a, b, c] in c2 {
assert_eq!(a, 0i32);
assert_eq!(b, 1i32);
assert_eq!(c, 2i32);
}
}

#[test]
fn test_rchunks_count() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
Expand Down