Skip to content

Commit a891f6e

Browse files
committed
Introduce the GroupBy and GroupByMut Iterators
1 parent e413d89 commit a891f6e

File tree

7 files changed

+280
-0
lines changed

7 files changed

+280
-0
lines changed

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
#![feature(try_trait)]
141141
#![feature(type_alias_impl_trait)]
142142
#![feature(associated_type_bounds)]
143+
#![feature(slice_group_by)]
143144
// Allow testing this library
144145

145146
#[cfg(test)]

library/alloc/src/slice.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ pub use core::slice::{RChunks, RChunksExact, RChunksExactMut, RChunksMut};
118118
pub use core::slice::{RSplit, RSplitMut};
119119
#[stable(feature = "rust1", since = "1.0.0")]
120120
pub use core::slice::{RSplitN, RSplitNMut, SplitN, SplitNMut};
121+
#[unstable(feature = "slice_group_by", issue = "0")]
122+
pub use core::slice::{GroupBy, GroupByMut};
121123

122124
////////////////////////////////////////////////////////////////////////////////
123125
// Basic slice extension methods

library/alloc/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#![feature(iter_map_while)]
2222
#![feature(int_bits_const)]
2323
#![feature(vecdeque_binary_search)]
24+
#![feature(slice_group_by)]
2425

2526
use std::collections::hash_map::DefaultHasher;
2627
use std::hash::{Hash, Hasher};

library/alloc/tests/slice.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,3 +1898,30 @@ fn subslice_patterns() {
18981898
m!(&mut v, [..] => ());
18991899
m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4))));
19001900
}
1901+
1902+
#[test]
1903+
fn test_group_by() {
1904+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
1905+
1906+
let mut iter = slice.group_by(|a, b| a == b);
1907+
1908+
assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
1909+
1910+
assert_eq!(iter.remaining(), &[3, 3, 2, 2, 2]);
1911+
1912+
assert_eq!(iter.next(), Some(&[3, 3][..]));
1913+
assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
1914+
assert_eq!(iter.next(), None);
1915+
}
1916+
1917+
#[test]
1918+
fn test_group_by_rev() {
1919+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
1920+
1921+
let mut iter = slice.group_by(|a, b| a == b);
1922+
1923+
assert_eq!(iter.next_back(), Some(&[2, 2, 2][..]));
1924+
assert_eq!(iter.next_back(), Some(&[3, 3][..]));
1925+
assert_eq!(iter.next_back(), Some(&[1, 1, 1][..]));
1926+
assert_eq!(iter.next_back(), None);
1927+
}

library/core/src/slice/iter.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,3 +2967,183 @@ unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> {
29672967
false
29682968
}
29692969
}
2970+
2971+
macro_rules! group_by {
2972+
(struct $name:ident, $elem:ty, $mkslice:ident) => {
2973+
#[unstable(feature = "slice_group_by", issue = "0")]
2974+
impl<'a, T: 'a, P> $name<'a, T, P> {
2975+
#[inline]
2976+
fn is_empty(&self) -> bool {
2977+
self.ptr == self.end
2978+
}
2979+
2980+
#[inline]
2981+
fn remaining_len(&self) -> usize {
2982+
unsafe { self.end.offset_from(self.ptr) as usize }
2983+
}
2984+
}
2985+
2986+
#[unstable(feature = "slice_group_by", issue = "0")]
2987+
impl<'a, T: 'a, P> Iterator for $name<'a, T, P>
2988+
where P: FnMut(&T, &T) -> bool,
2989+
{
2990+
type Item = $elem;
2991+
2992+
fn next(&mut self) -> Option<Self::Item> {
2993+
// we use an unsafe block to avoid bounds checking here.
2994+
// this is safe because the only thing we do here is to get
2995+
// two elements at `ptr` and `ptr + 1`, bounds checking is done by hand.
2996+
unsafe {
2997+
if self.is_empty() { return None }
2998+
2999+
let mut i = 0;
3000+
let mut ptr = self.ptr;
3001+
3002+
// we need to get *two* contiguous elements so we check that:
3003+
// - the first element is at the `end - 1` position because
3004+
// - the second one will be read from `ptr + 1` that must
3005+
// be lower or equal to `end`
3006+
while ptr != self.end.sub(1) {
3007+
let a = &*ptr;
3008+
ptr = ptr.add(1);
3009+
let b = &*ptr;
3010+
3011+
i += 1;
3012+
3013+
if !(self.predicate)(a, b) {
3014+
let slice = $mkslice(self.ptr, i);
3015+
self.ptr = ptr;
3016+
return Some(slice)
3017+
}
3018+
}
3019+
3020+
// `i` is either `0` or the slice `length - 1` because either:
3021+
// - we have not entered the loop and so `i` is equal to `0`
3022+
// the slice length is necessarily `1` because we ensure it is not empty
3023+
// - we have entered the loop and we have not early returned
3024+
// so `i` is equal to the slice `length - 1`
3025+
let slice = $mkslice(self.ptr, i + 1);
3026+
self.ptr = self.end;
3027+
Some(slice)
3028+
}
3029+
}
3030+
3031+
fn size_hint(&self) -> (usize, Option<usize>) {
3032+
if self.is_empty() { return (0, Some(0)) }
3033+
let len = self.remaining_len();
3034+
(1, Some(len))
3035+
}
3036+
3037+
fn last(mut self) -> Option<Self::Item> {
3038+
self.next_back()
3039+
}
3040+
}
3041+
3042+
#[unstable(feature = "slice_group_by", issue = "0")]
3043+
impl<'a, T: 'a, P> DoubleEndedIterator for $name<'a, T, P>
3044+
where P: FnMut(&T, &T) -> bool,
3045+
{
3046+
fn next_back(&mut self) -> Option<Self::Item> {
3047+
// during the loop we retrieve two elements at `ptr` and `ptr - 1`.
3048+
unsafe {
3049+
if self.is_empty() { return None }
3050+
3051+
let mut i = 0;
3052+
// we ensure that the first element that will be read
3053+
// is not under `end` because `end` is out of bound.
3054+
let mut ptr = self.end.sub(1);
3055+
3056+
while ptr != self.ptr {
3057+
// we first get `a` that is at the left of `ptr`
3058+
// then `b` that is under the `ptr` position.
3059+
let a = &*ptr.sub(1);
3060+
let b = &*ptr;
3061+
3062+
i += 1;
3063+
3064+
if !(self.predicate)(a, b) {
3065+
// the slice to return starts at the `ptr` position
3066+
// and `i` is the length of it.
3067+
let slice = $mkslice(ptr, i);
3068+
3069+
// because `end` is always an invalid bound
3070+
// we use `ptr` as `end` for the future call to `next`.
3071+
self.end = ptr;
3072+
return Some(slice)
3073+
}
3074+
3075+
ptr = ptr.sub(1);
3076+
}
3077+
3078+
let slice = $mkslice(self.ptr, i + 1);
3079+
self.ptr = self.end;
3080+
Some(slice)
3081+
}
3082+
}
3083+
}
3084+
3085+
#[unstable(feature = "slice_group_by", issue = "0")]
3086+
impl<'a, T: 'a, P> FusedIterator for $name<'a, T, P>
3087+
where P: FnMut(&T, &T) -> bool,
3088+
{ }
3089+
}
3090+
}
3091+
3092+
/// An iterator over slice in (non-overlapping) chunks separated by a predicate.
3093+
///
3094+
/// This struct is created by the [`group_by`] method on [slices].
3095+
///
3096+
/// [`group_by`]: ../../std/primitive.slice.html#method.group_by
3097+
/// [slices]: ../../std/primitive.slice.html
3098+
#[unstable(feature = "slice_group_by", issue = "0")]
3099+
#[derive(Debug)] // FIXME implement Debug to be more user friendly
3100+
pub struct GroupBy<'a, T: 'a, P> {
3101+
ptr: *const T,
3102+
end: *const T,
3103+
predicate: P,
3104+
_phantom: marker::PhantomData<&'a T>,
3105+
}
3106+
3107+
#[unstable(feature = "slice_group_by", issue = "0")]
3108+
impl<'a, T: 'a, P> GroupBy<'a, T, P>
3109+
where P: FnMut(&T, &T) -> bool,
3110+
{
3111+
/// Returns the remainder of the original slice that is going to be
3112+
/// returned by the iterator.
3113+
pub fn remaining(&self) -> &[T] {
3114+
let len = self.remaining_len();
3115+
unsafe { from_raw_parts(self.ptr, len) }
3116+
}
3117+
}
3118+
3119+
group_by!{ struct GroupBy, &'a [T], from_raw_parts }
3120+
3121+
/// An iterator over slice in (non-overlapping) mutable chunks separated
3122+
/// by a predicate.
3123+
///
3124+
/// This struct is created by the [`group_by_mut`] method on [slices].
3125+
///
3126+
/// [`group_by_mut`]: ../../std/primitive.slice.html#method.group_by_mut
3127+
/// [slices]: ../../std/primitive.slice.html
3128+
#[unstable(feature = "slice_group_by", issue = "0")]
3129+
#[derive(Debug)] // FIXME implement Debug to be more user friendly
3130+
pub struct GroupByMut<'a, T: 'a, P> {
3131+
ptr: *mut T,
3132+
end: *mut T,
3133+
predicate: P,
3134+
_phantom: marker::PhantomData<&'a T>,
3135+
}
3136+
3137+
#[unstable(feature = "slice_group_by", issue = "0")]
3138+
impl<'a, T: 'a, P> GroupByMut<'a, T, P>
3139+
where P: FnMut(&T, &T) -> bool,
3140+
{
3141+
/// Returns the remainder of the original slice that is going to be
3142+
/// returned by the iterator.
3143+
pub fn into_remaining(self) -> &'a mut [T] {
3144+
let len = self.remaining_len();
3145+
unsafe { from_raw_parts_mut(self.ptr, len) }
3146+
}
3147+
}
3148+
3149+
group_by!{ struct GroupByMut, &'a mut [T], from_raw_parts_mut }

library/core/src/slice/mod.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,74 @@ impl<T> [T] {
12071207
RChunksExactMut::new(self, chunk_size)
12081208
}
12091209

1210+
/// Returns an iterator over the slice producing non-overlapping runs
1211+
/// of elements using the predicate to separate them.
1212+
///
1213+
/// The predicate is called on two elements following themselves,
1214+
/// it means the predicate is called on `slice[0]` and `slice[1]`
1215+
/// then on `slice[1]` and `slice[2]` and so on.
1216+
///
1217+
/// # Examples
1218+
///
1219+
/// ```
1220+
/// #![feature(slice_group_by)]
1221+
///
1222+
/// let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
1223+
///
1224+
/// let mut iter = slice.group_by(|a, b| a == b);
1225+
///
1226+
/// assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
1227+
/// assert_eq!(iter.next(), Some(&[3, 3][..]));
1228+
/// assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
1229+
/// assert_eq!(iter.next(), None);
1230+
/// ```
1231+
#[unstable(feature = "slice_group_by", issue = "0")]
1232+
#[inline]
1233+
pub fn group_by<F>(&self, pred: F) -> GroupBy<T, F>
1234+
where F: FnMut(&T, &T) -> bool
1235+
{
1236+
GroupBy {
1237+
ptr: self.as_ptr(),
1238+
end: unsafe { self.as_ptr().add(self.len()) },
1239+
predicate: pred,
1240+
_phantom: marker::PhantomData,
1241+
}
1242+
}
1243+
1244+
/// Returns an iterator over the slice producing non-overlapping mutable
1245+
/// runs of elements using the predicate to separate them.
1246+
///
1247+
/// The predicate is called on two elements following themselves,
1248+
/// it means the predicate is called on `slice[0]` and `slice[1]`
1249+
/// then on `slice[1]` and `slice[2]` and so on.
1250+
///
1251+
/// # Examples
1252+
///
1253+
/// ```
1254+
/// #![feature(slice_group_by)]
1255+
///
1256+
/// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
1257+
///
1258+
/// let mut iter = slice.group_by_mut(|a, b| a == b);
1259+
///
1260+
/// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
1261+
/// assert_eq!(iter.next(), Some(&mut [3, 3][..]));
1262+
/// assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
1263+
/// assert_eq!(iter.next(), None);
1264+
/// ```
1265+
#[unstable(feature = "slice_group_by", issue = "0")]
1266+
#[inline]
1267+
pub fn group_by_mut<F>(&mut self, pred: F) -> GroupByMut<T, F>
1268+
where F: FnMut(&T, &T) -> bool
1269+
{
1270+
GroupByMut {
1271+
ptr: self.as_mut_ptr(),
1272+
end: unsafe { self.as_mut_ptr().add(self.len()) },
1273+
predicate: pred,
1274+
_phantom: marker::PhantomData,
1275+
}
1276+
}
1277+
12101278
/// Divides one slice into two at an index.
12111279
///
12121280
/// The first will contain all indices from `[0, mid)` (excluding

library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#![feature(nonzero_leading_trailing_zeros)]
6666
#![feature(const_option)]
6767
#![feature(integer_atomics)]
68+
#![feature(slice_group_by)]
6869
#![deny(unsafe_op_in_unsafe_fn)]
6970

7071
extern crate test;

0 commit comments

Comments
 (0)