Skip to content

Commit 8e6fcfd

Browse files
committed
feat: arrays and next_array
creates an ArrayVec type that handles uninitialised array data
1 parent a9e367f commit 8e6fcfd

File tree

4 files changed

+266
-0
lines changed

4 files changed

+266
-0
lines changed

src/array_impl.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/// An iterator that groups the items in arrays of a specific size.
2+
///
3+
/// See [`.arrays()`](crate::Itertools::arrays) for more information.
4+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
5+
pub struct Arrays<I: Iterator, const N: usize>
6+
{
7+
iter: I,
8+
buf: ArrayVec<I::Item, N>,
9+
}
10+
11+
impl<I: Iterator, const N: usize> Arrays<I, N> {
12+
pub fn new(iter: I) -> Self {
13+
Self { iter, buf: ArrayVec::new() }
14+
}
15+
}
16+
impl<I: Iterator, const N: usize> Arrays<I, N> where I::Item: Clone {
17+
pub fn remaining(self) -> Vec<I::Item> {
18+
self.buf.into_vec()
19+
}
20+
}
21+
22+
impl<I: Iterator, const N: usize> Iterator for Arrays<I, N> {
23+
type Item = [I::Item; N];
24+
25+
fn next(&mut self) -> Option<Self::Item> {
26+
// SAFETY:
27+
// ArrayVec::push_unchecked is safe as long as len < N.
28+
// This is guaranteed by the for loop
29+
unsafe {
30+
for _ in self.buf.len()..N {
31+
self.buf.push_unchecked(self.iter.next()?)
32+
}
33+
34+
Some(self.buf.take_unchecked())
35+
}
36+
}
37+
}
38+
39+
pub fn next_array<I: Iterator, const N: usize>(iter: &mut I) -> Option<[I::Item; N]> {
40+
let mut array_vec = ArrayVec::new();
41+
42+
// SAFETY:
43+
// ArrayVec::push_unchecked is safe as long as len < N.
44+
// This is guaranteed by the for loop
45+
unsafe {
46+
for _ in 0..N {
47+
array_vec.push_unchecked(iter.next()?)
48+
}
49+
}
50+
51+
array_vec.into_array()
52+
}
53+
54+
// ArrayVec is a safe wrapper around a [T; N].
55+
// It allows safely initialising an empty array
56+
pub struct ArrayVec<T, const N: usize> {
57+
data: std::mem::MaybeUninit<[T; N]>,
58+
len: usize,
59+
}
60+
61+
impl<T, const N: usize> Drop for ArrayVec<T, N> {
62+
fn drop(&mut self) {
63+
// SAFETY:
64+
// The contract of the ArrayVec ensures that data[..len] is initialised
65+
unsafe {
66+
let ptr = self.data.as_mut_ptr() as *mut T;
67+
drop(std::slice::from_raw_parts_mut(ptr, self.len));
68+
}
69+
}
70+
}
71+
72+
impl<T, const N: usize> ArrayVec<T, N> {
73+
pub const fn new() -> Self {
74+
Self {
75+
data: std::mem::MaybeUninit::uninit(),
76+
len: 0,
77+
}
78+
}
79+
80+
pub fn push(&mut self, v: T) {
81+
assert!(self.len < N);
82+
// SAFETY: asserts that len < N.
83+
unsafe { self.push_unchecked(v) }
84+
}
85+
86+
// Unsafety:
87+
// len must be less than N. If `len < N` is guaranteed, this operation is safe
88+
// This is because the contract of ArrayVec guarantees that if len < N, then the value
89+
// at len is valid and uninitialised.
90+
pub unsafe fn push_unchecked(&mut self, v: T) {
91+
// The contract of ArrayVec guarantees that the value at self.len, if < N,
92+
// is uninitialised, and therefore does not need dropping. So use write to
93+
// overwrite the value
94+
let ptr = (self.data.as_mut_ptr() as *mut T).add(self.len);
95+
std::ptr::write(ptr, v);
96+
self.len += 1;
97+
}
98+
99+
pub fn len(&self) -> usize {
100+
self.len
101+
}
102+
103+
pub fn into_array(self) -> Option<[T; N]> {
104+
if self.len == N {
105+
// SAFETY:
106+
// If len == N, then all the data is initialised and this is safe
107+
unsafe { Some(self.into_array_unchecked()) }
108+
} else {
109+
None
110+
}
111+
}
112+
113+
// Unsafety:
114+
// len must be equal to N. If `len == N` is guaranteed, this operation is safe.
115+
// This is because the contract of ArrayVec guarantees that if len == N, all the values
116+
// have been initialised correctly.
117+
unsafe fn into_array_unchecked(mut self) -> [T; N] {
118+
// move out without dropping
119+
let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
120+
std::mem::forget(self);
121+
data.assume_init()
122+
}
123+
124+
// Unsafety:
125+
// len must be equal to N. If `len == N` is guaranteed, this operation is safe.
126+
// This is because the contract of ArrayVec guarantees that if len == N, all the values
127+
// have been initialised correctly.
128+
unsafe fn take_unchecked(&mut self) -> [T; N] {
129+
let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
130+
self.len = 0;
131+
data.assume_init()
132+
}
133+
}
134+
135+
impl<T: Clone, const N: usize> ArrayVec<T, N> {
136+
pub fn into_vec(mut self) -> Vec<T> {
137+
unsafe {
138+
let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
139+
let len = self.len;
140+
std::mem::forget(self);
141+
std::slice::from_raw_parts(data.as_ptr() as *const T, len).to_vec()
142+
}
143+
}
144+
}

src/lib.rs

+57
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub mod structs {
112112
pub use crate::adaptors::{MapResults, Step};
113113
#[cfg(feature = "use_alloc")]
114114
pub use crate::adaptors::MultiProduct;
115+
pub use crate::array_impl::Arrays;
115116
#[cfg(feature = "use_alloc")]
116117
pub use crate::combinations::Combinations;
117118
#[cfg(feature = "use_alloc")]
@@ -181,6 +182,7 @@ pub use crate::sources::{repeat_call, unfold, iterate};
181182
pub use crate::with_position::Position;
182183
pub use crate::ziptuple::multizip;
183184
mod adaptors;
185+
mod array_impl;
184186
mod either_or_both;
185187
pub use crate::either_or_both::EitherOrBoth;
186188
#[doc(hidden)]
@@ -775,6 +777,42 @@ pub trait Itertools : Iterator {
775777
tuple_impl::tuples(self)
776778
}
777779

780+
/// Return an iterator that groups the items in arrays of a specific size
781+
///
782+
/// See also the method [`.next_array()`](#method.next_array).
783+
///
784+
/// ```
785+
/// use itertools::Itertools;
786+
/// let mut v = Vec::new();
787+
/// for [a, b] in (1..5).arrays() {
788+
/// v.push([a, b]);
789+
/// }
790+
/// assert_eq!(v, vec![[1, 2], [3, 4]]);
791+
///
792+
/// let mut it = (1..8).arrays();
793+
/// assert_eq!(Some([1, 2, 3]), it.next());
794+
/// assert_eq!(Some([4, 5, 6]), it.next());
795+
/// assert_eq!(None, it.next());
796+
/// // get any remaining data
797+
/// assert_eq!(vec![7], it.remaining());
798+
///
799+
/// // this requires a type hint
800+
/// let it = (1..7).arrays::<3>();
801+
/// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]);
802+
///
803+
/// // you can also specify the complete type
804+
/// use itertools::Arrays;
805+
/// use std::ops::Range;
806+
///
807+
/// let it: Arrays<Range<u32>, 3> = (1..7).arrays();
808+
/// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]);
809+
/// ```
810+
fn arrays<const N: usize>(self) -> array_impl::Arrays<Self, N>
811+
where Self: Sized + Iterator,
812+
{
813+
array_impl::Arrays::new(self)
814+
}
815+
778816
/// Split into an iterator pair that both yield all elements from
779817
/// the original iterator.
780818
///
@@ -1702,6 +1740,25 @@ pub trait Itertools : Iterator {
17021740
T::collect_from_iter_no_buf(self)
17031741
}
17041742

1743+
/// Advances the iterator and returns the next items grouped in an array of
1744+
/// a specific size.
1745+
///
1746+
/// If there are enough elements to be grouped in a tuple, then the tuple is
1747+
/// returned inside `Some`, otherwise `None` is returned.
1748+
///
1749+
/// ```
1750+
/// use itertools::Itertools;
1751+
///
1752+
/// let mut iter = 1..5;
1753+
///
1754+
/// assert_eq!(Some([1, 2]), iter.next_array());
1755+
/// ```
1756+
fn next_array<const N: usize>(&mut self) -> Option<[Self::Item; N]>
1757+
where Self: Sized + Iterator
1758+
{
1759+
array_impl::next_array(self)
1760+
}
1761+
17051762
/// Collects all items from the iterator into a tuple of a specific size
17061763
/// (up to 12).
17071764
///

tests/arrays.rs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use itertools::Itertools;
2+
3+
#[test]
4+
fn arrays() {
5+
let v = [1, 2, 3, 4, 5];
6+
let mut iter = v.iter().cloned().arrays();
7+
assert_eq!(Some([1]), iter.next());
8+
assert_eq!(Some([2]), iter.next());
9+
assert_eq!(Some([3]), iter.next());
10+
assert_eq!(Some([4]), iter.next());
11+
assert_eq!(Some([5]), iter.next());
12+
assert_eq!(None, iter.next());
13+
assert_eq!(Vec::<i32>::new(), iter.remaining());
14+
15+
let mut iter = v.iter().cloned().arrays();
16+
assert_eq!(Some([1, 2]), iter.next());
17+
assert_eq!(Some([3, 4]), iter.next());
18+
assert_eq!(None, iter.next());
19+
assert_eq!(vec![5], iter.remaining());
20+
21+
let mut iter = v.iter().cloned().arrays();
22+
assert_eq!(Some([1, 2, 3]), iter.next());
23+
assert_eq!(None, iter.next());
24+
assert_eq!(vec![4, 5], iter.remaining());
25+
26+
let mut iter = v.iter().cloned().arrays();
27+
assert_eq!(Some([1, 2, 3, 4]), iter.next());
28+
assert_eq!(None, iter.next());
29+
assert_eq!(vec![5], iter.remaining());
30+
}
31+
32+
#[test]
33+
fn next_array() {
34+
let v = [1, 2, 3, 4, 5];
35+
let mut iter = v.iter();
36+
assert_eq!(iter.next_array().map(|[&x, &y]| (x, y)), Some((1, 2)));
37+
assert_eq!(iter.next_array().map(|[&x, &y]| (x, y)), Some((3, 4)));
38+
assert_eq!(iter.next_array::<2>(), None);
39+
}

tests/quick.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,32 @@ quickcheck! {
10731073
}
10741074
}
10751075

1076+
quickcheck! {
1077+
fn equal_arrays_1(a: Vec<u8>) -> bool {
1078+
let x = a.chunks(1).map(|s| [&s[0]]);
1079+
let y = a.iter().arrays::<1>();
1080+
itertools::equal(x, y)
1081+
}
1082+
1083+
fn equal_arrays_2(a: Vec<u8>) -> bool {
1084+
let x = a.chunks(2).filter(|s| s.len() == 2).map(|s| [&s[0], &s[1]]);
1085+
let y = a.iter().arrays::<2>();
1086+
itertools::equal(x, y)
1087+
}
1088+
1089+
fn equal_arrays_3(a: Vec<u8>) -> bool {
1090+
let x = a.chunks(3).filter(|s| s.len() == 3).map(|s| [&s[0], &s[1], &s[2]]);
1091+
let y = a.iter().arrays::<3>();
1092+
itertools::equal(x, y)
1093+
}
1094+
1095+
fn equal_arrays_4(a: Vec<u8>) -> bool {
1096+
let x = a.chunks(4).filter(|s| s.len() == 4).map(|s| [&s[0], &s[1], &s[2], &s[3]]);
1097+
let y = a.iter().arrays::<4>();
1098+
itertools::equal(x, y)
1099+
}
1100+
}
1101+
10761102
// with_position
10771103
quickcheck! {
10781104
fn with_position_exact_size_1(a: Vec<u8>) -> bool {

0 commit comments

Comments
 (0)