Merge pull request #6 from bluss/nodrop-restored

Use NoDrop to fix panic safety issues
This commit is contained in:
bluss
2015-08-21 14:56:17 +02:00
5 changed files with 58 additions and 49 deletions
+2
View File
@@ -3,6 +3,7 @@ sudo: false
matrix: matrix:
include: include:
- rust: stable - rust: stable
- rust: beta
- rust: nightly - rust: nightly
- rust: nightly - rust: nightly
env: env:
@@ -11,6 +12,7 @@ script:
- | - |
[ -z "$NODROP_FEATURES" ] && cargo build --verbose --features "$FEATURES" [ -z "$NODROP_FEATURES" ] && cargo build --verbose --features "$FEATURES"
[ -z "$NODROP_FEATURES" ] && cargo test --verbose --features "$FEATURES" [ -z "$NODROP_FEATURES" ] && cargo test --verbose --features "$FEATURES"
[ -z "$NODROP_FEATURES" ] && cargo test --release --verbose --features "$FEATURES"
[ -z "$NODROP_FEATURES" ] && cargo bench --verbose --features "$FEATURES" -- --test [ -z "$NODROP_FEATURES" ] && cargo bench --verbose --features "$FEATURES" -- --test
[ -z "$NODROP_FEATURES" ] && cargo doc --verbose --features "$FEATURES" [ -z "$NODROP_FEATURES" ] && cargo doc --verbose --features "$FEATURES"
cargo test --verbose --manifest-path=nodrop/Cargo.toml --features "$NODROP_FEATURES" cargo test --verbose --manifest-path=nodrop/Cargo.toml --features "$NODROP_FEATURES"
+3 -3
View File
@@ -13,6 +13,6 @@ keywords = ["stack", "vector", "array", "container", "data-structure"]
[dependencies.odds] [dependencies.odds]
version = "0.2" version = "0.2"
[dev-dependencies] [dependencies.nodrop]
nodrop = "0.1" version = "0.1.4"
path = "nodrop"
-1
View File
@@ -22,7 +22,6 @@ subst: $(DOCCRATES)
mkdocs: Cargo.toml mkdocs: Cargo.toml
cargo doc --features=$(FEATURES) cargo doc --features=$(FEATURES)
cargo doc -p nodrop
rm -rf ./doc rm -rf ./doc
cp -r ./target/doc ./doc cp -r ./target/doc ./doc
-cat ./custom.css >> doc/main.css -cat ./custom.css >> doc/main.css
+20 -43
View File
@@ -1,4 +1,5 @@
extern crate odds; extern crate odds;
extern crate nodrop;
use std::iter; use std::iter;
use std::mem; use std::mem;
@@ -9,13 +10,13 @@ use std::ops::{
}; };
use std::slice; use std::slice;
use nodrop::NoDrop;
// extra traits // extra traits
use std::borrow::{Borrow, BorrowMut}; use std::borrow::{Borrow, BorrowMut};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::fmt; use std::fmt;
use odds::debug_assert_unreachable;
mod array; mod array;
pub use array::Array; pub use array::Array;
pub use odds::IndexRange as RangeArgument; pub use odds::IndexRange as RangeArgument;
@@ -30,13 +31,6 @@ unsafe fn new_array<A: Array>() -> A {
mem::uninitialized() mem::uninitialized()
} }
/// repr(u8) - Make sure the non-nullable pointer optimization does not occur!
#[repr(u8)]
enum NoDrop<T> {
Alive(T),
Dropped,
}
/// A vector with a fixed capacity. /// A vector with a fixed capacity.
/// ///
/// The **ArrayVec** is a vector backed by a fixed size array. It keeps track of /// The **ArrayVec** is a vector backed by a fixed size array. It keeps track of
@@ -57,12 +51,12 @@ pub struct ArrayVec<A: Array> {
impl<A: Array> Drop for ArrayVec<A> { impl<A: Array> Drop for ArrayVec<A> {
fn drop(&mut self) { fn drop(&mut self) {
// clear all elements // clear all elements
while let Some(_) = self.pop() { } while let Some(_) = self.pop() {
// inhibit drop
unsafe {
ptr::write(&mut self.xs, NoDrop::Dropped);
} }
// NoDrop inhibits array's drop
// panic safety: NoDrop::drop will trigger on panic, so the inner
// array will not drop even after panic.
} }
} }
@@ -84,7 +78,7 @@ impl<A: Array> ArrayVec<A> {
/// ``` /// ```
pub fn new() -> ArrayVec<A> { pub fn new() -> ArrayVec<A> {
unsafe { unsafe {
ArrayVec { xs: NoDrop::Alive(new_array()), len: Index::zero() } ArrayVec { xs: NoDrop::new(new_array()), len: Index::zero() }
} }
} }
@@ -375,31 +369,6 @@ impl<A: Array> DerefMut for ArrayVec<A> {
} }
} }
impl<T> Deref for NoDrop<T> {
type Target = T;
// Use type invariant, always Alive.
#[inline]
fn deref(&self) -> &T {
match *self {
NoDrop::Alive(ref inner) => inner,
_ => unsafe { debug_assert_unreachable() }
}
}
}
impl<T> DerefMut for NoDrop<T> {
// Use type invariant, always Alive.
#[inline]
fn deref_mut(&mut self) -> &mut T {
match *self {
NoDrop::Alive(ref mut inner) => inner,
_ => unsafe { debug_assert_unreachable() }
}
}
}
/// Create an **ArrayVec** from an array. /// Create an **ArrayVec** from an array.
/// ///
/// ## Examples /// ## Examples
@@ -412,7 +381,7 @@ impl<T> DerefMut for NoDrop<T> {
/// ``` /// ```
impl<A: Array> From<A> for ArrayVec<A> { impl<A: Array> From<A> for ArrayVec<A> {
fn from(array: A) -> Self { fn from(array: A) -> Self {
ArrayVec { xs: NoDrop::Alive(array), len: Index::from(A::capacity()) } ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) }
} }
} }
@@ -524,10 +493,16 @@ impl<A: Array> ExactSizeIterator for IntoIter<A> { }
impl<A: Array> Drop for IntoIter<A> { impl<A: Array> Drop for IntoIter<A> {
fn drop(&mut self) { fn drop(&mut self) {
// exhaust iterator and clear the vector // panic safety: Set length to 0 before dropping elements.
while let Some(_) = self.next() { } let index = self.index.to_usize();
let len = self.v.len();
unsafe { unsafe {
self.v.set_len(0); self.v.set_len(0);
let elements = slice::from_raw_parts(self.v.get_unchecked_mut(index),
len - index);
for elt in elements {
ptr::read(elt);
}
} }
} }
} }
@@ -588,6 +563,8 @@ impl<'a, A: Array> Drop for Drain<'a, A>
where A::Item: 'a where A::Item: 'a
{ {
fn drop(&mut self) { fn drop(&mut self) {
// len is currently 0 so panicking while dropping will not cause a double drop.
// exhaust self first // exhaust self first
while let Some(_) = self.next() { } while let Some(_) = self.next() { }
+33 -2
View File
@@ -130,12 +130,12 @@ fn test_compact_size() {
// 4 elements size + 1 len + 1 enum tag + [1 drop flag] // 4 elements size + 1 len + 1 enum tag + [1 drop flag]
type ByteArray = ArrayVec<[u8; 4]>; type ByteArray = ArrayVec<[u8; 4]>;
println!("{}", mem::size_of::<ByteArray>()); println!("{}", mem::size_of::<ByteArray>());
assert!(mem::size_of::<ByteArray>() <= 7); assert!(mem::size_of::<ByteArray>() <= 8);
// 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding // 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding
type QuadArray = ArrayVec<[u32; 3]>; type QuadArray = ArrayVec<[u32; 3]>;
println!("{}", mem::size_of::<QuadArray>()); println!("{}", mem::size_of::<QuadArray>());
assert!(mem::size_of::<QuadArray>() <= 20); assert!(mem::size_of::<QuadArray>() <= 24);
} }
#[test] #[test]
@@ -163,6 +163,37 @@ fn test_drain_oob() {
v.drain(0..8); v.drain(0..8);
} }
#[test]
#[should_panic]
fn test_drop_panic() {
struct DropPanic;
impl Drop for DropPanic {
fn drop(&mut self) {
panic!("drop");
}
}
let mut array = ArrayVec::<[DropPanic; 1]>::new();
array.push(DropPanic);
}
#[test]
#[should_panic]
fn test_drop_panic_into_iter() {
struct DropPanic;
impl Drop for DropPanic {
fn drop(&mut self) {
panic!("drop");
}
}
let mut array = ArrayVec::<[DropPanic; 1]>::new();
array.push(DropPanic);
array.into_iter();
}
#[test] #[test]
fn test_insert() { fn test_insert() {
let mut v = ArrayVec::from([]); let mut v = ArrayVec::from([]);