Skip to content

Commit 22868b8

Browse files
committed
Optimize extend() to avoid unnecessary capacity checks
1 parent beb652d commit 22868b8

File tree

1 file changed

+27
-1
lines changed

1 file changed

+27
-1
lines changed

src/lib.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,8 +824,26 @@ impl<T> ThinVec<T> {
824824
if old_len == self.capacity() {
825825
self.reserve(1);
826826
}
827+
unsafe {
828+
// SAFETY: reserve() ensures sufficient capacity.
829+
self.push_reserved(val);
830+
}
831+
}
832+
833+
/// Appends an element to the back like `push`,
834+
/// but assumes that sufficient capacity has already been reserved, i.e.
835+
/// `len() < capacity()`.
836+
///
837+
/// # Safety
838+
///
839+
/// - Capacity must be reserved in advance such that `capacity() > len()`.
840+
pub unsafe fn push_reserved(&mut self, val: T) {
841+
let old_len = self.len();
842+
827843
unsafe {
828844
ptr::write(self.data_raw().add(old_len), val);
845+
846+
// SAFETY: capacity > len >= 0, so capacity != 0, so this is not a singleton.
829847
self.set_len_non_singleton(old_len + 1);
830848
}
831849
}
@@ -1927,11 +1945,19 @@ impl<T> Extend<T> for ThinVec<T> {
19271945
where
19281946
I: IntoIterator<Item = T>,
19291947
{
1930-
let iter = iter.into_iter();
1948+
let mut iter = iter.into_iter();
19311949
let hint = iter.size_hint().0;
19321950
if hint > 0 {
19331951
self.reserve(hint);
19341952
}
1953+
for x in iter.by_ref().take(hint) {
1954+
// SAFETY: `reserve(hint)` ensures the next `hint` calls of `push_reserved`
1955+
// have sufficient capacity.
1956+
unsafe { self.push_reserved(x); }
1957+
}
1958+
1959+
// if the hint underestimated the iterator length,
1960+
// push the remaining items with capacity check each time.
19351961
for x in iter {
19361962
self.push(x);
19371963
}

0 commit comments

Comments
 (0)