diff --git a/.travis.yml b/.travis.yml index ee744e1..a5e7388 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,5 @@ script: | ([ $TRAVIS_RUST_VERSION != nightly ] || cargo check --verbose --no-default-features) && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --features union) && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --all-features) && - ([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench) + ([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench) && + ([ $TRAVIS_RUST_VERSION != nightly ] || bash ./scripts/run_miri.sh) diff --git a/Cargo.toml b/Cargo.toml index fcea83c..de8561c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smallvec" -version = "0.6.10" +version = "0.6.14" 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 e45ca7a..a227cb8 100644 --- a/lib.rs +++ b/lib.rs @@ -29,7 +29,6 @@ //! Note that this feature requires a nightly compiler (for now). #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] #![cfg_attr(feature = "union", feature(untagged_unions))] #![cfg_attr(feature = "specialization", feature(specialization))] #![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))] @@ -46,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; @@ -276,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 @@ -312,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!(), } } @@ -404,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()), } } @@ -448,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, @@ -484,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)), } } @@ -524,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)), } } @@ -572,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()) } } } @@ -585,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()) } } } @@ -652,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); @@ -718,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; } @@ -815,7 +823,7 @@ impl SmallVec { /// Insert multiple elements at position `index`, shifting all following elements toward the /// back. pub fn insert_many>(&mut self, index: usize, iterable: I) { - let iter = iterable.into_iter(); + let mut iter = iterable.into_iter(); if index == self.len() { return self.extend(iter); } @@ -824,11 +832,12 @@ impl SmallVec { assert!(lower_size_bound <= std::isize::MAX as usize); // Ensure offset is indexable assert!(index + lower_size_bound >= index); // Protect against overflow self.reserve(lower_size_bound); + let mut num_added = 0; unsafe { let old_len = self.len(); assert!(index <= old_len); - let mut ptr = self.as_mut_ptr().offset(index as isize); + let ptr = self.as_mut_ptr().offset(index as isize); // Move the trailing elements. ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index); @@ -836,16 +845,12 @@ impl SmallVec { // In case the iterator panics, don't double-drop the items we just copied above. self.set_len(index); - let mut num_added = 0; - for element in iter { - let mut cur = ptr.offset(num_added as isize); - if num_added >= lower_size_bound { - // Iterator provided more elements than the hint. Move trailing items again. - self.reserve(1); - ptr = self.as_mut_ptr().offset(index as isize); - cur = ptr.offset(num_added as isize); - ptr::copy(cur, cur.offset(1), old_len - index); - } + while num_added < lower_size_bound { + let element = match iter.next() { + Some(x) => x, + None => break, + }; + let cur = ptr.offset(num_added as isize); ptr::write(cur, element); num_added += 1; } @@ -853,9 +858,14 @@ impl SmallVec { // Iterator provided fewer elements than the hint ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index); } - self.set_len(old_len + num_added); } + + // If the iterator has more than `lower_size_bound` elements, insert the rest one-by-one. + for element in iter { + self.insert(index + num_added, element); + num_added += 1; + } } /// Convert a SmallVec to a Vec, without reallocating if the SmallVec has already spilled onto @@ -884,7 +894,7 @@ impl SmallVec { unsafe { let data = ptr::read(&self.data); mem::forget(self); - Ok(data.into_inline()) + Ok(data.into_inline().assume_init()) } } } @@ -1042,8 +1052,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 }) } @@ -1254,12 +1268,7 @@ trait SpecFrom { } #[cfg(feature = "specialization")] -impl<'a, A: Array> SpecFrom for SmallVec where A::Item: Clone { - #[inline] - default fn spec_from(slice: &'a [A::Item]) -> SmallVec { - slice.into_iter().cloned().collect() - } -} +mod specialization; #[cfg(feature = "specialization")] impl<'a, A: Array> SpecFrom for SmallVec where A::Item: Copy { @@ -1593,8 +1602,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!() } } )+ } @@ -1895,7 +1904,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() { @@ -2344,6 +2353,13 @@ mod tests { assert_eq!(v[..], ['a']); } + // #139 + #[test] + fn uninhabited() { + enum Void {} + let _sv = SmallVec::<[Void; 8]>::new(); + } + #[test] fn grow_spilled_same_size() { let mut v: SmallVec<[u8; 2]> = SmallVec::new(); @@ -2357,4 +2373,17 @@ mod tests { assert_eq!(v.capacity(), 4); assert_eq!(v[..], [0, 1, 2]); } + + #[test] + fn test_insert_many_overflow() { + let mut v: SmallVec<[u8; 1]> = SmallVec::new(); + v.push(123); + + // Prepare an iterator with small lower bound + let iter = (0u8..5).filter(|n| n % 2 == 0); + assert_eq!(iter.size_hint().0, 0); + + v.insert_many(0, iter); + assert_eq!(&*v, &[0, 2, 4, 123]); + } } diff --git a/scripts/run_miri.sh b/scripts/run_miri.sh new file mode 100644 index 0000000..42f2884 --- /dev/null +++ b/scripts/run_miri.sh @@ -0,0 +1,21 @@ +#!/usr/bin/bash + +set -ex + +# Clean out our target dir, which may have artifacts compiled by a version of +# rust different from the one we're about to download. +cargo clean + +# Install and run the latest version of nightly where miri built successfully. +# Taken from: https://github.com/rust-lang/miri#running-miri-on-ci + +MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) +echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" +rustup default "$MIRI_NIGHTLY" + +rustup component add miri +cargo miri setup + +cargo miri test --verbose -- -- -Zunstable-options --exclude-should-panic +cargo miri test --verbose --features union -- -- -Zunstable-options --exclude-should-panic +cargo miri test --verbose --all-features -- -- -Zunstable-options --exclude-should-panic diff --git a/specialization.rs b/specialization.rs new file mode 100644 index 0000000..2e7bb1e --- /dev/null +++ b/specialization.rs @@ -0,0 +1,16 @@ +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementations that require `default fn`. + +use super::{SpecFrom, SmallVec, Array}; + +impl<'a, A: Array> SpecFrom for SmallVec where A::Item: Clone { + #[inline] + default fn spec_from(slice: &'a [A::Item]) -> SmallVec { + slice.into_iter().cloned().collect() + } +}