Skip to content

Commit 26b2490

Browse files
committed
Make insert_many panic-safe
Fixes #96.
1 parent bfdd7ee commit 26b2490

File tree

1 file changed

+51
-11
lines changed

1 file changed

+51
-11
lines changed

lib.rs

+51-11
Original file line numberDiff line numberDiff line change
@@ -771,24 +771,33 @@ impl<A: Array> SmallVec<A> {
771771
unsafe {
772772
let old_len = self.len();
773773
assert!(index <= old_len);
774-
let ptr = self.as_mut_ptr().offset(index as isize);
774+
let mut ptr = self.as_mut_ptr().offset(index as isize);
775+
776+
// Move the trailing elements.
775777
ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index);
776-
for (off, element) in iter.enumerate() {
777-
if off < lower_size_bound {
778-
ptr::write(ptr.offset(off as isize), element);
779-
let len = self.len() + 1;
780-
self.set_len(len);
781-
} else {
782-
// Iterator provided more elements than the hint.
783-
assert!(index + off >= index); // Protect against overflow.
784-
self.insert(index + off, element);
778+
779+
// In case the iterator panics, don't double-drop the items we just copied above.
780+
self.set_len(index);
781+
782+
let mut num_added = 0;
783+
for element in iter {
784+
let mut cur = ptr.offset(num_added as isize);
785+
if num_added >= lower_size_bound {
786+
// Iterator provided more elements than the hint. Move trailing items again.
787+
self.reserve(1);
788+
ptr = self.as_mut_ptr().offset(index as isize);
789+
cur = ptr.offset(num_added as isize);
790+
ptr::copy(cur, cur.offset(1), old_len - index);
785791
}
792+
ptr::write(cur, element);
793+
num_added += 1;
786794
}
787-
let num_added = self.len() - old_len;
788795
if num_added < lower_size_bound {
789796
// Iterator provided fewer elements than the hint
790797
ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index);
791798
}
799+
800+
self.set_len(old_len + num_added);
792801
}
793802
}
794803

@@ -1645,6 +1654,37 @@ mod tests {
16451654
assert_eq!(&v.iter().map(|v| *v).collect::<Vec<_>>(), &[0, 5, 6, 1, 2, 3]);
16461655
}
16471656

1657+
#[test]
1658+
// https://github.com/servo/rust-smallvec/issues/96
1659+
fn test_insert_many_panic() {
1660+
struct PanicOnDoubleDrop {
1661+
dropped: Box<bool>
1662+
}
1663+
1664+
impl Drop for PanicOnDoubleDrop {
1665+
fn drop(&mut self) {
1666+
assert!(!*self.dropped, "already dropped");
1667+
*self.dropped = true;
1668+
}
1669+
}
1670+
1671+
struct BadIter;
1672+
impl Iterator for BadIter {
1673+
type Item = PanicOnDoubleDrop;
1674+
fn size_hint(&self) -> (usize, Option<usize>) { (1, None) }
1675+
fn next(&mut self) -> Option<Self::Item> { panic!() }
1676+
}
1677+
1678+
let mut vec: SmallVec<[PanicOnDoubleDrop; 0]> = vec![
1679+
PanicOnDoubleDrop { dropped: Box::new(false) },
1680+
PanicOnDoubleDrop { dropped: Box::new(false) },
1681+
].into();
1682+
let result = ::std::panic::catch_unwind(move || {
1683+
vec.insert_many(0, BadIter);
1684+
});
1685+
assert!(result.is_err());
1686+
}
1687+
16481688
#[test]
16491689
#[should_panic]
16501690
fn test_invalid_grow() {

0 commit comments

Comments
 (0)