diff --git a/.travis.yml b/.travis.yml index 62fe69fe..fffc9a8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,40 +4,34 @@ env: - FEATURES='serde-1' matrix: include: - - rust: 1.20.0 - - rust: stable - env: - - NODEFAULT=1 - - NODROP_FEATURES='use_needs_drop' - rust: 1.22.1 env: - FEATURES='array-sizes-33-128 array-sizes-129-255' + - rust: stable + - rust: stable + env: + - FEATURES='serde-1' - rust: stable env: - FEATURES='array-sizes-33-128 array-sizes-129-255' - rust: beta - - rust: nightly env: - - NODEFAULT=1 + - FEATURES='serde-1' - rust: nightly env: - - NODROP_FEATURES='use_needs_drop' + - FEATURES='serde-1' - rust: nightly env: - - FEATURES='serde use_union' - - NODROP_FEATURES='use_union' + - FEATURES='array-sizes-33-128 array-sizes-129-255' branches: only: - master - 0.3 script: - | - ([ ! -z "$NODROP_FEATURES" ] || cargo build --verbose --features "$FEATURES") && - ([ "$NODEFAULT" != 1 ] || cargo build --verbose --no-default-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 doc --verbose --features "$FEATURES") && - ([ "$NODEFAULT" != 1 ] || cargo build --verbose --manifest-path=nodrop/Cargo.toml --no-default-features) && - cargo test --verbose --manifest-path=nodrop/Cargo.toml --features "$NODROP_FEATURES" && - cargo bench --verbose --manifest-path=nodrop/Cargo.toml --features "$NODROP_FEATURES" -- --test + cargo build --verbose --no-default-features && + cargo build --verbose --features "$FEATURES" && + cargo test --verbose --features "$FEATURES" && + cargo test --release --verbose --features "$FEATURES" && + cargo bench --verbose --features "$FEATURES" --no-run && + cargo doc --verbose --features "$FEATURES" diff --git a/Cargo.toml b/Cargo.toml index f7a19480..93e401d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,9 @@ repository = "https://github.com/bluss/arrayvec" keywords = ["stack", "vector", "array", "data-structure", "no_std"] categories = ["data-structures", "no-std"] +[build-dependencies] + [dependencies] -nodrop = { version = "0.1.12", path = "nodrop", default-features = false } [dependencies.serde] version = "1.0" @@ -37,12 +38,14 @@ harness = false [features] default = ["std"] std = [] -use_union = [] serde-1 = ["serde"] array-sizes-33-128 = [] array-sizes-129-255 = [] +# has no effect +use_union = [] + [package.metadata.docs.rs] features = ["serde-1"] diff --git a/src/array.rs b/src/array.rs index 8d043e4c..ad18c795 100644 --- a/src/array.rs +++ b/src/array.rs @@ -15,14 +15,14 @@ pub unsafe trait Array { /// The array’s element type type Item; + /// The smallest type that can index and tell the length of the array. #[doc(hidden)] - /// The smallest index type that indexes the array. type Index: Index; + /// The array's element capacity + const CAPACITY: usize; #[doc(hidden)] fn as_ptr(&self) -> *const Self::Item; #[doc(hidden)] - fn as_mut_ptr(&mut self) -> *mut Self::Item; - #[doc(hidden)] fn capacity() -> usize; } @@ -91,14 +91,12 @@ macro_rules! fix_array_impl { unsafe impl Array for [T; $len] { type Item = T; type Index = $index_type; + const CAPACITY: usize = $len; #[doc(hidden)] #[inline(always)] fn as_ptr(&self) -> *const T { self as *const _ as *const _ } #[doc(hidden)] #[inline(always)] - fn as_mut_ptr(&mut self) -> *mut T { self as *mut _ as *mut _} - #[doc(hidden)] - #[inline(always)] fn capacity() -> usize { $len } } ) diff --git a/src/array_string.rs b/src/array_string.rs index 190ae153..a52fb58c 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -16,6 +16,8 @@ use char::encode_utf8; #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use super::MaybeUninitCopy; + /// A string with a fixed capacity. /// /// The `ArrayString` is a string backed by a fixed size array. It keeps track @@ -24,19 +26,25 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer}; /// The string is a contiguous value that you can store directly on the stack /// if needed. #[derive(Copy)] -pub struct ArrayString> { - xs: A, +pub struct ArrayString + where A: Array + Copy +{ + xs: MaybeUninitCopy, len: A::Index, } -impl> Default for ArrayString { +impl Default for ArrayString + where A: Array + Copy +{ /// Return an empty `ArrayString` fn default() -> ArrayString { ArrayString::new() } } -impl> ArrayString { +impl ArrayString + where A: Array + Copy +{ /// Create a new empty `ArrayString`. /// /// Capacity is inferred from the type parameter. @@ -52,7 +60,7 @@ impl> ArrayString { pub fn new() -> ArrayString { unsafe { ArrayString { - xs: ::new_array(), + xs: MaybeUninitCopy::uninitialized(), len: Index::from(0), } } @@ -88,11 +96,12 @@ impl> ArrayString { /// let string = ArrayString::from_byte_string(b"hello world").unwrap(); /// ``` pub fn from_byte_string(b: &A) -> Result { - let mut arraystr = Self::new(); - let s = try!(str::from_utf8(b.as_slice())); - let _result = arraystr.try_push_str(s); - debug_assert!(_result.is_ok()); - Ok(arraystr) + let len = str::from_utf8(b.as_slice())?.len(); + debug_assert_eq!(len, A::capacity()); + Ok(ArrayString { + xs: MaybeUninitCopy::from(*b), + len: Index::from(A::capacity()), + }) } /// Return the capacity of the `ArrayString`. @@ -210,7 +219,7 @@ impl> ArrayString { return Err(CapacityError::new(s)); } unsafe { - let dst = self.xs.as_mut_ptr().offset(self.len() as isize); + let dst = self.xs.ptr_mut().offset(self.len() as isize); let src = s.as_ptr(); ptr::copy_nonoverlapping(src, dst, s.len()); let newl = self.len() + s.len(); @@ -304,8 +313,8 @@ impl> ArrayString { let next = idx + ch.len_utf8(); let len = self.len(); unsafe { - ptr::copy(self.xs.as_ptr().offset(next as isize), - self.xs.as_mut_ptr().offset(idx as isize), + ptr::copy(self.xs.ptr().offset(next as isize), + self.xs.ptr_mut().offset(idx as isize), len - next); self.set_len(len - (next - idx)); } @@ -339,75 +348,99 @@ impl> ArrayString { /// Return a mutable slice of the whole string’s buffer unsafe fn raw_mut_bytes(&mut self) -> &mut [u8] { - slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.capacity()) + slice::from_raw_parts_mut(self.xs.ptr_mut(), self.capacity()) } } -impl> Deref for ArrayString { +impl Deref for ArrayString + where A: Array + Copy +{ type Target = str; #[inline] fn deref(&self) -> &str { unsafe { - let sl = slice::from_raw_parts(self.xs.as_ptr(), self.len.to_usize()); + let sl = slice::from_raw_parts(self.xs.ptr(), self.len.to_usize()); str::from_utf8_unchecked(sl) } } } -impl> DerefMut for ArrayString { +impl DerefMut for ArrayString + where A: Array + Copy +{ #[inline] fn deref_mut(&mut self) -> &mut str { unsafe { - let sl = slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.len.to_usize()); + let sl = slice::from_raw_parts_mut(self.xs.ptr_mut(), self.len.to_usize()); str::from_utf8_unchecked_mut(sl) } } } -impl> PartialEq for ArrayString { +impl PartialEq for ArrayString + where A: Array + Copy +{ fn eq(&self, rhs: &Self) -> bool { **self == **rhs } } -impl> PartialEq for ArrayString { +impl PartialEq for ArrayString + where A: Array + Copy +{ fn eq(&self, rhs: &str) -> bool { &**self == rhs } } -impl> PartialEq> for str { +impl PartialEq> for str + where A: Array + Copy +{ fn eq(&self, rhs: &ArrayString) -> bool { self == &**rhs } } -impl> Eq for ArrayString { } +impl Eq for ArrayString + where A: Array + Copy +{ } -impl> Hash for ArrayString { +impl Hash for ArrayString + where A: Array + Copy +{ fn hash(&self, h: &mut H) { (**self).hash(h) } } -impl> Borrow for ArrayString { +impl Borrow for ArrayString + where A: Array + Copy +{ fn borrow(&self) -> &str { self } } -impl> AsRef for ArrayString { +impl AsRef for ArrayString + where A: Array + Copy +{ fn as_ref(&self) -> &str { self } } -impl> fmt::Debug for ArrayString { +impl fmt::Debug for ArrayString + where A: Array + Copy +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } } -impl> fmt::Display for ArrayString { +impl fmt::Display for ArrayString + where A: Array + Copy +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } } /// `Write` appends written data to the end of the string. -impl> fmt::Write for ArrayString { +impl fmt::Write for ArrayString + where A: Array + Copy +{ fn write_char(&mut self, c: char) -> fmt::Result { self.try_push(c).map_err(|_| fmt::Error) } @@ -417,7 +450,9 @@ impl> fmt::Write for ArrayString { } } -impl + Copy> Clone for ArrayString { +impl Clone for ArrayString + where A: Array + Copy +{ fn clone(&self) -> ArrayString { *self } @@ -428,7 +463,9 @@ impl + Copy> Clone for ArrayString { } } -impl> PartialOrd for ArrayString { +impl PartialOrd for ArrayString + where A: Array + Copy +{ fn partial_cmp(&self, rhs: &Self) -> Option { (**self).partial_cmp(&**rhs) } @@ -438,7 +475,9 @@ impl> PartialOrd for ArrayString { fn ge(&self, rhs: &Self) -> bool { **self >= **rhs } } -impl> PartialOrd for ArrayString { +impl PartialOrd for ArrayString + where A: Array + Copy +{ fn partial_cmp(&self, rhs: &str) -> Option { (**self).partial_cmp(rhs) } @@ -448,7 +487,9 @@ impl> PartialOrd for ArrayString { fn ge(&self, rhs: &str) -> bool { &**self >= rhs } } -impl> PartialOrd> for str { +impl PartialOrd> for str + where A: Array + Copy +{ fn partial_cmp(&self, rhs: &ArrayString) -> Option { self.partial_cmp(&**rhs) } @@ -458,7 +499,9 @@ impl> PartialOrd> for str { fn ge(&self, rhs: &ArrayString) -> bool { self >= &**rhs } } -impl> Ord for ArrayString { +impl Ord for ArrayString + where A: Array + Copy +{ fn cmp(&self, rhs: &Self) -> cmp::Ordering { (**self).cmp(&**rhs) } @@ -466,7 +509,9 @@ impl> Ord for ArrayString { #[cfg(feature="serde-1")] /// Requires crate feature `"serde-1"` -impl> Serialize for ArrayString { +impl Serialize for ArrayString + where A: Array + Copy +{ fn serialize(&self, serializer: S) -> Result where S: Serializer { @@ -476,7 +521,9 @@ impl> Serialize for ArrayString { #[cfg(feature="serde-1")] /// Requires crate feature `"serde-1"` -impl<'de, A: Array> Deserialize<'de> for ArrayString { +impl<'de, A> Deserialize<'de> for ArrayString + where A: Array + Copy +{ fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { @@ -485,7 +532,7 @@ impl<'de, A: Array> Deserialize<'de> for ArrayString { struct ArrayStringVisitor>(PhantomData); - impl<'de, A: Array> Visitor<'de> for ArrayStringVisitor { + impl<'de, A: Copy + Array> Visitor<'de> for ArrayStringVisitor { type Value = ArrayString; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/lib.rs b/src/lib.rs index 1fba400e..c650c006 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,14 +7,6 @@ //! - Optional, enabled by default //! - Use libstd; disable to use `no_std` instead. //! -//! - `use_union` -//! - Optional -//! - Requires Rust nightly channel -//! - Experimental: This flag uses nightly so it *may break* unexpectedly -//! at some point; since it doesn't change API this flag may also change -//! to do nothing in the future. -//! - Use the unstable feature untagged unions for the internal implementation, -//! which may have reduced space overhead //! - `serde-1` //! - Optional //! - Enable serialization for ArrayVec and ArrayString using serde 1.0 @@ -28,7 +20,7 @@ //! #![doc(html_root_url="https://docs.rs/arrayvec/0.4/")] #![cfg_attr(not(feature="std"), no_std)] -extern crate nodrop; + #[cfg(feature="serde-1")] extern crate serde; @@ -53,11 +45,10 @@ use std::fmt; #[cfg(feature="std")] use std::io; -#[cfg(not(feature="use_union"))] -use nodrop::NoDrop; -#[cfg(feature="use_union")] -use std::mem::ManuallyDrop as NoDrop; +mod maybe_uninit; +use maybe_uninit::MaybeUninit; +use maybe_uninit::MaybeUninitCopy; #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; @@ -75,14 +66,6 @@ pub use array_string::ArrayString; pub use errors::CapacityError; -unsafe fn new_array() -> A { - // Note: Returning an uninitialized value here only works - // if we can be sure the data is never used. The nullable pointer - // inside enum optimization conflicts with this this for example, - // so we need to be extra careful. See `NoDrop` enum. - mem::uninitialized() -} - /// A vector with a fixed capacity. /// /// The `ArrayVec` is a vector backed by a fixed size array. It keeps track of @@ -96,7 +79,7 @@ unsafe fn new_array() -> A { /// /// ArrayVec can be converted into a by value iterator. pub struct ArrayVec { - xs: NoDrop, + xs: MaybeUninit, len: A::Index, } @@ -133,7 +116,7 @@ impl ArrayVec { /// ``` pub fn new() -> ArrayVec { unsafe { - ArrayVec { xs: NoDrop::new(new_array()), len: Index::from(0) } + ArrayVec { xs: MaybeUninit::uninitialized(), len: Index::from(0) } } } @@ -565,7 +548,7 @@ impl ArrayVec { let other_len = other.len(); unsafe { - let dst = self.xs.as_mut_ptr().offset(self_len as isize); + let dst = self.xs.ptr_mut().offset(self_len as isize); ptr::copy_nonoverlapping(other.as_ptr(), dst, other_len); self.set_len(self_len + other_len); } @@ -623,15 +606,12 @@ impl ArrayVec { /// /// Return an `Ok` value with the array if length equals capacity, /// return an `Err` with self otherwise. - /// - /// `Note:` This function may incur unproportionally large overhead - /// to move the array out, its performance is not optimal. pub fn into_inner(self) -> Result { if self.len() < self.capacity() { Err(self) } else { unsafe { - let array = ptr::read(&*self.xs); + let array = ptr::read(self.xs.ptr() as *const A); mem::forget(self); Ok(array) } @@ -639,6 +619,7 @@ impl ArrayVec { } /// Dispose of `self` without the overwriting that is needed in Drop. + #[deprecated(note="This method is now obsolete and is no better than dropping")] pub fn dispose(mut self) { self.clear(); mem::forget(self); @@ -660,7 +641,7 @@ impl Deref for ArrayVec { #[inline] fn deref(&self) -> &[A::Item] { unsafe { - slice::from_raw_parts(self.xs.as_ptr(), self.len()) + slice::from_raw_parts(self.xs.ptr(), self.len()) } } } @@ -670,7 +651,7 @@ impl DerefMut for ArrayVec { fn deref_mut(&mut self) -> &mut [A::Item] { let len = self.len(); unsafe { - slice::from_raw_parts_mut(self.xs.as_mut_ptr(), len) + slice::from_raw_parts_mut(self.xs.ptr_mut(), len) } } } @@ -686,7 +667,7 @@ impl DerefMut for ArrayVec { /// ``` impl From for ArrayVec { fn from(array: A) -> Self { - ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) } + ArrayVec { xs: MaybeUninit::from(array), len: Index::from(A::capacity()) } } } diff --git a/src/maybe_uninit.rs b/src/maybe_uninit.rs new file mode 100644 index 00000000..a6c1e1b2 --- /dev/null +++ b/src/maybe_uninit.rs @@ -0,0 +1,105 @@ + + +use array::Array; +use std::mem::ManuallyDrop; +use std::mem::uninitialized; + +/// A combination of ManuallyDrop and “maybe uninitialized”; +/// this wraps a value that can be wholly or partially uninitialized; +/// it also has no drop regardless of the type of T. +/// +/// This is a stop-gap solution until MaybeUninit is stable in Rust's std. +#[repr(C)] +pub struct MaybeUninit(ManuallyDrop); + +impl MaybeUninit { + /// Create a new MaybeUninit with uninitialized interior + pub unsafe fn uninitialized() -> Self { + Self::from(uninitialized()) + } + + /// Create a new MaybeUninit from the value `v`. + pub fn from(v: T) -> Self { + MaybeUninit(ManuallyDrop::new(v)) + } + + // Raw pointer casts written so that we don't reference or access the + // uninitialized interior value + + /// Return a raw pointer to the start of the interior array + pub fn ptr(&self) -> *const T::Item + where T: Array + { + self as *const _ as *const T::Item + } + + /// Return a mut raw pointer to the start of the interior array + pub fn ptr_mut(&mut self) -> *mut T::Item + where T: Array + { + self as *mut _ as *mut T::Item + } +} + + +/// This is like MaybeUninit, we can do it properly for T: Copy +#[repr(C)] +#[derive(Copy, Clone)] +pub union MaybeUninitCopy + where T: Copy +{ + empty: (), + value: T, +} + +impl MaybeUninitCopy + where T: Copy +{ + /// Create a new MaybeUninit with uninitialized interior + pub unsafe fn uninitialized() -> Self { + Self { empty: () } + } + + /// Create a new MaybeUninit from the value `v`. + pub fn from(value: T) -> Self { + Self { value } + } + + // Raw pointer casts written so that we don't reference or access the + // uninitialized interior value + + /// Return a raw pointer to the start of the interior array + pub fn ptr(&self) -> *const T::Item + where T: Array + { + self as *const _ as *const T::Item + } + + /// Return a mut raw pointer to the start of the interior array + pub fn ptr_mut(&mut self) -> *mut T::Item + where T: Array + { + self as *mut _ as *mut T::Item + } +} + + +#[test] +fn test_offset() { + use std::ptr; + + let mut mu = MaybeUninit::from([1, 2, 3]); + assert!(ptr::eq(mu.ptr(), &mu.0[0])); + assert!(ptr::eq(mu.ptr_mut(), &mut mu.0[0])); +} + +#[test] +#[cfg(feature = "std")] +fn test_offset_string() { + use std::ptr; + + let s = String::from; + let mut mu = MaybeUninit::from([s("a"), s("b")]); + assert!(ptr::eq(mu.ptr(), &mu.0[0])); + assert!(ptr::eq(mu.ptr_mut(), &mut mu.0[0])); +} diff --git a/tests/tests.rs b/tests/tests.rs index 043cde23..3deacbdc 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -281,6 +281,14 @@ fn test_compact_size() { assert!(mem::size_of::() <= 24); } +#[test] +fn test_still_works_with_option_arrayvec() { + type RefArray = ArrayVec<[&'static i32; 2]>; + let array = Some(RefArray::new()); + assert!(array.is_some()); + println!("{:?}", array); +} + #[test] fn test_drain() { let mut v = ArrayVec::from([0; 8]);