From f2aad9f9a7720313edb5182e54be68ff51ea16ae Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 10 Jul 2019 18:13:06 +0200 Subject: [PATCH 1/5] API: Update ArrayExt's methods to expose slices instead of pointers This is a nicer and simpler interface to expose, since the ref to uninit array to pointer cast is not usable anyway (it's historic now, arrayvec itself does not use it anymore). --- src/array.rs | 27 ++++++--------------------- src/array_string.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/array.rs b/src/array.rs index ad18c79..ed49211 100644 --- a/src/array.rs +++ b/src/array.rs @@ -20,10 +20,8 @@ pub unsafe trait Array { type Index: Index; /// The array's element capacity const CAPACITY: usize; - #[doc(hidden)] - fn as_ptr(&self) -> *const Self::Item; - #[doc(hidden)] - fn capacity() -> usize; + fn as_slice(&self) -> &[Self::Item]; + fn as_mut_slice(&mut self) -> &mut [Self::Item]; } pub trait Index : PartialEq + Copy { @@ -31,19 +29,6 @@ pub trait Index : PartialEq + Copy { fn from(usize) -> Self; } -use std::slice::{from_raw_parts}; - -pub trait ArrayExt : Array { - #[inline(always)] - fn as_slice(&self) -> &[Self::Item] { - unsafe { - from_raw_parts(self.as_ptr(), Self::capacity()) - } - } -} - -impl ArrayExt for A where A: Array { } - impl Index for () { #[inline(always)] fn to_usize(self) -> usize { 0 } @@ -93,11 +78,11 @@ macro_rules! fix_array_impl { type Index = $index_type; const CAPACITY: usize = $len; #[doc(hidden)] - #[inline(always)] - fn as_ptr(&self) -> *const T { self as *const _ as *const _ } + #[inline] + fn as_slice(&self) -> &[Self::Item] { self } #[doc(hidden)] - #[inline(always)] - fn capacity() -> usize { $len } + #[inline] + fn as_mut_slice(&mut self) -> &mut [Self::Item] { self } } ) } diff --git a/src/array_string.rs b/src/array_string.rs index d83b463..475977b 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use std::str::Utf8Error; use std::slice; -use array::{Array, ArrayExt}; +use array::Array; use array::Index; use CapacityError; use char::encode_utf8; diff --git a/src/lib.rs b/src/lib.rs index f27e3b6..be734c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,7 +155,7 @@ impl ArrayVec { /// assert_eq!(array.capacity(), 3); /// ``` #[inline] - pub fn capacity(&self) -> usize { A::capacity() } + pub fn capacity(&self) -> usize { A::CAPACITY } /// Return if the `ArrayVec` is completely filled. /// From ba94336265dd59fed72984dd3a17d9cc95b49e76 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 10 Jul 2019 18:14:30 +0200 Subject: [PATCH 2/5] DOC: Draft release note for 0.5 --- README.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rst b/README.rst index 6f6476f..7498526 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,20 @@ __ https://docs.rs/arrayvec Recent Changes (arrayvec) ------------------------- +- 0.5.0 (not released yet) + + - Add ``FromStr`` impl for ``ArrayString`` by @despawnerer + - Use a union in the implementation of ``ArrayString`` (stable Rust), + while this is only used for ``ArrayVec`` on nightly. + - Add method ``try_extend_from_slice`` to ``ArrayVec``, which is always + effecient by @Thomasdezeeuw. + - Add method ``remaining_capacity`` by @Thomasdezeeuw + - Improve performance of the ``extend`` method. + - The index type of zero capacity vectors is now itself zero size, by + @clarcharr + - Use ``drop_in_place`` for truncate and clear methods. This affects drop order + and resume from panic during drop. + - 0.4.11 - In Rust 1.36 or later, use newly stable MaybeUninit. This extends the From 8093e8d886509ba7a0dc3f8a9b2153a901a22036 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 1 Sep 2019 12:18:17 +0200 Subject: [PATCH 3/5] FEAT: Switch to using MaybeUninit for everything Use std::mem::MaybeUninit and stop using nodrop as a fallback. This means we require Rust 1.36 --- .travis.yml | 8 +--- Cargo.toml | 3 -- build.rs | 90 -------------------------------------- src/array_string.rs | 8 ++-- src/lib.rs | 20 ++------- src/maybe_uninit.rs | 30 ++++++------- src/maybe_uninit_copy.rs | 43 ------------------ src/maybe_uninit_nodrop.rs | 41 ----------------- src/maybe_uninit_stable.rs | 40 ----------------- tests/tests.rs | 8 ---- 10 files changed, 22 insertions(+), 269 deletions(-) delete mode 100644 build.rs delete mode 100644 src/maybe_uninit_copy.rs delete mode 100644 src/maybe_uninit_nodrop.rs delete mode 100644 src/maybe_uninit_stable.rs diff --git a/.travis.yml b/.travis.yml index 8b5b3ac..df3009b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ env: - FEATURES='serde-1' matrix: include: - - rust: 1.24.1 + - rust: 1.36.0 env: - FEATURES='array-sizes-33-128 array-sizes-129-255' - rust: stable @@ -14,23 +14,17 @@ matrix: - rust: stable env: - FEATURES='array-sizes-33-128 array-sizes-129-255' - - ARRAYVECTEST_ENSURE_MAYBEUNINIT=1 - rust: beta - rust: nightly - env: - - ARRAYVECTEST_ENSURE_UNION=1 - rust: nightly env: - FEATURES='serde' - - ARRAYVECTEST_ENSURE_UNION=1 - rust: nightly env: - FEATURES='serde-1' - - ARRAYVECTEST_ENSURE_UNION=1 - rust: nightly env: - FEATURES='array-sizes-33-128 array-sizes-129-255' - - ARRAYVECTEST_ENSURE_MAYBEUNINIT=1 branches: only: - master diff --git a/Cargo.toml b/Cargo.toml index bd634c8..fb8b2eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,6 @@ categories = ["data-structures", "no-std"] [build-dependencies] -[dependencies] -nodrop = { version = "0.1.12", path = "nodrop", default-features = false } - [dependencies.serde] version = "1.0" optional = true diff --git a/build.rs b/build.rs deleted file mode 100644 index a91c5f4..0000000 --- a/build.rs +++ /dev/null @@ -1,90 +0,0 @@ - -use std::env; -use std::io::Write; -use std::process::{Command, Stdio}; - -fn main() { - // we need to output *some* file to opt out of the default - println!("cargo:rerun-if-changed=build.rs"); - - detect_maybe_uninit(); -} - -fn detect_maybe_uninit() { - let has_stable_maybe_uninit = probe(&stable_maybe_uninit()); - if has_stable_maybe_uninit { - println!("cargo:rustc-cfg=has_stable_maybe_uninit"); - return; - } - let has_unstable_union_with_md = probe(&maybe_uninit_code(true)); - if has_unstable_union_with_md { - println!("cargo:rustc-cfg=has_manually_drop_in_union"); - println!("cargo:rustc-cfg=has_union_feature"); - } -} - -// To guard against changes in this currently unstable feature, use -// a detection tests instead of a Rustc version and/or date test. -fn stable_maybe_uninit() -> String { - let code = " - #![allow(warnings)] - use std::mem::MaybeUninit; - - fn main() { } - "; - code.to_string() -} - -// To guard against changes in this currently unstable feature, use -// a detection tests instead of a Rustc version and/or date test. -fn maybe_uninit_code(use_feature: bool) -> String { - let feature = if use_feature { "#![feature(untagged_unions)]" } else { "" }; - - let code = " - #![allow(warnings)] - use std::mem::ManuallyDrop; - - #[derive(Copy)] - pub union MaybeUninit { - empty: (), - value: ManuallyDrop, - } - - impl Clone for MaybeUninit where T: Copy - { - fn clone(&self) -> Self { *self } - } - - fn main() { - let value1 = MaybeUninit::<[i32; 3]> { empty: () }; - let value2 = MaybeUninit { value: ManuallyDrop::new([1, 2, 3]) }; - } - "; - - - [feature, code].concat() -} - -/// Test if a code snippet can be compiled -fn probe(code: &str) -> bool { - let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); - let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR"); - - let mut child = Command::new(rustc) - .arg("--out-dir") - .arg(out_dir) - .arg("--emit=obj") - .arg("-") - .stdin(Stdio::piped()) - .spawn() - .expect("rustc probe"); - - child - .stdin - .as_mut() - .expect("rustc stdin") - .write_all(code.as_bytes()) - .expect("write rustc stdin"); - - child.wait().expect("rustc probe").success() -} diff --git a/src/array_string.rs b/src/array_string.rs index 475977b..5aa8d4e 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -17,7 +17,7 @@ use char::encode_utf8; #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; -use super::MaybeUninitCopy; +use super::MaybeUninit as MaybeUninitCopy; /// A string with a fixed capacity. /// @@ -98,10 +98,10 @@ impl ArrayString /// ``` pub fn from_byte_string(b: &A) -> Result { let len = str::from_utf8(b.as_slice())?.len(); - debug_assert_eq!(len, A::capacity()); + debug_assert_eq!(len, A::CAPACITY); Ok(ArrayString { xs: MaybeUninitCopy::from(*b), - len: Index::from(A::capacity()), + len: Index::from(A::CAPACITY), }) } @@ -114,7 +114,7 @@ impl ArrayString /// assert_eq!(string.capacity(), 3); /// ``` #[inline] - pub fn capacity(&self) -> usize { A::capacity() } + pub fn capacity(&self) -> usize { A::CAPACITY } /// Return if the `ArrayString` is completely filled. /// diff --git a/src/lib.rs b/src/lib.rs index be734c0..0742cfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,9 +28,6 @@ extern crate serde; #[cfg(not(feature="std"))] extern crate core as std; -#[cfg(not(has_manually_drop_in_union))] -extern crate nodrop; - use std::cmp; use std::iter; use std::mem; @@ -50,19 +47,8 @@ use std::fmt; use std::io; -#[cfg(has_stable_maybe_uninit)] -#[path="maybe_uninit_stable.rs"] mod maybe_uninit; -#[cfg(all(not(has_stable_maybe_uninit), has_manually_drop_in_union))] -mod maybe_uninit; -#[cfg(all(not(has_stable_maybe_uninit), not(has_manually_drop_in_union)))] -#[path="maybe_uninit_nodrop.rs"] -mod maybe_uninit; - -mod maybe_uninit_copy; - use maybe_uninit::MaybeUninit; -use maybe_uninit_copy::MaybeUninitCopy; #[cfg(feature="serde-1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; @@ -223,7 +209,7 @@ impl ArrayVec { /// assert!(overflow.is_err()); /// ``` pub fn try_push(&mut self, element: A::Item) -> Result<(), CapacityError> { - if self.len() < A::capacity() { + if self.len() < A::CAPACITY { unsafe { self.push_unchecked(element); } @@ -258,7 +244,7 @@ impl ArrayVec { #[inline] pub unsafe fn push_unchecked(&mut self, element: A::Item) { let len = self.len(); - debug_assert!(len < A::capacity()); + debug_assert!(len < A::CAPACITY); ptr::write(self.get_unchecked_mut(len), element); self.set_len(len + 1); } @@ -680,7 +666,7 @@ impl DerefMut for ArrayVec { /// ``` impl From for ArrayVec { fn from(array: A) -> Self { - ArrayVec { xs: MaybeUninit::from(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 index 9ed1f6a..4903fa3 100644 --- a/src/maybe_uninit.rs +++ b/src/maybe_uninit.rs @@ -1,27 +1,28 @@ use array::Array; -use std::mem::ManuallyDrop; +use std::mem::MaybeUninit as StdMaybeUninit; -/// 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)] // for cast from self ptr to value -pub union MaybeUninit { - empty: (), - value: ManuallyDrop, +#[derive(Copy)] +pub struct MaybeUninit { + inner: StdMaybeUninit, +} + +impl Clone for MaybeUninit + where T: Copy +{ + fn clone(&self) -> Self { *self } } -// Why we don't use std's MaybeUninit on nightly? See the ptr method impl MaybeUninit { /// Create a new MaybeUninit with uninitialized interior pub unsafe fn uninitialized() -> Self { - MaybeUninit { empty: () } + MaybeUninit { inner: StdMaybeUninit::uninit() } } /// Create a new MaybeUninit from the value `v`. pub fn from(v: T) -> Self { - MaybeUninit { value: ManuallyDrop::new(v) } + MaybeUninit { inner: StdMaybeUninit::new(v) } } // Raw pointer casts written so that we don't reference or access the @@ -31,16 +32,13 @@ impl MaybeUninit { pub fn ptr(&self) -> *const T::Item where T: Array { - // std MaybeUninit creates a &self.value reference here which is - // not guaranteed to be sound in our case - we will partially - // initialize the value, not always wholly. - self as *const _ as *const T::Item + self.inner.as_ptr() 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 + self.inner.as_mut_ptr() as *mut T::Item } } diff --git a/src/maybe_uninit_copy.rs b/src/maybe_uninit_copy.rs deleted file mode 100644 index 1a1c9ec..0000000 --- a/src/maybe_uninit_copy.rs +++ /dev/null @@ -1,43 +0,0 @@ - -use array::Array; - -#[derive(Copy, Clone)] -#[repr(C)] // for cast from self ptr to value -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 - } -} - diff --git a/src/maybe_uninit_nodrop.rs b/src/maybe_uninit_nodrop.rs deleted file mode 100644 index 8213b97..0000000 --- a/src/maybe_uninit_nodrop.rs +++ /dev/null @@ -1,41 +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. -/// -/// NOTE: This is known to not be a good solution, but it's the one we have kept -/// working on stable Rust. Stable improvements are encouraged, in any form, -/// but of course we are waiting for a real, stable, MaybeUninit. -pub struct MaybeUninit(NoDrop); -// why don't we use ManuallyDrop here: It doesn't inhibit -// enum layout optimizations that depend on T, and we support older Rust. - -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 *const T as *const _ - } - - /// Return a mut raw pointer to the start of the interior array - pub fn ptr_mut(&mut self) -> *mut T::Item - where T: Array - { - &mut *self.0 as *mut T as *mut _ - } -} - diff --git a/src/maybe_uninit_stable.rs b/src/maybe_uninit_stable.rs deleted file mode 100644 index cb631a9..0000000 --- a/src/maybe_uninit_stable.rs +++ /dev/null @@ -1,40 +0,0 @@ - - -use array::Array; -use std::mem::MaybeUninit as StdMaybeUninit; - -pub struct MaybeUninit { - inner: StdMaybeUninit, -} - -impl MaybeUninit { - /// Create a new MaybeUninit with uninitialized interior - pub unsafe fn uninitialized() -> Self { - MaybeUninit { inner: StdMaybeUninit::uninit() } - } - - /// Create a new MaybeUninit from the value `v`. - pub fn from(v: T) -> Self { - MaybeUninit { inner: StdMaybeUninit::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 - { - // std MaybeUninit creates a &self.value reference here which is - // not guaranteed to be sound in our case - we will partially - // initialize the value, not always wholly. - self.inner.as_ptr() 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.inner.as_mut_ptr() as *mut T::Item - } -} diff --git a/tests/tests.rs b/tests/tests.rs index 306689c..f8bcd0f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -633,14 +633,6 @@ fn test_sizes_129_255() { ArrayVec::from([0u8; 255]); } - -#[test] -fn test_newish_stable_uses_maybe_uninit() { - if option_env!("ARRAYVECTEST_ENSURE_MAYBEUNINIT").map(|s| !s.is_empty()).unwrap_or(false) { - assert!(cfg!(has_stable_maybe_uninit)); - } -} - #[test] fn test_extend_zst() { let mut range = 0..10; From 7c33afb3aef2c3f624b67b245161576976d24dc7 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 1 Sep 2019 13:03:29 +0200 Subject: [PATCH 4/5] DOC: Update min rust version to Rust 1.36 for MaybeUninit --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0742cfc..9067a1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //! //! ## Rust Version //! -//! This version of arrayvec requires Rust 1.24 or later. +//! This version of arrayvec requires Rust 1.36 or later. //! #![doc(html_root_url="https://docs.rs/arrayvec/0.4/")] #![cfg_attr(not(feature="std"), no_std)] From 470cfd2a48b2517be8fc4f2bccdcdd76e2e9fcd3 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 1 Sep 2019 13:09:10 +0200 Subject: [PATCH 5/5] FIX: Update serde impls for Array changes --- src/array_string.rs | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_string.rs b/src/array_string.rs index 5aa8d4e..5159dfb 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -547,7 +547,7 @@ impl<'de, A> Deserialize<'de> for ArrayString type Value = ArrayString; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a string no more than {} bytes long", A::capacity()) + write!(formatter, "a string no more than {} bytes long", A::CAPACITY) } fn visit_str(self, v: &str) -> Result diff --git a/src/lib.rs b/src/lib.rs index 9067a1f..1c176fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1119,7 +1119,7 @@ impl<'de, T: Deserialize<'de>, A: Array> Deserialize<'de> for ArrayVec; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "an array with no more than {} items", A::capacity()) + write!(formatter, "an array with no more than {} items", A::CAPACITY) } fn visit_seq(self, mut seq: SA) -> Result @@ -1129,7 +1129,7 @@ impl<'de, T: Deserialize<'de>, A: Array> Deserialize<'de> for ArrayVec