Skip to content

Commit 81d38b8

Browse files
committed
Reduce clones in RepeatN
1 parent 70ae0f9 commit 81d38b8

File tree

2 files changed

+158
-59
lines changed

2 files changed

+158
-59
lines changed

src/iter/repeat.rs

+94-59
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use super::plumbing::*;
22
use super::*;
3-
use std::iter;
3+
use std::num::NonZeroUsize;
4+
use std::{fmt, iter, mem};
45

56
/// Iterator adaptor for [the `repeat()` function](fn.repeat.html).
67
#[derive(Debug, Clone)]
78
pub struct Repeat<T> {
89
element: T,
910
}
1011

11-
/// Creates a parallel iterator that endlessly repeats `elt` (by
12+
/// Creates a parallel iterator that endlessly repeats `element` (by
1213
/// cloning it). Note that this iterator has "infinite" length, so
1314
/// typically you would want to use `zip` or `take` or some other
1415
/// means to shorten it, or consider using
@@ -22,8 +23,8 @@ pub struct Repeat<T> {
2223
/// let x: Vec<(i32, i32)> = repeat(22).zip(0..3).collect();
2324
/// assert_eq!(x, vec![(22, 0), (22, 1), (22, 2)]);
2425
/// ```
25-
pub fn repeat<T: Clone + Send>(elt: T) -> Repeat<T> {
26-
Repeat { element: elt }
26+
pub fn repeat<T: Clone + Send>(element: T) -> Repeat<T> {
27+
Repeat { element }
2728
}
2829

2930
impl<T> Repeat<T>
@@ -98,13 +99,12 @@ impl<T: Clone + Send> UnindexedProducer for RepeatProducer<T> {
9899
}
99100

100101
/// Iterator adaptor for [the `repeat_n()` function](fn.repeat_n.html).
101-
#[derive(Debug, Clone)]
102+
#[derive(Clone)]
102103
pub struct RepeatN<T> {
103-
element: T,
104-
count: usize,
104+
inner: RepeatNProducer<T>,
105105
}
106106

107-
/// Creates a parallel iterator that produces `n` repeats of `elt`
107+
/// Creates a parallel iterator that produces `n` repeats of `element`
108108
/// (by cloning it).
109109
///
110110
/// # Examples
@@ -115,22 +115,33 @@ pub struct RepeatN<T> {
115115
/// let x: Vec<(i32, i32)> = repeat_n(22, 3).zip(0..3).collect();
116116
/// assert_eq!(x, vec![(22, 0), (22, 1), (22, 2)]);
117117
/// ```
118-
pub fn repeat_n<T: Clone + Send>(elt: T, n: usize) -> RepeatN<T> {
119-
RepeatN {
120-
element: elt,
121-
count: n,
122-
}
118+
pub fn repeat_n<T: Clone + Send>(element: T, n: usize) -> RepeatN<T> {
119+
let inner = match NonZeroUsize::new(n) {
120+
Some(count) => RepeatNProducer::Repeats(element, count),
121+
None => RepeatNProducer::Empty,
122+
};
123+
RepeatN { inner }
123124
}
124125

125-
/// Creates a parallel iterator that produces `n` repeats of `elt`
126+
/// Creates a parallel iterator that produces `n` repeats of `element`
126127
/// (by cloning it).
127128
///
128129
/// Deprecated in favor of [`repeat_n`] for consistency with the standard library.
129130
#[deprecated(note = "use `repeat_n`")]
130-
pub fn repeatn<T: Clone + Send>(elt: T, n: usize) -> RepeatN<T> {
131-
RepeatN {
132-
element: elt,
133-
count: n,
131+
pub fn repeatn<T: Clone + Send>(element: T, n: usize) -> RepeatN<T> {
132+
repeat_n(element, n)
133+
}
134+
135+
impl<T: fmt::Debug> fmt::Debug for RepeatN<T> {
136+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137+
let mut dbg = f.debug_struct("RepeatN");
138+
if let RepeatNProducer::Repeats(element, count) = &self.inner {
139+
dbg.field("count", &count.get())
140+
.field("element", element)
141+
.finish()
142+
} else {
143+
dbg.field("count", &0usize).finish_non_exhaustive()
144+
}
134145
}
135146
}
136147

@@ -148,7 +159,7 @@ where
148159
}
149160

150161
fn opt_len(&self) -> Option<usize> {
151-
Some(self.count)
162+
Some(self.inner.len())
152163
}
153164
}
154165

@@ -167,86 +178,110 @@ where
167178
where
168179
CB: ProducerCallback<Self::Item>,
169180
{
170-
callback.callback(RepeatNProducer {
171-
element: self.element,
172-
count: self.count,
173-
})
181+
callback.callback(self.inner)
174182
}
175183

176184
fn len(&self) -> usize {
177-
self.count
185+
self.inner.len()
178186
}
179187
}
180188

181189
/// Producer for `RepeatN`.
182-
struct RepeatNProducer<T: Clone + Send> {
183-
element: T,
184-
count: usize,
190+
#[derive(Clone)]
191+
enum RepeatNProducer<T> {
192+
Repeats(T, NonZeroUsize),
193+
Empty,
185194
}
186195

187196
impl<T: Clone + Send> Producer for RepeatNProducer<T> {
188197
type Item = T;
189-
type IntoIter = Iter<T>;
198+
type IntoIter = Self;
190199

191200
fn into_iter(self) -> Self::IntoIter {
192-
Iter {
193-
element: self.element,
194-
count: self.count,
195-
}
201+
// We could potentially use `std::iter::RepeatN` with MSRV 1.82, but we have no way to
202+
// create an empty instance without a value in hand, like `repeat_n(value, 0)`.
203+
self
196204
}
197205

198206
fn split_at(self, index: usize) -> (Self, Self) {
199-
(
200-
RepeatNProducer {
201-
element: self.element.clone(),
202-
count: index,
203-
},
204-
RepeatNProducer {
205-
element: self.element,
206-
count: self.count - index,
207-
},
208-
)
207+
if let Self::Repeats(element, count) = self {
208+
assert!(index <= count.get());
209+
match (
210+
NonZeroUsize::new(index),
211+
NonZeroUsize::new(count.get() - index),
212+
) {
213+
(Some(left), Some(right)) => (
214+
Self::Repeats(element.clone(), left),
215+
Self::Repeats(element, right),
216+
),
217+
(Some(left), None) => (Self::Repeats(element, left), Self::Empty),
218+
(None, Some(right)) => (Self::Empty, Self::Repeats(element, right)),
219+
(None, None) => unreachable!(),
220+
}
221+
} else {
222+
assert!(index == 0);
223+
(Self::Empty, Self::Empty)
224+
}
209225
}
210226
}
211227

212-
/// Iterator for `RepeatN`.
213-
///
214-
/// This is conceptually like `std::iter::Take<std::iter::Repeat<T>>`, but
215-
/// we need `DoubleEndedIterator` and unconditional `ExactSizeIterator`.
216-
struct Iter<T: Clone> {
217-
element: T,
218-
count: usize,
219-
}
220-
221-
impl<T: Clone> Iterator for Iter<T> {
228+
impl<T: Clone> Iterator for RepeatNProducer<T> {
222229
type Item = T;
223230

224231
#[inline]
225232
fn next(&mut self) -> Option<T> {
226-
if self.count > 0 {
227-
self.count -= 1;
228-
Some(self.element.clone())
233+
if let Self::Repeats(element, count) = self {
234+
if let Some(rem) = NonZeroUsize::new(count.get() - 1) {
235+
*count = rem;
236+
Some(element.clone())
237+
} else {
238+
match mem::replace(self, Self::Empty) {
239+
Self::Repeats(element, _) => Some(element),
240+
Self::Empty => unreachable!(),
241+
}
242+
}
229243
} else {
230244
None
231245
}
232246
}
233247

248+
#[inline]
249+
fn nth(&mut self, n: usize) -> Option<T> {
250+
if let Self::Repeats(_, count) = self {
251+
if let Some(rem) = NonZeroUsize::new(count.get().saturating_sub(n)) {
252+
*count = rem;
253+
return self.next();
254+
}
255+
*self = Self::Empty;
256+
}
257+
None
258+
}
259+
234260
#[inline]
235261
fn size_hint(&self) -> (usize, Option<usize>) {
236-
(self.count, Some(self.count))
262+
let len = self.len();
263+
(len, Some(len))
237264
}
238265
}
239266

240-
impl<T: Clone> DoubleEndedIterator for Iter<T> {
267+
impl<T: Clone> DoubleEndedIterator for RepeatNProducer<T> {
241268
#[inline]
242269
fn next_back(&mut self) -> Option<T> {
243270
self.next()
244271
}
272+
273+
#[inline]
274+
fn nth_back(&mut self, n: usize) -> Option<T> {
275+
self.nth(n)
276+
}
245277
}
246278

247-
impl<T: Clone> ExactSizeIterator for Iter<T> {
279+
impl<T: Clone> ExactSizeIterator for RepeatNProducer<T> {
248280
#[inline]
249281
fn len(&self) -> usize {
250-
self.count
282+
match self {
283+
Self::Repeats(_, count) => count.get(),
284+
Self::Empty => 0,
285+
}
251286
}
252287
}

src/iter/test.rs

+64
Original file line numberDiff line numberDiff line change
@@ -2202,6 +2202,70 @@ fn check_repeat_n_zip_right() {
22022202
}
22032203
}
22042204

2205+
#[test]
2206+
fn count_repeat_n_clones() {
2207+
use std::sync::atomic::{AtomicUsize, Ordering};
2208+
2209+
static CLONES: AtomicUsize = AtomicUsize::new(0);
2210+
static DROPS: AtomicUsize = AtomicUsize::new(0);
2211+
2212+
struct Counter;
2213+
2214+
impl Clone for Counter {
2215+
fn clone(&self) -> Self {
2216+
CLONES.fetch_add(1, Ordering::Relaxed);
2217+
Counter
2218+
}
2219+
}
2220+
2221+
impl Drop for Counter {
2222+
fn drop(&mut self) {
2223+
DROPS.fetch_add(1, Ordering::Relaxed);
2224+
}
2225+
}
2226+
2227+
#[track_caller]
2228+
fn check(clones: usize, drops: usize) {
2229+
assert_eq!(CLONES.swap(0, Ordering::Relaxed), clones, "clones");
2230+
assert_eq!(DROPS.swap(0, Ordering::Relaxed), drops, "drops");
2231+
}
2232+
2233+
drop(repeat_n(Counter, 100));
2234+
check(0, 1);
2235+
2236+
let empty = repeat_n(Counter, 0);
2237+
check(0, 1);
2238+
let empty2 = empty.clone();
2239+
check(0, 0);
2240+
assert_eq!(empty.count(), 0);
2241+
assert_eq!(empty2.count(), 0);
2242+
check(0, 0);
2243+
2244+
let par_iter = repeat_n(Counter, 100);
2245+
let par_iter2 = par_iter.clone();
2246+
check(1, 0);
2247+
assert_eq!(par_iter.count(), 100);
2248+
check(99, 100);
2249+
assert_eq!(par_iter2.map(std::mem::forget).count(), 100);
2250+
check(99, 0);
2251+
2252+
// Clone once in `split_at` and again for the first item, leaving its unused tail.
2253+
// The other split doesn't have a tail, so it can avoid a clone.
2254+
let step99 = repeat_n(Counter, 100).step_by(99);
2255+
assert_eq!(step99.count(), 2);
2256+
check(2, 3);
2257+
2258+
// Same without any parallel splitting
2259+
let step99 = repeat_n(Counter, 100).step_by(99).with_min_len(2);
2260+
assert_eq!(step99.count(), 2);
2261+
check(1, 2);
2262+
2263+
// Clone once in `split_at` and again for both items, leaving both unused tails.
2264+
let step50 = repeat_n(Counter, 100).step_by(50);
2265+
assert_eq!(step50.count(), 2);
2266+
check(3, 4);
2267+
}
2268+
22052269
#[test]
22062270
fn check_empty() {
22072271
// drive_unindexed

0 commit comments

Comments
 (0)