From cf273fec1f33687e02beb484a21db3807c2681da Mon Sep 17 00:00:00 2001 From: root Date: Thu, 30 Jul 2015 16:11:08 +0200 Subject: [PATCH] Use a non-dropping enum directly Don't use NoDrop as a separate abstraction: Then we have two drop flags, one for ArrayVec and one for NoDrop. Instead import the logic from NoDrop. The result is a much smaller ArrayVec value. --- src/lib.rs | 50 +++++++++++++++++++++++++++++++++++++++++++------- tests/tests.rs | 8 ++++---- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c6e54ca..9cb6dc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,4 @@ extern crate odds; -extern crate nodrop; - -use nodrop::NoDrop; use std::iter; use std::mem; @@ -17,6 +14,8 @@ use std::borrow::{Borrow, BorrowMut}; use std::hash::{Hash, Hasher}; use std::fmt; +use odds::debug_assert_unreachable; + mod array; pub use array::Array; pub use odds::IndexRange as RangeArgument; @@ -27,10 +26,17 @@ 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 `Flag` enum. + // so we need to be extra careful. See `NoDrop` enum. mem::uninitialized() } +/// repr(u8) - Make sure the non-nullable pointer optimization does not occur! +#[repr(u8)] +enum NoDrop { + Alive(T), + Dropped, +} + /// A vector with a fixed capacity. /// /// The **ArrayVec** is a vector backed by a fixed size array. It keeps track of @@ -50,8 +56,13 @@ pub struct ArrayVec { impl Drop for ArrayVec { fn drop(&mut self) { - // clear all elements, then NoDrop inhibits drop of inner array + // clear all elements while let Some(_) = self.pop() { } + + // inhibit drop + unsafe { + ptr::write(&mut self.xs, NoDrop::Dropped); + } } } @@ -73,7 +84,7 @@ impl ArrayVec { /// ``` pub fn new() -> ArrayVec { unsafe { - ArrayVec { xs: NoDrop::new(new_array()), len: Index::zero() } + ArrayVec { xs: NoDrop::Alive(new_array()), len: Index::zero() } } } @@ -364,6 +375,31 @@ impl DerefMut for ArrayVec { } } +impl Deref for NoDrop { + type Target = T; + + // Use type invariant, always Alive. + #[inline] + fn deref(&self) -> &T { + match *self { + NoDrop::Alive(ref inner) => inner, + _ => unsafe { debug_assert_unreachable() } + } + } +} + +impl DerefMut for NoDrop { + // Use type invariant, always Alive. + #[inline] + fn deref_mut(&mut self) -> &mut T { + match *self { + NoDrop::Alive(ref mut inner) => inner, + _ => unsafe { debug_assert_unreachable() } + } + } +} + + /// Create an **ArrayVec** from an array. /// /// ## Examples @@ -376,7 +412,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: NoDrop::Alive(array), len: Index::from(A::capacity()) } } } diff --git a/tests/tests.rs b/tests/tests.rs index c2cf272..60bbcde 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -127,15 +127,15 @@ fn test_is_send_sync() { #[test] fn test_compact_size() { // Future rust will kill these drop flags! - // 4 elements size + 1 len + 1 enum tag + [1 drop flag] + [1 drop flag nodrop] + // 4 elements size + 1 len + 1 enum tag + [1 drop flag] type ByteArray = ArrayVec<[u8; 4]>; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= 8); + assert!(mem::size_of::() <= 7); - // 12 element size + 1 len + 1 drop flag + 2 padding + 1 enum tag + 3 padding + // 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding type QuadArray = ArrayVec<[u32; 3]>; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= 24); + assert!(mem::size_of::() <= 20); } #[test]