Skip to content
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 @@ -122,6 +122,7 @@
#![feature(inclusive_range_methods)]
#![feature(rustc_const_unstable)]
#![feature(const_vec_new)]
#![feature(slice_group_by)]

#![cfg_attr(not(test), feature(fn_traits, i128))]
#![cfg_attr(test, feature(test))]
Expand Down
2 changes: 2 additions & 0 deletions src/liballoc/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ pub use core::slice::{from_ref, from_mut};
pub use core::slice::SliceIndex;
#[unstable(feature = "exact_chunks", issue = "47115")]
pub use core::slice::{ExactChunks, ExactChunksMut};
#[unstable(feature = "slice_group_by", issue = "0")]
pub use core::slice::{GroupBy, GroupByMut};

////////////////////////////////////////////////////////////////////////////////
// Basic slice extension methods
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(exact_chunks)]
#![feature(slice_group_by)]

extern crate alloc_system;
extern crate core;
Expand Down
52 changes: 52 additions & 0 deletions src/liballoc/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,58 @@ fn test_exact_chunksator_0() {
let _it = v.exact_chunks(0);
}

#[test]
fn test_group_by() {
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];

let mut iter = slice.group_by(|a, b| a == b);

assert_eq!(iter.next(), Some(&[1, 1, 1][..]));

assert_eq!(iter.remaining(), &[3, 3, 2, 2, 2]);

assert_eq!(iter.next(), Some(&[3, 3][..]));
assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
assert_eq!(iter.next(), None);
}

#[test]
fn test_group_by_rev() {
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];

let mut iter = slice.group_by(|a, b| a == b);

assert_eq!(iter.next_back(), Some(&[2, 2, 2][..]));
assert_eq!(iter.next_back(), Some(&[3, 3][..]));
assert_eq!(iter.next_back(), Some(&[1, 1, 1][..]));
assert_eq!(iter.next_back(), None);
}

#[derive(Debug, Eq)]
enum Guard {
Valid(i32),
Invalid(i32),
}

impl PartialEq for Guard {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Guard::Valid(_), Guard::Valid(_)) => true,
(a, b) => panic!("denied read on Guard::Invalid variant ({:?}, {:?})", a, b),
}
}
}

#[test]
fn test_group_by_overflow() {
let slice = &[Guard::Invalid(0), Guard::Valid(1), Guard::Valid(2), Guard::Invalid(3)];

let mut iter = (&slice[1..3]).group_by(|a, b| a == b);

assert_eq!(iter.next(), Some(&[Guard::Valid(1), Guard::Valid(2)][..]));
assert_eq!(iter.next(), None);
}

#[test]
fn test_reverse_part() {
let mut values = [1, 2, 3, 4, 5];
Expand Down
249 changes: 249 additions & 0 deletions src/libcore/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,74 @@ impl<T> [T] {
ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size}
}

/// Returns an iterator over the slice producing non-overlapping runs
/// of elements using the predicate to separate them.
///
/// The predicate is called on two elements following themselves,
/// it means the predicate is called on `slice[0]` and `slice[1]`
/// then on `slice[1]` and `slice[2]` and so on.
///
/// # Examples
///
/// ```
/// #![feature(slice_group_by)]
///
/// let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
///
/// let mut iter = slice.group_by(|a, b| a == b);
///
/// assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
/// assert_eq!(iter.next(), Some(&[3, 3][..]));
/// assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
/// assert_eq!(iter.next(), None);
/// ```
#[unstable(feature = "slice_group_by", issue = "0")]
#[inline]
pub fn group_by<F>(&self, pred: F) -> GroupBy<T, F>
where F: FnMut(&T, &T) -> bool
{
GroupBy {
ptr: self.as_ptr(),
end: unsafe { self.as_ptr().add(self.len()) },
predicate: pred,
_phantom: marker::PhantomData,
}
}

/// Returns an iterator over the slice producing non-overlapping mutable
/// runs of elements using the predicate to separate them.
///
/// The predicate is called on two elements following themselves,
/// it means the predicate is called on `slice[0]` and `slice[1]`
/// then on `slice[1]` and `slice[2]` and so on.
///
/// # Examples
///
/// ```
/// #![feature(slice_group_by)]
///
/// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
///
/// let mut iter = slice.group_by_mut(|a, b| a == b);
///
/// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
/// assert_eq!(iter.next(), Some(&mut [3, 3][..]));
/// assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
/// assert_eq!(iter.next(), None);
/// ```
#[unstable(feature = "slice_group_by", issue = "0")]
#[inline]
pub fn group_by_mut<F>(&mut self, pred: F) -> GroupByMut<T, F>
where F: FnMut(&T, &T) -> bool
{
GroupByMut {
ptr: self.as_mut_ptr(),
end: unsafe { self.as_mut_ptr().add(self.len()) },
predicate: pred,
_phantom: marker::PhantomData,
}
}

/// Divides one slice into two at an index.
///
/// The first will contain all indices from `[0, mid)` (excluding
Expand Down Expand Up @@ -3859,6 +3927,187 @@ unsafe impl<'a, T> TrustedRandomAccess for ExactChunksMut<'a, T> {
fn may_have_side_effect() -> bool { false }
}

macro_rules! group_by {
(struct $name:ident, $elem:ty, $mkslice:ident) => {
#[unstable(feature = "slice_group_by", issue = "0")]
impl<'a, T: 'a, P> $name<'a, T, P> {
#[inline]
fn is_empty(&self) -> bool {
self.ptr == self.end
}

#[inline]
fn remaining_len(&self) -> usize {
unsafe { self.end.offset_from(self.ptr) as usize }
}
}

#[unstable(feature = "slice_group_by", issue = "0")]
impl<'a, T: 'a, P> Iterator for $name<'a, T, P>
where P: FnMut(&T, &T) -> bool,
{
type Item = $elem;

fn next(&mut self) -> Option<Self::Item> {
// we use an unsafe block to avoid bounds checking here.
// this is safe because the only thing we do here is to get
// two elements at `ptr` and `ptr + 1`, bounds checking is done by hand.
unsafe {
if self.is_empty() { return None }

let mut i = 0;
let mut ptr = self.ptr;

// we need to get *two* contiguous elements so we check that:
// - the first element is at the `end - 1` position because
// - the second one will be read from `ptr + 1` that must
// be lower or equal to `end`
while ptr != self.end.sub(1) {
let a = &*ptr;
ptr = ptr.add(1);
let b = &*ptr;

i += 1;

if !(self.predicate)(a, b) {
let slice = $mkslice(self.ptr, i);
self.ptr = ptr;
return Some(slice)
}
}

// `i` is either `0` or the slice `length - 1` because either:
// - we have not entered the loop and so `i` is equal to `0`
// the slice length is necessarily `1` because we ensure it is not empty
// - we have entered the loop and we have not early returned
// so `i` is equal to the slice `length - 1`
let slice = $mkslice(self.ptr, i + 1);
self.ptr = self.end;
Some(slice)
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
if self.is_empty() { return (0, Some(0)) }

let len = self.remaining_len();
(1, Some(len))
}

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

#[unstable(feature = "slice_group_by", issue = "0")]
impl<'a, T: 'a, P> DoubleEndedIterator for $name<'a, T, P>
where P: FnMut(&T, &T) -> bool,
{
fn next_back(&mut self) -> Option<Self::Item> {
// during the loop we retrieve two elements at `ptr` and `ptr - 1`.
unsafe {
if self.is_empty() { return None }

let mut i = 0;
// we ensure that the first element that will be read
// is not under `end` because `end` is out of bound.
let mut ptr = self.end.sub(1);

while ptr != self.ptr {
// we first get `a` that is at the left of `ptr`
// then `b` that is under the `ptr` position.
let a = &*ptr.sub(1);
let b = &*ptr;

i += 1;

if !(self.predicate)(a, b) {
// the slice to return starts at the `ptr` position
// and `i` is the length of it.
let slice = $mkslice(ptr, i);

// because `end` is always an invalid bound
// we use `ptr` as `end` for the future call to `next`.
self.end = ptr;
return Some(slice)
}

ptr = ptr.sub(1);
}

let slice = $mkslice(self.ptr, i + 1);
self.ptr = self.end;
Some(slice)
}
}
}

#[unstable(feature = "slice_group_by", issue = "0")]
impl<'a, T: 'a, P> FusedIterator for $name<'a, T, P>
where P: FnMut(&T, &T) -> bool,
{ }
}
}

/// An iterator over slice in (non-overlapping) chunks separated by a predicate.
///
/// This struct is created by the [`group_by`] method on [slices].
///
/// [`group_by`]: ../../std/primitive.slice.html#method.group_by
/// [slices]: ../../std/primitive.slice.html
#[unstable(feature = "slice_group_by", issue = "0")]
#[derive(Debug)] // FIXME implement Debug to be more user friendly
pub struct GroupBy<'a, T: 'a, P> {
ptr: *const T,
end: *const T,
predicate: P,
_phantom: marker::PhantomData<&'a T>,
}

#[unstable(feature = "slice_group_by", issue = "0")]
impl<'a, T: 'a, P> GroupBy<'a, T, P>
where P: FnMut(&T, &T) -> bool,
{
/// Returns the remainder of the original slice that is going to be
/// returned by the iterator.
pub fn remaining(&self) -> &[T] {
let len = self.remaining_len();
unsafe { from_raw_parts(self.ptr, len) }
}
}

group_by!{ struct GroupBy, &'a [T], from_raw_parts }

/// An iterator over slice in (non-overlapping) mutable chunks separated
/// by a predicate.
///
/// This struct is created by the [`group_by_mut`] method on [slices].
///
/// [`group_by_mut`]: ../../std/primitive.slice.html#method.group_by_mut
/// [slices]: ../../std/primitive.slice.html
#[unstable(feature = "slice_group_by", issue = "0")]
#[derive(Debug)] // FIXME implement Debug to be more user friendly
pub struct GroupByMut<'a, T: 'a, P> {
ptr: *mut T,
end: *mut T,
predicate: P,
_phantom: marker::PhantomData<&'a T>,
}

#[unstable(feature = "slice_group_by", issue = "0")]
impl<'a, T: 'a, P> GroupByMut<'a, T, P>
where P: FnMut(&T, &T) -> bool,
{
/// Returns the remainder of the original slice that is going to be
/// returned by the iterator.
pub fn into_remaining(self) -> &'a mut [T] {
let len = self.remaining_len();
unsafe { from_raw_parts_mut(self.ptr, len) }
}
}

group_by!{ struct GroupByMut, &'a mut [T], from_raw_parts_mut }

//
// Free functions
//
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#![feature(reverse_bits)]
#![feature(iterator_find_map)]
#![feature(slice_internals)]
#![feature(slice_group_by)]

extern crate core;
extern crate test;
Expand Down