Skip to content

Commit 7496a5f

Browse files
authored
Merge pull request #115 from bluss/merge-0.4
Merge 0.4 branch to master
2 parents ac94d16 + 94ab27a commit 7496a5f

9 files changed

+234
-31
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ matrix:
1919
- rust: nightly
2020
env:
2121
- NODEFAULT=1
22+
- ARRAYVECTEST_ENSURE_UNION=1
2223
- rust: nightly
2324
env:
2425
- NODROP_FEATURES='use_needs_drop'
26+
- ARRAYVECTEST_ENSURE_UNION=1
2527
- rust: nightly
2628
env:
2729
- FEATURES='serde use_union'
2830
- NODROP_FEATURES='use_union'
31+
- ARRAYVECTEST_ENSURE_UNION=1
2932
branches:
3033
only:
3134
- master

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "arrayvec"
3-
version = "0.4.8"
3+
version = "0.4.9"
44
authors = ["bluss"]
55
license = "MIT/Apache-2.0"
66

@@ -11,6 +11,8 @@ repository = "https://github.com/bluss/arrayvec"
1111
keywords = ["stack", "vector", "array", "data-structure", "no_std"]
1212
categories = ["data-structures", "no-std"]
1313

14+
[build-dependencies]
15+
1416
[dependencies]
1517
nodrop = { version = "0.1.12", path = "nodrop", default-features = false }
1618

@@ -37,12 +39,14 @@ harness = false
3739
[features]
3840
default = ["std"]
3941
std = []
40-
use_union = []
4142
serde-1 = ["serde"]
4243

4344
array-sizes-33-128 = []
4445
array-sizes-129-255 = []
4546

47+
# has no effect
48+
use_union = []
49+
4650
[package.metadata.docs.rs]
4751
features = ["serde-1"]
4852

README.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ __ https://docs.rs/arrayvec
2222
Recent Changes (arrayvec)
2323
-------------------------
2424

25+
- 0.4.9
26+
27+
- Use ``union`` in the implementation on when this is detected to be supported
28+
(nightly only for now). This is a better solution for treating uninitialized
29+
regions correctly, and we'll use it in stable Rust as soon as we are able.
30+
When this is enabled, the ``ArrayVec`` has no space overhead in its memory
31+
layout, although the size of the vec should not be relied upon. (See `#114`_)
32+
- ``ArrayString`` updated to not use uninitialized memory, it instead zeros its
33+
backing array. This will be refined in the next version, since we
34+
need to make changes to the user visible API.
35+
- The ``use_union`` feature now does nothing (like its documentation foretold).
36+
37+
.. _`#114`: https://github.com/bluss/arrayvec/pull/114
38+
2539
- 0.4.8
2640

2741
- Implement Clone and Debug for ``IntoIter`` by @clarcharr

build.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
2+
use std::env;
3+
use std::io::Write;
4+
use std::process::{Command, Stdio};
5+
6+
fn main() {
7+
// we need to output *some* file to opt out of the default
8+
println!("cargo:rerun-if-changed=build.rs");
9+
10+
detect_maybe_uninit();
11+
}
12+
13+
fn detect_maybe_uninit() {
14+
let has_unstable_union_with_md = probe(&maybe_uninit_code(true));
15+
if has_unstable_union_with_md {
16+
println!("cargo:rustc-cfg=has_manually_drop_in_union");
17+
println!("cargo:rustc-cfg=has_union_feature");
18+
return;
19+
}
20+
21+
let has_stable_union_with_md = probe(&maybe_uninit_code(false));
22+
if has_stable_union_with_md {
23+
println!("cargo:rustc-cfg=has_manually_drop_in_union");
24+
}
25+
}
26+
27+
// To guard against changes in this currently unstable feature, use
28+
// a detection tests instead of a Rustc version and/or date test.
29+
fn maybe_uninit_code(use_feature: bool) -> String {
30+
let feature = if use_feature { "#![feature(untagged_unions)]" } else { "" };
31+
32+
let code = "
33+
#![allow(warnings)]
34+
use std::mem::ManuallyDrop;
35+
36+
#[derive(Copy)]
37+
pub union MaybeUninit<T> {
38+
empty: (),
39+
value: ManuallyDrop<T>,
40+
}
41+
42+
impl<T> Clone for MaybeUninit<T> where T: Copy
43+
{
44+
fn clone(&self) -> Self { *self }
45+
}
46+
47+
fn main() {
48+
let value1 = MaybeUninit::<[i32; 3]> { empty: () };
49+
let value2 = MaybeUninit { value: ManuallyDrop::new([1, 2, 3]) };
50+
}
51+
";
52+
53+
54+
[feature, code].concat()
55+
}
56+
57+
/// Test if a code snippet can be compiled
58+
fn probe(code: &str) -> bool {
59+
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
60+
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
61+
62+
let mut child = Command::new(rustc)
63+
.arg("--out-dir")
64+
.arg(out_dir)
65+
.arg("--emit=obj")
66+
.arg("-")
67+
.stdin(Stdio::piped())
68+
.spawn()
69+
.expect("rustc probe");
70+
71+
child
72+
.stdin
73+
.as_mut()
74+
.expect("rustc stdin")
75+
.write_all(code.as_bytes())
76+
.expect("write rustc stdin");
77+
78+
child.wait().expect("rustc probe").success()
79+
}

src/array_string.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::borrow::Borrow;
22
use std::cmp;
33
use std::fmt;
44
use std::hash::{Hash, Hasher};
5+
use std::mem;
56
use std::ptr;
67
use std::ops::{Deref, DerefMut};
78
use std::str;
@@ -25,6 +26,7 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer};
2526
/// if needed.
2627
#[derive(Copy)]
2728
pub struct ArrayString<A: Array<Item=u8>> {
29+
// FIXME: Use Copyable union for xs when we can
2830
xs: A,
2931
len: A::Index,
3032
}
@@ -52,7 +54,8 @@ impl<A: Array<Item=u8>> ArrayString<A> {
5254
pub fn new() -> ArrayString<A> {
5355
unsafe {
5456
ArrayString {
55-
xs: ::new_array(),
57+
// FIXME: Use Copyable union for xs when we can
58+
xs: mem::zeroed(),
5659
len: Index::from(0),
5760
}
5861
}

src/lib.rs

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@
77
//! - Optional, enabled by default
88
//! - Use libstd; disable to use `no_std` instead.
99
//!
10-
//! - `use_union`
11-
//! - Optional
12-
//! - Requires Rust nightly channel
13-
//! - Experimental: This flag uses nightly so it *may break* unexpectedly
14-
//! at some point; since it doesn't change API this flag may also change
15-
//! to do nothing in the future.
16-
//! - Use the unstable feature untagged unions for the internal implementation,
17-
//! which may have reduced space overhead
1810
//! - `serde-1`
1911
//! - Optional
2012
//! - Enable serialization for ArrayVec and ArrayString using serde 1.0
@@ -28,13 +20,17 @@
2820
//!
2921
#![doc(html_root_url="https://docs.rs/arrayvec/0.4/")]
3022
#![cfg_attr(not(feature="std"), no_std)]
31-
extern crate nodrop;
23+
#![cfg_attr(has_union_feature, feature(untagged_unions))]
24+
3225
#[cfg(feature="serde-1")]
3326
extern crate serde;
3427

3528
#[cfg(not(feature="std"))]
3629
extern crate core as std;
3730

31+
#[cfg(not(has_manually_drop_in_union))]
32+
extern crate nodrop;
33+
3834
use std::cmp;
3935
use std::iter;
4036
use std::mem;
@@ -53,11 +49,14 @@ use std::fmt;
5349
#[cfg(feature="std")]
5450
use std::io;
5551

56-
#[cfg(not(feature="use_union"))]
57-
use nodrop::NoDrop;
5852

59-
#[cfg(feature="use_union")]
60-
use std::mem::ManuallyDrop as NoDrop;
53+
#[cfg(has_manually_drop_in_union)]
54+
mod maybe_uninit;
55+
#[cfg(not(has_manually_drop_in_union))]
56+
#[path="maybe_uninit_nodrop.rs"]
57+
mod maybe_uninit;
58+
59+
use maybe_uninit::MaybeUninit;
6160

6261
#[cfg(feature="serde-1")]
6362
use serde::{Serialize, Deserialize, Serializer, Deserializer};
@@ -75,14 +74,6 @@ pub use array_string::ArrayString;
7574
pub use errors::CapacityError;
7675

7776

78-
unsafe fn new_array<A: Array>() -> A {
79-
// Note: Returning an uninitialized value here only works
80-
// if we can be sure the data is never used. The nullable pointer
81-
// inside enum optimization conflicts with this this for example,
82-
// so we need to be extra careful. See `NoDrop` enum.
83-
mem::uninitialized()
84-
}
85-
8677
/// A vector with a fixed capacity.
8778
///
8879
/// The `ArrayVec` is a vector backed by a fixed size array. It keeps track of
@@ -96,7 +87,7 @@ unsafe fn new_array<A: Array>() -> A {
9687
///
9788
/// ArrayVec can be converted into a by value iterator.
9889
pub struct ArrayVec<A: Array> {
99-
xs: NoDrop<A>,
90+
xs: MaybeUninit<A>,
10091
len: A::Index,
10192
}
10293

@@ -133,7 +124,7 @@ impl<A: Array> ArrayVec<A> {
133124
/// ```
134125
pub fn new() -> ArrayVec<A> {
135126
unsafe {
136-
ArrayVec { xs: NoDrop::new(new_array()), len: Index::from(0) }
127+
ArrayVec { xs: MaybeUninit::uninitialized(), len: Index::from(0) }
137128
}
138129
}
139130

@@ -565,7 +556,7 @@ impl<A: Array> ArrayVec<A> {
565556
let other_len = other.len();
566557

567558
unsafe {
568-
let dst = self.xs.as_mut_ptr().offset(self_len as isize);
559+
let dst = self.xs.ptr_mut().offset(self_len as isize);
569560
ptr::copy_nonoverlapping(other.as_ptr(), dst, other_len);
570561
self.set_len(self_len + other_len);
571562
}
@@ -631,7 +622,7 @@ impl<A: Array> ArrayVec<A> {
631622
Err(self)
632623
} else {
633624
unsafe {
634-
let array = ptr::read(&*self.xs);
625+
let array = ptr::read(self.xs.ptr() as *const A);
635626
mem::forget(self);
636627
Ok(array)
637628
}
@@ -660,7 +651,7 @@ impl<A: Array> Deref for ArrayVec<A> {
660651
#[inline]
661652
fn deref(&self) -> &[A::Item] {
662653
unsafe {
663-
slice::from_raw_parts(self.xs.as_ptr(), self.len())
654+
slice::from_raw_parts(self.xs.ptr(), self.len())
664655
}
665656
}
666657
}
@@ -670,7 +661,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
670661
fn deref_mut(&mut self) -> &mut [A::Item] {
671662
let len = self.len();
672663
unsafe {
673-
slice::from_raw_parts_mut(self.xs.as_mut_ptr(), len)
664+
slice::from_raw_parts_mut(self.xs.ptr_mut(), len)
674665
}
675666
}
676667
}
@@ -686,7 +677,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
686677
/// ```
687678
impl<A: Array> From<A> for ArrayVec<A> {
688679
fn from(array: A) -> Self {
689-
ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) }
680+
ArrayVec { xs: MaybeUninit::from(array), len: Index::from(A::capacity()) }
690681
}
691682
}
692683

src/maybe_uninit.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
3+
use array::Array;
4+
use std::mem::ManuallyDrop;
5+
6+
/// A combination of ManuallyDrop and “maybe uninitialized”;
7+
/// this wraps a value that can be wholly or partially uninitialized;
8+
/// it also has no drop regardless of the type of T.
9+
#[derive(Copy)]
10+
pub union MaybeUninit<T> {
11+
empty: (),
12+
value: ManuallyDrop<T>,
13+
}
14+
// Why we don't use std's MaybeUninit on nightly? See the ptr method
15+
16+
impl<T> Clone for MaybeUninit<T> where T: Copy
17+
{
18+
fn clone(&self) -> Self { *self }
19+
}
20+
21+
impl<T> MaybeUninit<T> {
22+
/// Create a new MaybeUninit with uninitialized interior
23+
pub unsafe fn uninitialized() -> Self {
24+
MaybeUninit { empty: () }
25+
}
26+
27+
/// Create a new MaybeUninit from the value `v`.
28+
pub fn from(v: T) -> Self {
29+
MaybeUninit { value: ManuallyDrop::new(v) }
30+
}
31+
32+
// Raw pointer casts written so that we don't reference or access the
33+
// uninitialized interior value
34+
35+
/// Return a raw pointer to the start of the interior array
36+
pub fn ptr(&self) -> *const T::Item
37+
where T: Array
38+
{
39+
// std MaybeUninit creates a &self.value reference here which is
40+
// not guaranteed to be sound in our case - we will partially
41+
// initialize the value, not always wholly.
42+
self as *const _ as *const T::Item
43+
}
44+
45+
/// Return a mut raw pointer to the start of the interior array
46+
pub fn ptr_mut(&mut self) -> *mut T::Item
47+
where T: Array
48+
{
49+
self as *mut _ as *mut T::Item
50+
}
51+
}

0 commit comments

Comments
 (0)