Skip to content

Commit 109e5a7

Browse files
committed
Make insert_many panic-safe
1 parent 28b9110 commit 109e5a7

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.5.0"
3+
version = "0.5.1"
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
@@ -600,23 +600,33 @@ impl<A: Array> SmallVec<A> {
600600
unsafe {
601601
let old_len = self.len;
602602
assert!(index <= old_len);
603-
let ptr = self.as_mut_ptr().offset(index as isize);
603+
let mut ptr = self.as_mut_ptr().offset(index as isize);
604+
605+
// Move the trailing elements.
604606
ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index);
605-
for (off, element) in iter.enumerate() {
606-
if off < lower_size_bound {
607-
ptr::write(ptr.offset(off as isize), element);
608-
self.len = self.len + 1;
609-
} else {
610-
// Iterator provided more elements than the hint.
611-
assert!(index + off >= index); // Protect against overflow.
612-
self.insert(index + off, element);
607+
608+
// In case the iterator panics, don't double-drop the items we just copied above.
609+
self.set_len(index);
610+
611+
let mut num_added = 0;
612+
for element in iter {
613+
let mut cur = ptr.offset(num_added as isize);
614+
if num_added >= lower_size_bound {
615+
// Iterator provided more elements than the hint. Move trailing items again.
616+
self.reserve(1);
617+
ptr = self.as_mut_ptr().offset(index as isize);
618+
cur = ptr.offset(num_added as isize);
619+
ptr::copy(cur, cur.offset(1), old_len - index);
613620
}
621+
ptr::write(cur, element);
622+
num_added += 1;
614623
}
615-
let num_added = self.len - old_len;
616624
if num_added < lower_size_bound {
617625
// Iterator provided fewer elements than the hint
618626
ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index);
619627
}
628+
629+
self.set_len(old_len + num_added);
620630
}
621631
}
622632

@@ -1376,6 +1386,36 @@ pub mod tests {
13761386
assert_eq!(&v.iter().map(|v| *v).collect::<Vec<_>>(), &[0, 5, 6, 1, 2, 3]);
13771387
}
13781388

1389+
#[test]
1390+
// https://github.com/servo/rust-smallvec/issues/96
1391+
fn test_insert_many_panic() {
1392+
struct PanicOnDoubleDrop {
1393+
dropped: Box<bool>
1394+
}
1395+
1396+
impl Drop for PanicOnDoubleDrop {
1397+
fn drop(&mut self) {
1398+
assert!(!*self.dropped, "already dropped");
1399+
*self.dropped = true;
1400+
}
1401+
}
1402+
1403+
struct BadIter;
1404+
impl Iterator for BadIter {
1405+
type Item = PanicOnDoubleDrop;
1406+
fn size_hint(&self) -> (usize, Option<usize>) { (1, None) }
1407+
fn next(&mut self) -> Option<Self::Item> { panic!() }
1408+
}
1409+
1410+
let mut vec: SmallVec<[PanicOnDoubleDrop; 1]> = SmallVec::new();
1411+
vec.push(PanicOnDoubleDrop { dropped: Box::new(false) });
1412+
vec.push(PanicOnDoubleDrop { dropped: Box::new(false) });
1413+
let result = ::std::panic::catch_unwind(move || {
1414+
vec.insert_many(0, BadIter);
1415+
});
1416+
assert!(result.is_err());
1417+
}
1418+
13791419
#[test]
13801420
#[should_panic]
13811421
fn test_invalid_grow() {

0 commit comments

Comments
 (0)