From f45c14191794cfa655ddbb31cc60f5d6fbfdf00d Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Mar 2018 22:28:33 +0200 Subject: [PATCH 01/12] =?UTF-8?q?FEAT:=20Implement=20a=20careful=20?= =?UTF-8?q?=E2=80=9CMaybeUninit=E2=80=9D=20using=20ManuallyDrop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a cautious version of MaybeUninit, since we don't have one in libstd, based on ManuallyDrop. This moves ArrayVec to a nice, no size overhead implementation by default. We use Rust version sniffing (build script) to automatically use this for new enough Rust versions. This doesn't kill nodrop unfortunately, it still remains as the fallback. --- Cargo.toml | 7 ++++++- build.rs | 13 +++++++++++++ src/lib.rs | 36 +++++++++++++++++------------------- src/maybe_uninit.rs | 34 ++++++++++++++++++++++++++++++++++ src/maybe_uninit_nodrop.rs | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 build.rs create mode 100644 src/maybe_uninit.rs create mode 100644 src/maybe_uninit_nodrop.rs diff --git a/Cargo.toml b/Cargo.toml index f7a19480..4dbdd0cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ repository = "https://github.com/bluss/arrayvec" keywords = ["stack", "vector", "array", "data-structure", "no_std"] categories = ["data-structures", "no-std"] +[build-dependencies] +version_check = "0.1" + [dependencies] nodrop = { version = "0.1.12", path = "nodrop", default-features = false } @@ -37,12 +40,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/build.rs b/build.rs new file mode 100644 index 00000000..87765430 --- /dev/null +++ b/build.rs @@ -0,0 +1,13 @@ + +//! +//! This build script detects if we have new enough Rust +//! + +extern crate version_check; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + if let Some((true, _)) = version_check::is_min_version("1.20.0") { + println!("cargo:rustc-cfg=has_manuallydrop"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 1fba400e..88b06fd6 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,6 +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 +46,16 @@ 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; +#[cfg(has_manuallydrop)] +mod maybe_uninit; +#[cfg(has_manuallydrop)] +use maybe_uninit::MaybeUninit; + +#[cfg(not(has_manuallydrop))] +mod maybe_uninit_nodrop; +#[cfg(not(has_manuallydrop))] +use maybe_uninit_nodrop::MaybeUninit; #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; @@ -96,7 +94,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 +131,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 +563,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); } @@ -631,7 +629,7 @@ impl ArrayVec { Err(self) } else { unsafe { - let array = ptr::read(&*self.xs); + let array = ptr::read(self.xs.ptr()); mem::forget(self); Ok(array) } @@ -660,7 +658,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()).as_ptr(), self.len()) } } } @@ -670,7 +668,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()).as_mut_ptr(), len) } } } @@ -686,7 +684,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..ea623267 --- /dev/null +++ b/src/maybe_uninit.rs @@ -0,0 +1,34 @@ + + +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. +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)) + } + + /// Return a raw pointer to the interior + pub fn ptr(&self) -> *const T { + (&self.0) as *const ManuallyDrop<_> as *const T + } + + /// Return a raw pointer to the interior (mutable) + pub fn ptr_mut(&mut self) -> *mut T { + (&mut self.0) as *mut ManuallyDrop<_> as *mut T + } +} + + + diff --git a/src/maybe_uninit_nodrop.rs b/src/maybe_uninit_nodrop.rs new file mode 100644 index 00000000..4ceb75a7 --- /dev/null +++ b/src/maybe_uninit_nodrop.rs @@ -0,0 +1,33 @@ + + +use nodrop::NoDrop; +use std::mem::uninitialized; + +/// A combination of NoDrop and “maybe uninitialized”; +/// this wraps a value that can be wholly or partially uninitialized. +pub struct MaybeUninit(NoDrop); + +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(NoDrop::new(v)) + } + + /// Return a raw pointer to the interior + pub fn ptr(&self) -> *const T { + &**(&self.0) + } + + /// Return a raw pointer to the interior (mutable) + pub fn ptr_mut(&mut self) -> *mut T { + &mut **(&mut self.0) + } +} + + + From 7059dc44c1f4468e7d573e96dca60551e26cd056 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Mar 2018 22:44:51 +0200 Subject: [PATCH 02/12] FIX: Use raw pointer casts for MaybeUninit The nodrop/fallback code is as it was, it uses the Array trait accessors as_ptr/as_ptr_mut; but we really want to do this properly with a raw pointer cast according to discussion with arielb. The idea is that we don't take a reference to a partially uninitialized array value -- we just extract a raw pointer to the first array element instead. --- src/lib.rs | 6 +++--- src/maybe_uninit.rs | 40 ++++++++++++++++++++++++++++++++------ src/maybe_uninit_nodrop.rs | 18 ++++++++++------- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 88b06fd6..a489be34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -629,7 +629,7 @@ impl ArrayVec { Err(self) } else { unsafe { - let array = ptr::read(self.xs.ptr()); + let array = ptr::read(self.xs.ptr() as *const A); mem::forget(self); Ok(array) } @@ -658,7 +658,7 @@ impl Deref for ArrayVec { #[inline] fn deref(&self) -> &[A::Item] { unsafe { - slice::from_raw_parts((*self.xs.ptr()).as_ptr(), self.len()) + slice::from_raw_parts(self.xs.ptr(), self.len()) } } } @@ -668,7 +668,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.ptr_mut()).as_mut_ptr(), len) + slice::from_raw_parts_mut(self.xs.ptr_mut(), len) } } } diff --git a/src/maybe_uninit.rs b/src/maybe_uninit.rs index ea623267..a546563a 100644 --- a/src/maybe_uninit.rs +++ b/src/maybe_uninit.rs @@ -1,11 +1,13 @@ +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. +#[repr(C)] pub struct MaybeUninit(ManuallyDrop); impl MaybeUninit { @@ -19,16 +21,42 @@ impl MaybeUninit { MaybeUninit(ManuallyDrop::new(v)) } - /// Return a raw pointer to the interior - pub fn ptr(&self) -> *const T { - (&self.0) as *const ManuallyDrop<_> as *const T + // 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 raw pointer to the interior (mutable) - pub fn ptr_mut(&mut self) -> *mut T { - (&mut self.0) as *mut ManuallyDrop<_> as *mut T + /// 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/src/maybe_uninit_nodrop.rs b/src/maybe_uninit_nodrop.rs index 4ceb75a7..99ab54c9 100644 --- a/src/maybe_uninit_nodrop.rs +++ b/src/maybe_uninit_nodrop.rs @@ -1,5 +1,5 @@ - +use array::Array; use nodrop::NoDrop; use std::mem::uninitialized; @@ -18,14 +18,18 @@ impl MaybeUninit { MaybeUninit(NoDrop::new(v)) } - /// Return a raw pointer to the interior - pub fn ptr(&self) -> *const T { - &**(&self.0) + /// Return a raw pointer to the start of the interior array + pub fn ptr(&self) -> *const T::Item + where T: Array + { + self.0.as_ptr() } - /// Return a raw pointer to the interior (mutable) - pub fn ptr_mut(&mut self) -> *mut T { - &mut **(&mut self.0) + /// 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.0.as_mut_ptr() } } From 51debed586dc220a0a993ab9e2c655213a83e7b5 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Mar 2018 23:09:02 +0200 Subject: [PATCH 03/12] API: Deprecate .dispose() --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index a489be34..f2f8d666 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -637,6 +637,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); From 2cebc1f93265f89534fdbb6e7f664ceba8a56417 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Nov 2018 10:53:12 +0100 Subject: [PATCH 04/12] DOC: MaybeUninit comment --- src/maybe_uninit.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/maybe_uninit.rs b/src/maybe_uninit.rs index a546563a..aef1a524 100644 --- a/src/maybe_uninit.rs +++ b/src/maybe_uninit.rs @@ -7,6 +7,8 @@ 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); From 5f88d1f678585ad33f2176e700f863cb9e24acf9 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Nov 2018 11:14:42 +0100 Subject: [PATCH 05/12] FIX: Remove the nodrop fallback We have ManuallyDrop as the fallback since Rust 1.20, and don't need nodrop anymore. --- Cargo.toml | 2 -- build.rs | 13 ------------- src/lib.rs | 8 -------- src/maybe_uninit_nodrop.rs | 37 ------------------------------------- 4 files changed, 60 deletions(-) delete mode 100644 build.rs delete mode 100644 src/maybe_uninit_nodrop.rs diff --git a/Cargo.toml b/Cargo.toml index 4dbdd0cc..93e401d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,8 @@ keywords = ["stack", "vector", "array", "data-structure", "no_std"] categories = ["data-structures", "no-std"] [build-dependencies] -version_check = "0.1" [dependencies] -nodrop = { version = "0.1.12", path = "nodrop", default-features = false } [dependencies.serde] version = "1.0" diff --git a/build.rs b/build.rs deleted file mode 100644 index 87765430..00000000 --- a/build.rs +++ /dev/null @@ -1,13 +0,0 @@ - -//! -//! This build script detects if we have new enough Rust -//! - -extern crate version_check; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - if let Some((true, _)) = version_check::is_min_version("1.20.0") { - println!("cargo:rustc-cfg=has_manuallydrop"); - } -} diff --git a/src/lib.rs b/src/lib.rs index f2f8d666..0035be4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,6 @@ #![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; @@ -47,16 +46,9 @@ use std::fmt; use std::io; -#[cfg(has_manuallydrop)] mod maybe_uninit; -#[cfg(has_manuallydrop)] use maybe_uninit::MaybeUninit; -#[cfg(not(has_manuallydrop))] -mod maybe_uninit_nodrop; -#[cfg(not(has_manuallydrop))] -use maybe_uninit_nodrop::MaybeUninit; - #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; diff --git a/src/maybe_uninit_nodrop.rs b/src/maybe_uninit_nodrop.rs deleted file mode 100644 index 99ab54c9..00000000 --- a/src/maybe_uninit_nodrop.rs +++ /dev/null @@ -1,37 +0,0 @@ - -use array::Array; -use nodrop::NoDrop; -use std::mem::uninitialized; - -/// A combination of NoDrop and “maybe uninitialized”; -/// this wraps a value that can be wholly or partially uninitialized. -pub struct MaybeUninit(NoDrop); - -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(NoDrop::new(v)) - } - - /// Return a raw pointer to the start of the interior array - pub fn ptr(&self) -> *const T::Item - where T: Array - { - self.0.as_ptr() - } - - /// 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.0.as_mut_ptr() - } -} - - - From 7e991daec7171ada6fcbf4c4cc767dbb63cca811 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Nov 2018 13:28:32 +0100 Subject: [PATCH 06/12] MAINT: Rust 1.22.1 was the first version with Copy for ManuallyDrop --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 62fe69fe..52c55c67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,13 @@ env: - FEATURES='serde-1' matrix: include: - - rust: 1.20.0 + - rust: 1.22.1 + env: + - FEATURES='array-sizes-33-128 array-sizes-129-255' - 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 env: - FEATURES='array-sizes-33-128 array-sizes-129-255' From 97eb3fdec58430d6b570c585f8ec37ce7a7b965c Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 25 Nov 2018 12:18:59 +0100 Subject: [PATCH 07/12] FEAT: Use a separate, and hopefully sound MaybeUninitCopy for ArrayString This is the "real" union solution, and ArrayString can use it since its backing array is Copy. Unfortunately, we'll have to use the Copy bound on the type, making it "viral". --- src/array_string.rs | 119 ++++++++++++++++++++++++++++++-------------- src/lib.rs | 9 +--- src/maybe_uninit.rs | 41 +++++++++++++++ 3 files changed, 125 insertions(+), 44 deletions(-) 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 0035be4f..027c1c06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ use std::io; mod maybe_uninit; use maybe_uninit::MaybeUninit; +use maybe_uninit::MaybeUninitCopy; #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; @@ -65,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 diff --git a/src/maybe_uninit.rs b/src/maybe_uninit.rs index aef1a524..a6c1e1b2 100644 --- a/src/maybe_uninit.rs +++ b/src/maybe_uninit.rs @@ -42,6 +42,47 @@ impl MaybeUninit { } +/// 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() { From c321bf216c5dea17ac9adb4c8b4fd80e9e467a39 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 Dec 2018 11:58:01 +0100 Subject: [PATCH 08/12] FIX: Add associated constant for Array's capacity This isn't of much use at the moment, but future Rust features could mean we can use it. --- src/array.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/array.rs b/src/array.rs index 8d043e4c..b6ccd017 100644 --- a/src/array.rs +++ b/src/array.rs @@ -15,9 +15,11 @@ 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)] @@ -91,6 +93,7 @@ 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 _ } From 6bb14060910e05891a1489d4d17204922b548ea5 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 Dec 2018 12:01:09 +0100 Subject: [PATCH 09/12] FIX: Remove unused .as_mut_ptr() on the Array trait Raw pointer taking should go through the MaybeUninit wrappers around the arrays anyway, when it is partially uninitialized, which it often is. The remaining .as_ptr() and .as_slice() methods on Array is only used on a fully initialized array in ArrayString::from_byte_string --- src/array.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/array.rs b/src/array.rs index b6ccd017..ad18c795 100644 --- a/src/array.rs +++ b/src/array.rs @@ -23,8 +23,6 @@ pub unsafe trait Array { #[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; } @@ -99,9 +97,6 @@ macro_rules! fix_array_impl { 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 } } ) From 1ee64821bee835ac12e91d6bee8768286c9d3313 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 Dec 2018 12:13:37 +0100 Subject: [PATCH 10/12] DOC: Remove warning on ArrayVec's into_inner method --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 027c1c06..c650c006 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -606,9 +606,6 @@ 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) From 306b3632a2548b975dce5f80225cbcda4136e3af Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 Dec 2018 12:29:46 +0100 Subject: [PATCH 11/12] MAINT: Edit travis setup for changed crate features --- .travis.yml | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 52c55c67..fffc9a8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,36 +7,31 @@ matrix: - rust: 1.22.1 env: - FEATURES='array-sizes-33-128 array-sizes-129-255' + - rust: stable - rust: stable env: - - NODEFAULT=1 - - NODROP_FEATURES='use_needs_drop' + - 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" From 7f7e3128233cc44a14c65388c452a42644402f11 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 3 Dec 2018 18:40:08 +0100 Subject: [PATCH 12/12] TEST: Add test that Some(ArrayVec<[&_;_]>).is_some() This seems like a trivial test, but since it can fail, it shows us that we don't have a sound implementation yet. --- tests/tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) 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]);