Skip to content

Commit c4a1d5d

Browse files
committed
extract code path shared between FromIterator and Extend
Previously FromIterator specialization called with_capacity() and then delegated to Extend which called reserve(). The reserve() is only needed when extending. This should reduce the amount of generated IR.
1 parent c2452de commit c4a1d5d

File tree

3 files changed

+35
-15
lines changed

3 files changed

+35
-15
lines changed

library/alloc/src/vec/mod.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use core::hash::{Hash, Hasher};
6262
use core::intrinsics::{arith_offset, assume};
6363
use core::iter;
6464
#[cfg(not(no_global_oom_handling))]
65-
use core::iter::FromIterator;
65+
use core::iter::{FromIterator, TrustedLen};
6666
use core::marker::PhantomData;
6767
use core::mem::{self, ManuallyDrop, MaybeUninit};
6868
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
@@ -2627,6 +2627,31 @@ impl<T, A: Allocator> Vec<T, A> {
26272627
}
26282628
}
26292629

2630+
/// Appends the iterator's items to the vec without allocating.
2631+
///
2632+
/// # Safety
2633+
///
2634+
/// The caller must ensure that `self` has sufficient spare capacity
2635+
/// to hold the items returned by the iterator.
2636+
///
2637+
/// The bound `I: TrustedLen` ensures that the caller can safely know
2638+
/// how much needs to be allocated.
2639+
#[cfg(not(no_global_oom_handling))]
2640+
unsafe fn extend_prealloc_trustedlen<I: TrustedLen<Item = T>>(&mut self, iterator: I) {
2641+
unsafe {
2642+
let mut ptr = self.as_mut_ptr().add(self.len());
2643+
let mut local_len = SetLenOnDrop::new(&mut self.len);
2644+
iterator.for_each(move |element| {
2645+
ptr::write(ptr, element);
2646+
ptr = ptr.offset(1);
2647+
// Since the loop executes user code which can panic we have to bump the length
2648+
// after each step.
2649+
// NB can't overflow since we would have had to alloc the address space
2650+
local_len.increment_len(1);
2651+
});
2652+
}
2653+
}
2654+
26302655
/// Creates a splicing iterator that replaces the specified range in the vector
26312656
/// with the given `replace_with` iterator and yields the removed items.
26322657
/// `replace_with` does not need to be the same length as `range`.

library/alloc/src/vec/spec_extend.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use crate::alloc::Allocator;
22
use core::iter::TrustedLen;
3-
use core::ptr::{self};
43
use core::slice::{self};
54

6-
use super::{IntoIter, SetLenOnDrop, Vec};
5+
use super::{IntoIter, Vec};
76

87
// Specialization trait used for Vec::extend
98
pub(super) trait SpecExtend<T, I> {
@@ -34,17 +33,10 @@ where
3433
(low, high)
3534
);
3635
self.reserve(additional);
36+
// Safety: We rely on the TrustedLen contract to know how much capacity needs to be
37+
// reserved. And we reserved at least that amount above.
3738
unsafe {
38-
let mut ptr = self.as_mut_ptr().add(self.len());
39-
let mut local_len = SetLenOnDrop::new(&mut self.len);
40-
iterator.for_each(move |element| {
41-
ptr::write(ptr, element);
42-
ptr = ptr.offset(1);
43-
// Since the loop executes user code which can panic we have to bump the pointer
44-
// after each step.
45-
// NB can't overflow since we would have had to alloc the address space
46-
local_len.increment_len(1);
47-
});
39+
self.extend_prealloc_trustedlen(iterator);
4840
}
4941
} else {
5042
// Per TrustedLen contract a `None` upper bound means that the iterator length

library/alloc/src/vec/spec_from_iter_nested.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ where
5252
// (via `with_capacity`) we do the same here.
5353
_ => panic!("capacity overflow"),
5454
};
55-
// reuse extend specialization for TrustedLen
56-
vector.spec_extend(iterator);
55+
// Safety: The TrustedLen contract together with the `with_capacity`
56+
// above guarantee that no further allocations should be needed to collect the iterator
57+
unsafe {
58+
vector.extend_prealloc_trustedlen(iterator);
59+
}
5760
vector
5861
}
5962
}

0 commit comments

Comments
 (0)