From e64afc8c473d43e375ab42bd33db2d0d4ac4e41b Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sun, 8 Sep 2019 01:24:45 -0700 Subject: [PATCH] Use MaybeUninit for storage of inline items. This is a backport of #162 to the smallvec 0.6 branch. To avoid bumping the minimum Rust version, the `maybe-uninit` crate is used in place of `std::mem::MaybeUninit`. To avoid breaking changes, the `Array::ptr` and `ptr_mut` methods are retained but are no longer used, and the API to `from_buf_and_len_unchecked` is unchanged. --- Cargo.toml | 3 +- lib.rs | 91 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8622f80..299a646 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smallvec" -version = "0.6.12" +version = "0.6.13" authors = ["Simon Sapin "] license = "MIT/Apache-2.0" repository = "https://github.com/servo/rust-smallvec" @@ -23,6 +23,7 @@ path = "lib.rs" [dependencies] serde = { version = "1", optional = true } +maybe-uninit = "2.0" [dev_dependencies] bincode = "1.0.1" diff --git a/lib.rs b/lib.rs index cedf2bd..ffb8766 100644 --- a/lib.rs +++ b/lib.rs @@ -45,18 +45,21 @@ use alloc::vec::Vec; #[cfg(feature = "serde")] extern crate serde; +extern crate maybe_uninit; + #[cfg(not(feature = "std"))] mod std { pub use core::*; } +use maybe_uninit::MaybeUninit; + use std::borrow::{Borrow, BorrowMut}; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator, repeat}; use std::mem; -use std::mem::ManuallyDrop; use std::ops; use std::ptr; use std::slice; @@ -275,26 +278,28 @@ impl<'a, T: 'a> Drop for Drain<'a,T> { #[cfg(feature = "union")] union SmallVecData { - inline: ManuallyDrop, + inline: MaybeUninit, heap: (*mut A::Item, usize), } #[cfg(feature = "union")] impl SmallVecData { #[inline] - unsafe fn inline(&self) -> &A { - &self.inline + unsafe fn inline(&self) -> *const A::Item { + self.inline.as_ptr() as *const A::Item } #[inline] - unsafe fn inline_mut(&mut self) -> &mut A { - &mut self.inline + unsafe fn inline_mut(&mut self) -> *mut A::Item { + self.inline.as_mut_ptr() as *mut A::Item } #[inline] - fn from_inline(inline: A) -> SmallVecData { - SmallVecData { inline: ManuallyDrop::new(inline) } + fn from_inline(inline: MaybeUninit) -> SmallVecData { + SmallVecData { inline } } #[inline] - unsafe fn into_inline(self) -> A { ManuallyDrop::into_inner(self.inline) } + unsafe fn into_inline(self) -> MaybeUninit { + self.inline + } #[inline] unsafe fn heap(&self) -> (*mut A::Item, usize) { self.heap @@ -311,34 +316,34 @@ impl SmallVecData { #[cfg(not(feature = "union"))] enum SmallVecData { - Inline(ManuallyDrop), + Inline(MaybeUninit), Heap((*mut A::Item, usize)), } #[cfg(not(feature = "union"))] impl SmallVecData { #[inline] - unsafe fn inline(&self) -> &A { + unsafe fn inline(&self) -> *const A::Item { match *self { - SmallVecData::Inline(ref a) => a, + SmallVecData::Inline(ref a) => a.as_ptr() as *const A::Item, _ => debug_unreachable!(), } } #[inline] - unsafe fn inline_mut(&mut self) -> &mut A { + unsafe fn inline_mut(&mut self) -> *mut A::Item { match *self { - SmallVecData::Inline(ref mut a) => a, + SmallVecData::Inline(ref mut a) => a.as_mut_ptr() as *mut A::Item, _ => debug_unreachable!(), } } #[inline] - fn from_inline(inline: A) -> SmallVecData { - SmallVecData::Inline(ManuallyDrop::new(inline)) + fn from_inline(inline: MaybeUninit) -> SmallVecData { + SmallVecData::Inline(inline) } #[inline] - unsafe fn into_inline(self) -> A { + unsafe fn into_inline(self) -> MaybeUninit { match self { - SmallVecData::Inline(a) => ManuallyDrop::into_inner(a), + SmallVecData::Inline(a) => a, _ => debug_unreachable!(), } } @@ -403,11 +408,15 @@ impl SmallVec { /// Construct an empty vector #[inline] pub fn new() -> SmallVec { - unsafe { - SmallVec { - capacity: 0, - data: SmallVecData::from_inline(mem::uninitialized()), - } + // Try to detect invalid custom implementations of `Array`. Hopefuly, + // this check should be optimized away entirely for valid ones. + assert!( + mem::size_of::() == A::size() * mem::size_of::() + && mem::align_of::() >= mem::align_of::() + ); + SmallVec { + capacity: 0, + data: SmallVecData::from_inline(MaybeUninit::uninit()), } } @@ -447,10 +456,10 @@ impl SmallVec { pub fn from_vec(mut vec: Vec) -> SmallVec { if vec.capacity() <= A::size() { unsafe { - let mut data = SmallVecData::::from_inline(mem::uninitialized()); + let mut data = SmallVecData::::from_inline(MaybeUninit::uninit()); let len = vec.len(); vec.set_len(0); - ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut().ptr_mut(), len); + ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut(), len); SmallVec { capacity: len, @@ -483,7 +492,7 @@ impl SmallVec { pub fn from_buf(buf: A) -> SmallVec { SmallVec { capacity: A::size(), - data: SmallVecData::from_inline(buf), + data: SmallVecData::from_inline(MaybeUninit::new(buf)), } } @@ -523,7 +532,7 @@ impl SmallVec { pub unsafe fn from_buf_and_len_unchecked(buf: A, len: usize) -> SmallVec { SmallVec { capacity: len, - data: SmallVecData::from_inline(buf), + data: SmallVecData::from_inline(MaybeUninit::new(buf)), } } @@ -571,7 +580,7 @@ impl SmallVec { let (ptr, len) = self.data.heap(); (ptr, len, self.capacity) } else { - (self.data.inline().ptr(), self.capacity, A::size()) + (self.data.inline(), self.capacity, A::size()) } } } @@ -584,7 +593,7 @@ impl SmallVec { let &mut (ptr, ref mut len_ptr) = self.data.heap_mut(); (ptr, len_ptr, self.capacity) } else { - (self.data.inline_mut().ptr_mut(), &mut self.capacity, A::size()) + (self.data.inline_mut(), &mut self.capacity, A::size()) } } } @@ -651,8 +660,8 @@ impl SmallVec { if unspilled { return; } - self.data = SmallVecData::from_inline(mem::uninitialized()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + self.data = SmallVecData::from_inline(MaybeUninit::uninit()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); self.capacity = len; } else if new_cap != cap { let mut vec = Vec::with_capacity(new_cap); @@ -717,8 +726,8 @@ impl SmallVec { if self.inline_size() >= len { unsafe { let (ptr, len) = self.data.heap(); - self.data = SmallVecData::from_inline(mem::uninitialized()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + self.data = SmallVecData::from_inline(MaybeUninit::uninit()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); deallocate(ptr, self.capacity); self.capacity = len; } @@ -883,7 +892,7 @@ impl SmallVec { unsafe { let data = ptr::read(&self.data); mem::forget(self); - Ok(data.into_inline()) + Ok(data.into_inline().assume_init()) } } } @@ -1041,8 +1050,12 @@ impl SmallVec where A::Item: Copy { SmallVec { capacity: len, data: SmallVecData::from_inline(unsafe { - let mut data: A = mem::uninitialized(); - ptr::copy_nonoverlapping(slice.as_ptr(), data.ptr_mut(), len); + let mut data: MaybeUninit = MaybeUninit::uninit(); + ptr::copy_nonoverlapping( + slice.as_ptr(), + data.as_mut_ptr() as *mut A::Item, + len, + ); data }) } @@ -1587,8 +1600,8 @@ macro_rules! impl_array( unsafe impl Array for [T; $size] { type Item = T; fn size() -> usize { $size } - fn ptr(&self) -> *const T { self.as_ptr() } - fn ptr_mut(&mut self) -> *mut T { self.as_mut_ptr() } + fn ptr(&self) -> *const T { unimplemented!() } + fn ptr_mut(&mut self) -> *mut T { unimplemented!() } } )+ } @@ -1889,7 +1902,7 @@ mod tests { assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); } - #[cfg(feature = "std")] + #[cfg(all(feature = "std", not(miri)))] // Miri currently does not support unwinding #[test] // https://github.com/servo/rust-smallvec/issues/96 fn test_insert_many_panic() {