API: Panic in .extend() and from_iter on capacity exceeded

This regresses performance of the .extend(s) benchmark where s is a
slice; to compensate add a private extend_from_slice method that we can
use where possible (clone_from, try_from).
This commit is contained in:
bluss
2021-03-23 18:46:18 +01:00
parent 182097035d
commit a554ea219a
2 changed files with 97 additions and 48 deletions
+70 -40
View File
@@ -704,7 +704,7 @@ impl<T, const CAP: usize> std::convert::TryFrom<&[T]> for ArrayVec<T, CAP>
Err(CapacityError::new(()))
} else {
let mut array = Self::new();
array.extend(slice.iter().cloned());
array.extend_from_slice(slice);
Ok(array)
}
}
@@ -931,37 +931,73 @@ impl<T, Data, F> Drop for ScopeExitGuard<T, Data, F>
/// Extend the `ArrayVec` with an iterator.
///
/// Does not extract more items than there is space for. No error
/// occurs if there are more iterator elements.
/// ***Panics*** if extending the vector exceeds its capacity.
impl<T, const CAP: usize> Extend<T> for ArrayVec<T, CAP> {
/// Extend the `ArrayVec` with an iterator.
///
/// ***Panics*** if extending the vector exceeds its capacity.
fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
let take = self.capacity() - self.len();
unsafe {
let len = self.len();
let mut ptr = raw_ptr_add(self.as_mut_ptr(), len);
let end_ptr = raw_ptr_add(ptr, take);
// Keep the length in a separate variable, write it back on scope
// exit. To help the compiler with alias analysis and stuff.
// We update the length to handle panic in the iteration of the
// user's iterator, without dropping any elements on the floor.
let mut guard = ScopeExitGuard {
value: &mut self.len,
data: len,
f: move |&len, self_len| {
**self_len = len;
}
};
let mut iter = iter.into_iter();
loop {
if ptr == end_ptr { break; }
if let Some(elt) = iter.next() {
raw_ptr_write(ptr, elt);
ptr = raw_ptr_add(ptr, 1);
guard.data += 1;
} else {
break;
}
self.extend_from_iter::<_, true>(iter)
}
}
}
#[inline(never)]
#[cold]
fn extend_panic() {
panic!("ArrayVec: capacity exceeded in extend/from_iter");
}
impl<T, const CAP: usize> ArrayVec<T, CAP> {
/// Extend the arrayvec from the iterable.
///
/// ## Safety
///
/// Unsafe because if CHECK is false, the length of the input is not checked.
/// The caller must ensure the length of the input fits in the capacity.
pub(crate) unsafe fn extend_from_iter<I, const CHECK: bool>(&mut self, iterable: I)
where I: IntoIterator<Item = T>
{
let take = self.capacity() - self.len();
let len = self.len();
let mut ptr = raw_ptr_add(self.as_mut_ptr(), len);
let end_ptr = raw_ptr_add(ptr, take);
// Keep the length in a separate variable, write it back on scope
// exit. To help the compiler with alias analysis and stuff.
// We update the length to handle panic in the iteration of the
// user's iterator, without dropping any elements on the floor.
let mut guard = ScopeExitGuard {
value: &mut self.len,
data: len,
f: move |&len, self_len| {
**self_len = len;
}
};
let mut iter = iterable.into_iter();
loop {
if let Some(elt) = iter.next() {
if ptr == end_ptr && CHECK { extend_panic(); }
debug_assert_ne!(ptr, end_ptr);
ptr.write(elt);
ptr = raw_ptr_add(ptr, 1);
guard.data += 1;
} else {
return; // success
}
}
}
/// Extend the ArrayVec with clones of elements from the slice;
/// the length of the slice must be <= the remaining capacity in the arrayvec.
pub(crate) fn extend_from_slice(&mut self, slice: &[T])
where T: Clone
{
let take = self.capacity() - self.len();
debug_assert!(slice.len() <= take);
unsafe {
let slice = if take < slice.len() { &slice[..take] } else { slice };
self.extend_from_iter::<_, false>(slice.iter().cloned());
}
}
}
@@ -976,19 +1012,13 @@ unsafe fn raw_ptr_add<T>(ptr: *mut T, offset: usize) -> *mut T {
}
}
unsafe fn raw_ptr_write<T>(ptr: *mut T, value: T) {
if mem::size_of::<T>() == 0 {
/* nothing */
} else {
ptr::write(ptr, value)
}
}
/// Create an `ArrayVec` from an iterator.
///
/// Does not extract more items than there is space for. No error
/// occurs if there are more iterator elements.
/// ***Panics*** if the number of elements in the iterator exceeds the arrayvec's capacity.
impl<T, const CAP: usize> iter::FromIterator<T> for ArrayVec<T, CAP> {
/// Create an `ArrayVec` from an iterator.
///
/// ***Panics*** if the number of elements in the iterator exceeds the arrayvec's capacity.
fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> Self {
let mut array = ArrayVec::new();
array.extend(iter);
@@ -1014,8 +1044,8 @@ impl<T, const CAP: usize> Clone for ArrayVec<T, CAP>
self.pop();
}
} else {
let rhs_elems = rhs[self.len()..].iter().cloned();
self.extend(rhs_elems);
let rhs_elems = &rhs[self.len()..];
self.extend_from_slice(rhs_elems);
}
}
}