diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 74bbd65924630..5a267e665380e 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -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))] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index c27c596e7975a..93d7eaaddec76 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -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 diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index dbac2c0bb18a6..7e9f627fbacb4 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -24,6 +24,7 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(exact_chunks)] +#![feature(slice_group_by)] extern crate alloc_system; extern crate core; diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 3b7eec38609d0..239fbea36aede 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -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]; diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 6f4c130d8f318..ac921932c97ea 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -802,6 +802,74 @@ impl [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(&self, pred: F) -> GroupBy + 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(&mut self, pred: F) -> GroupByMut + 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 @@ -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 { + // 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) { + if self.is_empty() { return (0, Some(0)) } + + let len = self.remaining_len(); + (1, Some(len)) + } + + fn last(mut self) -> Option { + 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 { + // 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 // diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 11765e3ef56ee..dffd4295c7d4b 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -44,6 +44,7 @@ #![feature(reverse_bits)] #![feature(iterator_find_map)] #![feature(slice_internals)] +#![feature(slice_group_by)] extern crate core; extern crate test;