Merge pull request #36 from bluss/union
nodrop: Add nightly feature use_union
This commit is contained in:
@@ -13,6 +13,9 @@ matrix:
|
|||||||
- rust: nightly
|
- rust: nightly
|
||||||
env:
|
env:
|
||||||
- NODROP_FEATURES='use_needs_drop'
|
- NODROP_FEATURES='use_needs_drop'
|
||||||
|
- rust: nightly
|
||||||
|
env:
|
||||||
|
- NODROP_FEATURES='use_union'
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "nodrop-union"
|
||||||
|
version = "0.1.8"
|
||||||
|
authors = ["bluss"]
|
||||||
|
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
|
description = "A wrapper type to inhibit drop (destructor). Implementation crate for nodrop, the untagged unions implementation (which is unstable / requires nightly) as of this writing."
|
||||||
|
documentation = "http://bluss.github.io/arrayvec/doc/nodrop"
|
||||||
|
repository = "https://github.com/bluss/arrayvec"
|
||||||
|
|
||||||
|
keywords = ["container", "drop", "no_std"]
|
||||||
|
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
//!
|
||||||
|
//! The **nodrop** crate has the following cargo feature flags:
|
||||||
|
//!
|
||||||
|
//! - `std`
|
||||||
|
//! - Optional, enabled by default
|
||||||
|
//! - Requires Rust 1.6 *to disable*
|
||||||
|
//! - Use libstd
|
||||||
|
//! - `use_needs_drop`
|
||||||
|
//! - Optional
|
||||||
|
//! - Requires nightly channel.
|
||||||
|
//! - Use `needs_drop` to skip overwriting if not necessary
|
||||||
|
//! - `use_union`
|
||||||
|
//! - Optional
|
||||||
|
//! - Requires nightly channel
|
||||||
|
//! - Using untagged union, finally we have an implementation of `NoDrop` without hacks,
|
||||||
|
//! for example the fact that `NoDrop<T>` never has a destructor anymore.
|
||||||
|
//!
|
||||||
|
|
||||||
|
#![feature(untagged_unions)]
|
||||||
|
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
#[cfg(not(test))]
|
||||||
|
extern crate core as std;
|
||||||
|
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
#[allow(unions_with_drop_fields)]
|
||||||
|
union UnionFlag<T> {
|
||||||
|
value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NoDrop<T>(UnionFlag<T>);
|
||||||
|
|
||||||
|
impl<T> NoDrop<T> {
|
||||||
|
/// Create a new **NoDrop**.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(value: T) -> Self {
|
||||||
|
NoDrop(UnionFlag { value: value })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the inner value.
|
||||||
|
///
|
||||||
|
/// Once extracted, the value can of course drop again.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
unsafe {
|
||||||
|
self.0.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for NoDrop<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
// Use type invariant, always initialized
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
unsafe {
|
||||||
|
&self.0.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for NoDrop<T> {
|
||||||
|
// Use type invariant, always initialized
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
unsafe {
|
||||||
|
&mut self.0.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::NoDrop;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_drop() {
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
let flag = &Cell::new(0);
|
||||||
|
|
||||||
|
struct Bump<'a>(&'a Cell<i32>);
|
||||||
|
|
||||||
|
impl<'a> Drop for Bump<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let n = self.0.get();
|
||||||
|
self.0.set(n + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ = NoDrop::new([Bump(flag), Bump(flag)]);
|
||||||
|
}
|
||||||
|
assert_eq!(flag.get(), 0);
|
||||||
|
|
||||||
|
// test something with the nullable pointer optimization
|
||||||
|
flag.set(0);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut array = NoDrop::new(Vec::new());
|
||||||
|
array.push(vec![Bump(flag)]);
|
||||||
|
array.push(vec![Bump(flag), Bump(flag)]);
|
||||||
|
array.push(vec![]);
|
||||||
|
array.push(vec![Bump(flag)]);
|
||||||
|
drop(array.pop());
|
||||||
|
assert_eq!(flag.get(), 1);
|
||||||
|
drop(array.pop());
|
||||||
|
assert_eq!(flag.get(), 1);
|
||||||
|
drop(array.pop());
|
||||||
|
assert_eq!(flag.get(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last one didn't drop.
|
||||||
|
assert_eq!(flag.get(), 3);
|
||||||
|
|
||||||
|
flag.set(0);
|
||||||
|
{
|
||||||
|
let array = NoDrop::new(Bump(flag));
|
||||||
|
array.into_inner();
|
||||||
|
assert_eq!(flag.get(), 1);
|
||||||
|
}
|
||||||
|
assert_eq!(flag.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size_of() {
|
||||||
|
assert!(mem::size_of::<NoDrop<&i32>>() == mem::size_of::<&i32>());
|
||||||
|
assert!(mem::size_of::<NoDrop<Vec<i32>>>() == mem::size_of::<Vec<i32>>());
|
||||||
|
// No non-nullable pointer optimization!
|
||||||
|
assert!(mem::size_of::<Option<NoDrop<&i32>>>() > mem::size_of::<NoDrop<&i32>>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,13 @@ std = ["odds/std"]
|
|||||||
# Use `needs_drop` to skip overwriting if not necessary
|
# Use `needs_drop` to skip overwriting if not necessary
|
||||||
use_needs_drop = []
|
use_needs_drop = []
|
||||||
|
|
||||||
|
# Optional, nightly channel
|
||||||
|
use_union = ["nodrop-union"]
|
||||||
|
|
||||||
[dependencies.odds]
|
[dependencies.odds]
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.nodrop-union]
|
||||||
|
path = "../nodrop-union"
|
||||||
|
optional = true
|
||||||
|
|||||||
+129
-109
@@ -9,6 +9,11 @@
|
|||||||
//! - Optional
|
//! - Optional
|
||||||
//! - Requires nightly channel.
|
//! - Requires nightly channel.
|
||||||
//! - Use `needs_drop` to skip overwriting if not necessary
|
//! - Use `needs_drop` to skip overwriting if not necessary
|
||||||
|
//! - `use_union`
|
||||||
|
//! - Optional
|
||||||
|
//! - Requires nightly channel
|
||||||
|
//! - Using untagged union, finally we have an implementation of `NoDrop` without hacks,
|
||||||
|
//! for example the fact that `NoDrop<T>` never has a destructor anymore.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#![cfg_attr(feature="use_needs_drop", feature(core_intrinsics))]
|
#![cfg_attr(feature="use_needs_drop", feature(core_intrinsics))]
|
||||||
@@ -17,150 +22,165 @@
|
|||||||
#[cfg(not(any(test, feature="std")))]
|
#[cfg(not(any(test, feature="std")))]
|
||||||
extern crate core as std;
|
extern crate core as std;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use_union"))]
|
||||||
extern crate odds;
|
extern crate odds;
|
||||||
|
|
||||||
use odds::debug_assert_unreachable;
|
#[cfg(feature = "use_union")]
|
||||||
|
extern crate nodrop_union as imp;
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
pub use imp::NoDrop;
|
||||||
use std::ptr;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
/// repr(u8) - Make sure the non-nullable pointer optimization does not occur!
|
|
||||||
#[repr(u8)]
|
|
||||||
enum Flag<T> {
|
|
||||||
Alive(T),
|
|
||||||
Dropped,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type holding **T** that will not call its destructor on drop
|
#[cfg(not(feature = "use_union"))]
|
||||||
pub struct NoDrop<T>(Flag<T>);
|
mod imp {
|
||||||
|
use std;
|
||||||
|
use odds::debug_assert_unreachable;
|
||||||
|
use std::ptr;
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
impl<T> NoDrop<T> {
|
/// repr(u8) - Make sure the non-nullable pointer optimization does not occur!
|
||||||
/// Create a new **NoDrop**.
|
#[repr(u8)]
|
||||||
|
enum Flag<T> {
|
||||||
|
Alive(T),
|
||||||
|
Dropped,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A type holding **T** that will not call its destructor on drop
|
||||||
|
pub struct NoDrop<T>(Flag<T>);
|
||||||
|
|
||||||
|
impl<T> NoDrop<T> {
|
||||||
|
/// Create a new **NoDrop**.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(value: T) -> NoDrop<T> {
|
||||||
|
NoDrop(Flag::Alive(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the inner value.
|
||||||
|
///
|
||||||
|
/// Once extracted, the value can of course drop again.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_inner(mut self) -> T {
|
||||||
|
let inner = unsafe {
|
||||||
|
ptr::read(&mut *self)
|
||||||
|
};
|
||||||
|
// skip Drop, so we don't even have to overwrite
|
||||||
|
mem::forget(self);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use_needs_drop"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(value: T) -> NoDrop<T> {
|
fn needs_drop<T>() -> bool {
|
||||||
NoDrop(Flag::Alive(value))
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the inner value.
|
#[cfg(feature = "use_needs_drop")]
|
||||||
///
|
|
||||||
/// Once extracted, the value can of course drop again.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_inner(mut self) -> T {
|
fn needs_drop<T>() -> bool {
|
||||||
let inner = unsafe {
|
unsafe {
|
||||||
ptr::read(&mut *self)
|
std::intrinsics::needs_drop::<T>()
|
||||||
};
|
}
|
||||||
// skip Drop, so we don't even have to overwrite
|
|
||||||
mem::forget(self);
|
|
||||||
inner
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "use_needs_drop"))]
|
impl<T> Drop for NoDrop<T> {
|
||||||
#[inline]
|
fn drop(&mut self) {
|
||||||
fn needs_drop<T>() -> bool {
|
if needs_drop::<T>() {
|
||||||
true
|
// inhibit drop
|
||||||
}
|
unsafe {
|
||||||
|
ptr::write(&mut self.0, Flag::Dropped);
|
||||||
#[cfg(feature = "use_needs_drop")]
|
}
|
||||||
#[inline]
|
|
||||||
fn needs_drop<T>() -> bool {
|
|
||||||
unsafe {
|
|
||||||
std::intrinsics::needs_drop::<T>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Drop for NoDrop<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if needs_drop::<T>() {
|
|
||||||
// inhibit drop
|
|
||||||
unsafe {
|
|
||||||
ptr::write(&mut self.0, Flag::Dropped);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for NoDrop<T> {
|
impl<T> Deref for NoDrop<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
// Use type invariant, always Flag::Alive.
|
// Use type invariant, always Flag::Alive.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref(&self) -> &T {
|
fn deref(&self) -> &T {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Flag::Alive(ref inner) => inner,
|
Flag::Alive(ref inner) => inner,
|
||||||
_ => unsafe { debug_assert_unreachable() }
|
_ => unsafe { debug_assert_unreachable() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> DerefMut for NoDrop<T> {
|
impl<T> DerefMut for NoDrop<T> {
|
||||||
// Use type invariant, always Flag::Alive.
|
// Use type invariant, always Flag::Alive.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Flag::Alive(ref mut inner) => inner,
|
Flag::Alive(ref mut inner) => inner,
|
||||||
_ => unsafe { debug_assert_unreachable() }
|
_ => unsafe { debug_assert_unreachable() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[test]
|
||||||
|
fn test_no_nonnullable_opt() {
|
||||||
|
// Make sure `Flag` does not apply the non-nullable pointer optimization
|
||||||
|
// as Option would do.
|
||||||
|
assert!(mem::size_of::<Flag<&i32>>() > mem::size_of::<&i32>());
|
||||||
|
assert!(mem::size_of::<Flag<Vec<i32>>>() > mem::size_of::<Vec<i32>>());
|
||||||
|
assert!(mem::size_of::<Option<Flag<&i32>>>() > mem::size_of::<Flag<&i32>>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[test]
|
mod tests {
|
||||||
fn test_no_nonnullable_opt() {
|
use super::NoDrop;
|
||||||
// Make sure `Flag` does not apply the non-nullable pointer optimization
|
|
||||||
// as Option would do.
|
|
||||||
assert!(mem::size_of::<Flag<&i32>>() > mem::size_of::<&i32>());
|
|
||||||
assert!(mem::size_of::<Flag<Vec<i32>>>() > mem::size_of::<Vec<i32>>());
|
|
||||||
assert!(mem::size_of::<Option<Flag<&i32>>>() > mem::size_of::<Flag<&i32>>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[test]
|
||||||
#[test]
|
fn test_drop() {
|
||||||
fn test_drop() {
|
use std::cell::Cell;
|
||||||
use std::cell::Cell;
|
|
||||||
|
|
||||||
let flag = &Cell::new(0);
|
let flag = &Cell::new(0);
|
||||||
|
|
||||||
struct Bump<'a>(&'a Cell<i32>);
|
struct Bump<'a>(&'a Cell<i32>);
|
||||||
|
|
||||||
impl<'a> Drop for Bump<'a> {
|
impl<'a> Drop for Bump<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let n = self.0.get();
|
let n = self.0.get();
|
||||||
self.0.set(n + 1);
|
self.0.set(n + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let _ = NoDrop::new([Bump(flag), Bump(flag)]);
|
let _ = NoDrop::new([Bump(flag), Bump(flag)]);
|
||||||
}
|
}
|
||||||
assert_eq!(flag.get(), 0);
|
assert_eq!(flag.get(), 0);
|
||||||
|
|
||||||
// test something with the nullable pointer optimization
|
// test something with the nullable pointer optimization
|
||||||
flag.set(0);
|
flag.set(0);
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut array = NoDrop::new(Vec::new());
|
let mut array = NoDrop::new(Vec::new());
|
||||||
array.push(vec![Bump(flag)]);
|
array.push(vec![Bump(flag)]);
|
||||||
array.push(vec![Bump(flag), Bump(flag)]);
|
array.push(vec![Bump(flag), Bump(flag)]);
|
||||||
array.push(vec![]);
|
array.push(vec![]);
|
||||||
array.push(vec![Bump(flag)]);
|
array.push(vec![Bump(flag)]);
|
||||||
drop(array.pop());
|
drop(array.pop());
|
||||||
assert_eq!(flag.get(), 1);
|
assert_eq!(flag.get(), 1);
|
||||||
drop(array.pop());
|
drop(array.pop());
|
||||||
assert_eq!(flag.get(), 1);
|
assert_eq!(flag.get(), 1);
|
||||||
drop(array.pop());
|
drop(array.pop());
|
||||||
|
assert_eq!(flag.get(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last one didn't drop.
|
||||||
assert_eq!(flag.get(), 3);
|
assert_eq!(flag.get(), 3);
|
||||||
}
|
|
||||||
|
|
||||||
// last one didn't drop.
|
flag.set(0);
|
||||||
assert_eq!(flag.get(), 3);
|
{
|
||||||
|
let array = NoDrop::new(Bump(flag));
|
||||||
flag.set(0);
|
array.into_inner();
|
||||||
{
|
assert_eq!(flag.get(), 1);
|
||||||
let array = NoDrop::new(Bump(flag));
|
}
|
||||||
array.into_inner();
|
|
||||||
assert_eq!(flag.get(), 1);
|
assert_eq!(flag.get(), 1);
|
||||||
}
|
}
|
||||||
assert_eq!(flag.get(), 1);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user