Skip to content

Commit c311241

Browse files
Auto merge of #149751 - scottmcm:spec-repeat-n-fold, r=<try>
Another attempt at `Vec::extend_with` via `iter::repeat_n`
2 parents ba2142a + 3d96b4f commit c311241

File tree

4 files changed

+95
-30
lines changed

4 files changed

+95
-30
lines changed

library/alloc/src/vec/mod.rs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3499,33 +3499,10 @@ impl<T, A: Allocator, const N: usize> Vec<[T; N], A> {
34993499

35003500
impl<T: Clone, A: Allocator> Vec<T, A> {
35013501
#[cfg(not(no_global_oom_handling))]
3502+
#[inline]
35023503
/// Extend the vector by `n` clones of value.
35033504
fn extend_with(&mut self, n: usize, value: T) {
3504-
self.reserve(n);
3505-
3506-
unsafe {
3507-
let mut ptr = self.as_mut_ptr().add(self.len());
3508-
// Use SetLenOnDrop to work around bug where compiler
3509-
// might not realize the store through `ptr` through self.set_len()
3510-
// don't alias.
3511-
let mut local_len = SetLenOnDrop::new(&mut self.len);
3512-
3513-
// Write all elements except the last one
3514-
for _ in 1..n {
3515-
ptr::write(ptr, value.clone());
3516-
ptr = ptr.add(1);
3517-
// Increment the length in every step in case clone() panics
3518-
local_len.increment_len(1);
3519-
}
3520-
3521-
if n > 0 {
3522-
// We can write the last element directly without cloning needlessly
3523-
ptr::write(ptr, value);
3524-
local_len.increment_len(1);
3525-
}
3526-
3527-
// len set by scope guard
3528-
}
3505+
self.extend_trusted(iter::repeat_n(value, n))
35293506
}
35303507
}
35313508

@@ -3907,16 +3884,29 @@ impl<T, A: Allocator> Vec<T, A> {
39073884
(low, high)
39083885
);
39093886
self.reserve(additional);
3910-
unsafe {
3911-
let ptr = self.as_mut_ptr();
3912-
let mut local_len = SetLenOnDrop::new(&mut self.len);
3913-
iterator.for_each(move |element| {
3887+
3888+
// Split out so it's generic only on T, not on the iterator (or allocator)
3889+
#[inline]
3890+
unsafe fn make_write_element_fn<T>(
3891+
ptr: *mut T,
3892+
mut local_len: SetLenOnDrop<'_>,
3893+
) -> impl FnMut(T) {
3894+
move |element| unsafe {
39143895
ptr::write(ptr.add(local_len.current_len()), element);
39153896
// Since the loop executes user code which can panic we have to update
39163897
// the length every step to correctly drop what we've written.
39173898
// NB can't overflow since we would have had to alloc the address space
39183899
local_len.increment_len(1);
3919-
});
3900+
}
3901+
}
3902+
3903+
let ptr = self.as_mut_ptr();
3904+
let local_len = SetLenOnDrop::new(&mut self.len);
3905+
3906+
// SAFETY: we just allocated enough space and because it's `TrustedLen`
3907+
// we know it can't run extra times.
3908+
unsafe {
3909+
iterator.for_each(make_write_element_fn(ptr, local_len));
39203910
}
39213911
} else {
39223912
// Per TrustedLen contract a `None` upper bound means that the iterator length

library/core/src/clone.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ pub const trait Clone: Sized {
282282
#[marker]
283283
pub const unsafe trait TrivialClone: [const] Clone {}
284284

285+
pub(crate) fn trivial_clone<T: TrivialClone>(x: &T) -> T {
286+
// SAFETY: the point of the trait is that this is sound.
287+
unsafe { crate::ptr::read(x) }
288+
}
289+
285290
/// Derive macro generating an impl of the trait `Clone`.
286291
#[rustc_builtin_macro]
287292
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]

library/core/src/iter/sources/repeat_n.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::clone::{TrivialClone, trivial_clone};
12
use crate::fmt;
23
use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator};
34
use crate::num::NonZero;
@@ -153,6 +154,50 @@ impl<A: Clone> Iterator for RepeatN<A> {
153154
fn count(self) -> usize {
154155
self.len()
155156
}
157+
158+
#[inline]
159+
fn fold<B, F>(self, init: B, f: F) -> B
160+
where
161+
Self: Sized,
162+
F: FnMut(B, Self::Item) -> B,
163+
{
164+
if let Some(inner) = self.inner { SpecFold::spec_fold(inner, init, f) } else { init }
165+
}
166+
}
167+
168+
trait SpecFold: Sized {
169+
fn spec_fold<B, F>(inner: RepeatNInner<Self>, init: B, f: F) -> B
170+
where
171+
F: FnMut(B, Self) -> B;
172+
}
173+
174+
impl<A: Clone> SpecFold for A {
175+
default fn spec_fold<B, F>(inner: RepeatNInner<A>, mut init: B, mut f: F) -> B
176+
where
177+
F: FnMut(B, Self) -> B,
178+
{
179+
for _ in 1..inner.count.get() {
180+
init = f(init, inner.element.clone());
181+
}
182+
f(init, inner.element)
183+
}
184+
}
185+
186+
impl<A: TrivialClone> SpecFold for A {
187+
fn spec_fold<B, F>(inner: RepeatNInner<A>, mut init: B, mut f: F) -> B
188+
where
189+
F: FnMut(B, Self) -> B,
190+
{
191+
let RepeatNInner { element, count } = inner;
192+
let mut n = count.get();
193+
loop {
194+
init = f(init, trivial_clone(&element));
195+
n -= 1;
196+
if n == 0 {
197+
return init;
198+
}
199+
}
200+
}
156201
}
157202

158203
#[stable(feature = "iter_repeat_n", since = "1.82.0")]

library/coretests/tests/iter/sources.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ fn test_repeat_with_take_collect() {
7979
assert_eq!(v, vec![1, 2, 4, 8, 16]);
8080
}
8181

82+
#[test]
83+
fn test_repeat_n_trivial_clone_for_each() {
84+
let mut n = 0;
85+
repeat_n(123_u32, 10).for_each(|_| n += 1);
86+
assert_eq!(n, 10);
87+
}
88+
89+
#[test]
90+
fn test_repeat_n_custom_clone_for_each() {
91+
#[derive(Copy)]
92+
struct CloneCounter<'a>(&'a Cell<u32>);
93+
impl Clone for CloneCounter<'_> {
94+
fn clone(&self) -> Self {
95+
self.0.set(self.0.get() + 1);
96+
*self
97+
}
98+
}
99+
100+
let clone_count = Cell::new(0);
101+
let mut n = 0;
102+
repeat_n(CloneCounter(&clone_count), 10).for_each(|_| n += 1);
103+
assert_eq!(n, 10);
104+
assert_eq!(clone_count.get(), 9);
105+
}
106+
82107
#[test]
83108
fn test_successors() {
84109
let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10));

0 commit comments

Comments
 (0)