Skip to content

Commit 06a8984

Browse files
committed
Make insert_many panic-safe
1 parent 46ef708 commit 06a8984

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.3.3"
3+
version = "0.3.4"
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
@@ -488,23 +488,33 @@ impl<A: Array> SmallVec<A> {
488488
unsafe {
489489
let old_len = self.len;
490490
assert!(index <= old_len);
491-
let ptr = self.as_mut_ptr().offset(index as isize);
491+
let mut ptr = self.as_mut_ptr().offset(index as isize);
492+
493+
// Move the trailing elements.
492494
ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index);
493-
for (off, element) in iter.enumerate() {
494-
if off < lower_size_bound {
495-
ptr::write(ptr.offset(off as isize), element);
496-
self.len = self.len + 1;
497-
} else {
498-
// Iterator provided more elements than the hint.
499-
assert!(index + off >= index); // Protect against overflow.
500-
self.insert(index + off, element);
495+
496+
// In case the iterator panics, don't double-drop the items we just copied above.
497+
self.set_len(index);
498+
499+
let mut num_added = 0;
500+
for element in iter {
501+
let mut cur = ptr.offset(num_added as isize);
502+
if num_added >= lower_size_bound {
503+
// Iterator provided more elements than the hint. Move trailing items again.
504+
self.reserve(1);
505+
ptr = self.as_mut_ptr().offset(index as isize);
506+
cur = ptr.offset(num_added as isize);
507+
ptr::copy(cur, cur.offset(1), old_len - index);
501508
}
509+
ptr::write(cur, element);
510+
num_added += 1;
502511
}
503-
let num_added = self.len - old_len;
504512
if num_added < lower_size_bound {
505513
// Iterator provided fewer elements than the hint
506514
ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index);
507515
}
516+
517+
self.set_len(old_len + num_added);
508518
}
509519
}
510520

@@ -1153,6 +1163,36 @@ pub mod tests {
11531163
assert_eq!(&v.iter().map(|v| *v).collect::<Vec<_>>(), &[0, 5, 6, 1, 2, 3]);
11541164
}
11551165

1166+
#[test]
1167+
// https://github.com/servo/rust-smallvec/issues/96
1168+
fn test_insert_many_panic() {
1169+
struct PanicOnDoubleDrop {
1170+
dropped: Box<bool>
1171+
}
1172+
1173+
impl Drop for PanicOnDoubleDrop {
1174+
fn drop(&mut self) {
1175+
assert!(!*self.dropped, "already dropped");
1176+
*self.dropped = true;
1177+
}
1178+
}
1179+
1180+
struct BadIter;
1181+
impl Iterator for BadIter {
1182+
type Item = PanicOnDoubleDrop;
1183+
fn size_hint(&self) -> (usize, Option<usize>) { (1, None) }
1184+
fn next(&mut self) -> Option<Self::Item> { panic!() }
1185+
}
1186+
1187+
let mut vec: SmallVec<[PanicOnDoubleDrop; 1]> = SmallVec::new();
1188+
vec.push(PanicOnDoubleDrop { dropped: Box::new(false) });
1189+
vec.push(PanicOnDoubleDrop { dropped: Box::new(false) });
1190+
let result = ::std::panic::catch_unwind(move || {
1191+
vec.insert_many(0, BadIter);
1192+
});
1193+
assert!(result.is_err());
1194+
}
1195+
11561196
#[test]
11571197
#[should_panic]
11581198
fn test_invalid_grow() {

0 commit comments

Comments
 (0)