Skip to content

Commit 3edc9a6

Browse files
authored
Merge pull request #114 from bluss/maybe-uninit-0.4
Implement a "MaybeUninit" and use it conditionally (0.4.x version)
2 parents cc3cb8d + f0ec3e1 commit 3edc9a6

File tree

8 files changed

+217
-30
lines changed

8 files changed

+217
-30
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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer};
2626
/// if needed.
2727
#[derive(Copy)]
2828
pub struct ArrayString<A: Array<Item=u8>> {
29+
// FIXME: Use Copyable union for xs when we can
2930
xs: A,
3031
len: A::Index,
3132
}
@@ -53,7 +54,8 @@ impl<A: Array<Item=u8>> ArrayString<A> {
5354
pub fn new() -> ArrayString<A> {
5455
unsafe {
5556
ArrayString {
56-
xs: ::new_array(),
57+
// FIXME: Use Copyable union for xs when we can
58+
xs: mem::zeroed(),
5759
len: Index::from(0),
5860
}
5961
}

src/lib.rs

Lines changed: 18 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

@@ -517,7 +508,6 @@ impl<A: Array> ArrayVec<A> {
517508
self.len = Index::from(length);
518509
}
519510

520-
521511
/// Create a draining iterator that removes the specified range in the vector
522512
/// and yields the removed items from start to end. The element range is
523513
/// removed even if the iterator is not consumed until the end.
@@ -577,7 +567,7 @@ impl<A: Array> ArrayVec<A> {
577567
Err(self)
578568
} else {
579569
unsafe {
580-
let array = ptr::read(&*self.xs);
570+
let array = ptr::read(self.xs.ptr() as *const A);
581571
mem::forget(self);
582572
Ok(array)
583573
}
@@ -606,7 +596,7 @@ impl<A: Array> Deref for ArrayVec<A> {
606596
#[inline]
607597
fn deref(&self) -> &[A::Item] {
608598
unsafe {
609-
slice::from_raw_parts(self.xs.as_ptr(), self.len())
599+
slice::from_raw_parts(self.xs.ptr(), self.len())
610600
}
611601
}
612602
}
@@ -616,7 +606,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
616606
fn deref_mut(&mut self) -> &mut [A::Item] {
617607
let len = self.len();
618608
unsafe {
619-
slice::from_raw_parts_mut(self.xs.as_mut_ptr(), len)
609+
slice::from_raw_parts_mut(self.xs.ptr_mut(), len)
620610
}
621611
}
622612
}
@@ -632,7 +622,7 @@ impl<A: Array> DerefMut for ArrayVec<A> {
632622
/// ```
633623
impl<A: Array> From<A> for ArrayVec<A> {
634624
fn from(array: A) -> Self {
635-
ArrayVec { xs: NoDrop::new(array), len: Index::from(A::capacity()) }
625+
ArrayVec { xs: MaybeUninit::from(array), len: Index::from(A::capacity()) }
636626
}
637627
}
638628

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+
}

src/maybe_uninit_nodrop.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
use array::Array;
3+
use nodrop::NoDrop;
4+
use std::mem::uninitialized;
5+
6+
/// A combination of NoDrop and “maybe uninitialized”;
7+
/// this wraps a value that can be wholly or partially uninitialized.
8+
///
9+
/// NOTE: This is known to not be a good solution, but it's the one we have kept
10+
/// working on stable Rust. Stable improvements are encouraged, in any form,
11+
/// but of course we are waiting for a real, stable, MaybeUninit.
12+
pub struct MaybeUninit<T>(NoDrop<T>);
13+
// why don't we use ManuallyDrop here: It doesn't inhibit
14+
// enum layout optimizations that depend on T, and we support older Rust.
15+
16+
impl<T> MaybeUninit<T> {
17+
/// Create a new MaybeUninit with uninitialized interior
18+
pub unsafe fn uninitialized() -> Self {
19+
Self::from(uninitialized())
20+
}
21+
22+
/// Create a new MaybeUninit from the value `v`.
23+
pub fn from(v: T) -> Self {
24+
MaybeUninit(NoDrop::new(v))
25+
}
26+
27+
/// Return a raw pointer to the start of the interior array
28+
pub fn ptr(&self) -> *const T::Item
29+
where T: Array
30+
{
31+
&*self.0 as *const T as *const _
32+
}
33+
34+
/// Return a mut raw pointer to the start of the interior array
35+
pub fn ptr_mut(&mut self) -> *mut T::Item
36+
where T: Array
37+
{
38+
&mut *self.0 as *mut T as *mut _
39+
}
40+
}
41+

tests/tests.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ fn test_compact_size() {
164164
assert!(mem::size_of::<QuadArray>() <= 24);
165165
}
166166

167+
#[test]
168+
fn test_still_works_with_option_arrayvec() {
169+
type RefArray = ArrayVec<[&'static i32; 2]>;
170+
let array = Some(RefArray::new());
171+
assert!(array.is_some());
172+
println!("{:?}", array);
173+
}
174+
167175
#[test]
168176
fn test_drain() {
169177
let mut v = ArrayVec::from([0; 8]);
@@ -500,3 +508,12 @@ fn test_sizes_129_255() {
500508
ArrayVec::from([0u8; 255]);
501509
}
502510

511+
512+
#[test]
513+
fn test_nightly_uses_maybe_uninit() {
514+
if option_env!("ARRAYVECTEST_ENSURE_UNION").map(|s| !s.is_empty()).unwrap_or(false) {
515+
assert!(cfg!(has_manually_drop_in_union));
516+
type ByteArray = ArrayVec<[u8; 4]>;
517+
assert!(mem::size_of::<ByteArray>() == 5);
518+
}
519+
}

0 commit comments

Comments
 (0)