Skip to content

Commit f9f92a4

Browse files
committed
Make insert_many panic-safe
1 parent c2833c5 commit f9f92a4

File tree

2 files changed

+51
-11
lines changed

2 files changed

+51
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "smallvec"
3-
version = "0.4.4"
3+
version = "0.4.5"
44
authors = ["Simon Sapin <[email protected]>"]
55
license = "MPL-2.0"
66
repository = "https://github.com/servo/rust-smallvec"

lib.rs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -607,23 +607,33 @@ impl<A: Array> SmallVec<A> {
607607
unsafe {
608608
let old_len = self.len;
609609
assert!(index <= old_len);
610-
let ptr = self.as_mut_ptr().offset(index as isize);
610+
let mut ptr = self.as_mut_ptr().offset(index as isize);
611+
612+
// Move the trailing elements.
611613
ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index);
612-
for (off, element) in iter.enumerate() {
613-
if off < lower_size_bound {
614-
ptr::write(ptr.offset(off as isize), element);
615-
self.len = self.len + 1;
616-
} else {
617-
// Iterator provided more elements than the hint.
618-
assert!(index + off >= index); // Protect against overflow.
619-
self.insert(index + off, element);
614+
615+
// In case the iterator panics, don't double-drop the items we just copied above.
616+
self.set_len(index);
617+
618+
let mut num_added = 0;
619+
for element in iter {
620+
let mut cur = ptr.offset(num_added as isize);
621+
if num_added >= lower_size_bound {
622+
// Iterator provided more elements than the hint. Move trailing items again.
623+
self.reserve(1);
624+
ptr = self.as_mut_ptr().offset(index as isize);
625+
cur = ptr.offset(num_added as isize);
626+
ptr::copy(cur, cur.offset(1), old_len - index);
620627
}
628+
ptr::write(cur, element);
629+
num_added += 1;
621630
}
622-
let num_added = self.len - old_len;
623631
if num_added < lower_size_bound {
624632
// Iterator provided fewer elements than the hint
625633
ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index);
626634
}
635+
636+
self.set_len(old_len + num_added);
627637
}
628638
}
629639

@@ -1388,6 +1398,36 @@ pub mod tests {
13881398
assert_eq!(&v.iter().map(|v| *v).collect::<Vec<_>>(), &[0, 5, 6, 1, 2, 3]);
13891399
}
13901400

1401+
#[test]
1402+
// https://github.com/servo/rust-smallvec/issues/96
1403+
fn test_insert_many_panic() {
1404+
struct PanicOnDoubleDrop {
1405+
dropped: Box<bool>
1406+
}
1407+
1408+
impl Drop for PanicOnDoubleDrop {
1409+
fn drop(&mut self) {
1410+
assert!(!*self.dropped, "already dropped");
1411+
*self.dropped = true;
1412+
}
1413+
}
1414+
1415+
struct BadIter;
1416+
impl Iterator for BadIter {
1417+
type Item = PanicOnDoubleDrop;
1418+
fn size_hint(&self) -> (usize, Option<usize>) { (1, None) }
1419+
fn next(&mut self) -> Option<Self::Item> { panic!() }
1420+
}
1421+
1422+
let mut vec: SmallVec<[PanicOnDoubleDrop; 1]> = SmallVec::new();
1423+
vec.push(PanicOnDoubleDrop { dropped: Box::new(false) });
1424+
vec.push(PanicOnDoubleDrop { dropped: Box::new(false) });
1425+
let result = ::std::panic::catch_unwind(move || {
1426+
vec.insert_many(0, BadIter);
1427+
});
1428+
assert!(result.is_err());
1429+
}
1430+
13911431
#[test]
13921432
#[should_panic]
13931433
fn test_invalid_grow() {

0 commit comments

Comments
 (0)