#![deny(missing_docs)]
pub mod ext;
use std::borrow::Borrow;
use std::cmp;
use std::convert::AsRef;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use std::rc::Rc;
use std::sync::Arc;
use self::ext::*;
#[macro_export]
macro_rules! supercow_features {
($(#[$meta:meta])* pub trait $feature_name:ident: $($stuff:tt)*) => {
supercow_features!(@_ACCUM $(#[$meta])* pub trait $feature_name:
[] [] [] $($stuff)*);
};
(@_ACCUM $(#[$meta:meta])* pub trait $feature_name:ident:
$clone:tt $twostep:tt [$($others:tt),*] Clone $($more:tt)*) => {
supercow_features!(@_ACCUM $(#[$meta])* pub trait $feature_name:
[Clone clone_boxed] $twostep [$($others)*]
$($more)*);
};
(@_ACCUM $(#[$meta:meta])* pub trait $feature_name:ident:
$clone:tt $twostep:tt [$($others:tt),*]
TwoStepShared($($inner:tt)*)
$($more:tt)*) => {
supercow_features!(@_ACCUM $(#[$meta])* pub trait $feature_name:
$clone [$($inner)*] [$($others)*]
$($more)*);
};
(@_ACCUM $(#[$meta:meta])* pub trait $feature_name:ident:
$clone:tt $twostep:tt [$($others:tt),*], $($more:tt)*) => {
supercow_features!(@_ACCUM $(#[$meta])* pub trait $feature_name:
$clone $twostep [$($others)*] $($more)*);
};
(@_ACCUM $(#[$meta:meta])* pub trait $feature_name:ident:
$clone:tt $twostep:tt [$($others:ident),*] $other:ident $($more:tt)*) => {
supercow_features!(@_ACCUM $(#[$meta])* pub trait $feature_name:
$clone $twostep [$($others, )* $other]
$($more)*);
};
(@_ACCUM $(#[$meta:meta])* pub trait $feature_name:ident:
$clone:tt $twostep:tt [$($others:ident),*]) => {
supercow_features!(@_DEFINE $(#[$meta])* pub trait $feature_name:
$clone $twostep [$($others),*]);
};
(@_DEFINE $(#[$meta:meta])*
pub trait $feature_name:ident:
[$($clone:ident $clone_boxed:ident)*]
[$($twostep_inner:ident)*]
[$($req:ident),*]) => {
$(#[$meta])*
pub trait $feature_name<'a>: $($req +)* 'a {
$(
fn $clone_boxed(&self) -> Box<$feature_name<'a> + 'a>;
)*
fn self_address_mut(&mut self) -> *mut ();
}
impl<'a, T : 'a + $($req +)* $($clone +)* Sized>
$feature_name<'a> for T {
$(
fn $clone_boxed(&self) -> Box<$feature_name<'a> + 'a> {
let cloned: T = self.clone();
Box::new(cloned)
}
)*
fn self_address_mut(&mut self) -> *mut () {
self as *mut Self as *mut ()
}
}
unsafe impl<'a, T : $feature_name<'a>> $crate::ext::SharedFrom<T>
for Box<$feature_name<'a> + 'a> {
fn shared_from(t: T) -> Self {
Box::new(t)
}
}
$(
impl<'a> $clone for Box<$feature_name<'a> + 'a> {
fn clone(&self) -> Self {
$feature_name::clone_boxed(&**self)
}
}
)*
$(
impl<'a, S : 'a + ?Sized, T : 'a> $crate::ext::TwoStepShared<T, S>
for Box<$feature_name<'a> + 'a>
where T : $crate::ext::SafeBorrow<S>,
$twostep_inner<T,S> : $feature_name<'a> {
fn new_two_step() -> Self {
Box::new(
<$twostep_inner<T,S> as $crate::ext::TwoStepShared<T, S>>::
new_two_step())
}
unsafe fn deref_holder(&mut self) -> &mut Option<T> {
<$twostep_inner<T,S> as $crate::ext::TwoStepShared<T, S>>::
deref_holder(
&mut* ($feature_name::self_address_mut(&mut **self)
as *mut $twostep_inner<T,S>))
}
}
)*
};
}
supercow_features!(
pub trait DefaultFeatures: Clone, TwoStepShared(TwoStepArc), Send, Sync);
supercow_features!(
pub trait NonSyncFeatures: Clone, TwoStepShared(TwoStepRc));
pub type NonSyncSupercow<'a, OWNED, BORROWED = OWNED> =
Supercow<'a, OWNED, BORROWED,
Box<NonSyncFeatures<'static> + 'static>,
BoxedStorage>;
pub type InlineSupercow<'a, OWNED, BORROWED = OWNED,
SHARED = Box<DefaultFeatures<'static> + 'static>> =
Supercow<'a, OWNED, BORROWED, SHARED, InlineStorage<OWNED, SHARED>>;
pub type InlineNonSyncSupercow<'a, OWNED, BORROWED = OWNED> =
Supercow<'a, OWNED, BORROWED,
Box<NonSyncFeatures<'static> + 'static>,
InlineStorage<OWNED, Box<NonSyncFeatures<'static> + 'static>>>;
pub struct Supercow<'a, OWNED, BORROWED : ?Sized = OWNED,
SHARED = Box<DefaultFeatures<'static> + 'static>,
STORAGE = BoxedStorage, PTR = *const BORROWED>
where BORROWED : 'a,
*const BORROWED : PointerFirstRef,
STORAGE : OwnedStorage<OWNED, SHARED>,
PTR : PtrWrite<BORROWED> {
ptr: PTR,
mode: *mut (),
storage: STORAGE,
_owned: PhantomData<OWNED>,
_borrowed: PhantomData<&'a BORROWED>,
_shared: PhantomData<SHARED>,
}
pub type Phantomcow<'a, OWNED, BORROWED = OWNED,
SHARED = Box<DefaultFeatures<'static> + 'static>,
STORAGE = BoxedStorage> =
Supercow<'a, OWNED, BORROWED, SHARED, STORAGE, ()>;
pub type NonSyncPhantomcow<'a, OWNED, BORROWED = OWNED> =
Phantomcow<'a, OWNED, BORROWED, Box<NonSyncFeatures<'static> + 'static>,
BoxedStorage>;
pub type InlinePhantomcow<'a, OWNED, BORROWED = OWNED,
SHARED = Box<DefaultFeatures<'static> + 'static>> =
Phantomcow<'a, OWNED, BORROWED, SHARED, InlineStorage<OWNED, SHARED>>;
pub type InlineNonSyncPhantomcow<'a, OWNED, BORROWED = OWNED> =
Phantomcow<'a, OWNED, BORROWED, Box<NonSyncFeatures<'static> + 'static>,
InlineStorage<OWNED, Box<NonSyncFeatures<'static> + 'static>>>;
enum SupercowMode {
Owned(*mut ()),
Borrowed,
Shared(*mut ()),
}
impl SupercowMode {
fn from_ptr(mode: *mut ()) -> Self {
if mode.is_null() {
Borrowed
} else if mode.is_2_aligned() {
Owned(mode)
} else {
Shared(mode.align2())
}
}
}
use self::SupercowMode::*;
macro_rules! defimpl {
($(@$us:tt)* [$($tparm:ident $(: ?$tparmsized:ident)*),*] ($($spec:tt)*)
where { $($wo:tt)* } $body:tt) => {
$($us)* impl<'a, $($tparm $(: ?$tparmsized)*,)* OWNED,
BORROWED : ?Sized, SHARED, STORAGE, PTR>
$($spec)* Supercow<'a, OWNED, BORROWED, SHARED, STORAGE, PTR>
where BORROWED : 'a,
*const BORROWED : PointerFirstRef,
STORAGE : OwnedStorage<OWNED, SHARED>,
PTR : PtrWrite<BORROWED>,
$($wo)*
$body
}
}
defimpl! {[] (Drop for) where { } {
fn drop(&mut self) {
match self.mode() {
Owned(ptr) => unsafe { self.storage.deallocate_a(ptr) },
Shared(ptr) => unsafe { self.storage.deallocate_b(ptr) },
Borrowed => (),
}
}
} }
defimpl! {@unsafe [] (Send for) where {
OWNED : Send,
&'a BORROWED : Send,
SHARED : Send,
STORAGE : Send,
} { } }
defimpl! {@unsafe [] (Sync for) where {
OWNED : Sync,
&'a BORROWED : Sync,
SHARED : Sync,
STORAGE : Sync,
} { } }
defimpl! {[] () where { } {
pub fn owned(inner: OWNED) -> Self
where OWNED : SafeBorrow<BORROWED> {
let mut this = unsafe { Self::empty() };
this.mode = this.storage.allocate_a(inner);
unsafe { this.borrow_owned(); }
this
}
pub fn borrowed<T : Borrow<BORROWED> + ?Sized>(inner: &'a T) -> Self {
let mut this = unsafe { Self::empty() };
this.ptr.store_ptr(inner.borrow() as *const BORROWED);
this
}
pub fn shared<T>(inner: T) -> Self
where T : ConstDeref<Target = BORROWED>,
SHARED : SharedFrom<T> {
let mut ptr = PTR::new();
ptr.store_ptr(inner.const_deref());
Self::shared_nocvt(SHARED::shared_from(inner), ptr)
}
fn shared_nocvt(shared: SHARED, ptr: PTR) -> Self {
let mut this = unsafe { Self::empty() };
this.ptr = ptr;
this.mode = this.storage.allocate_b(shared).unalign2() as *mut ();
this
}
pub fn clone_non_owned(this: &Self) -> Option<Self>
where SHARED : Clone {
match this.mode() {
Owned(_) => None,
Borrowed => Some(Supercow {
ptr: this.ptr,
mode: this.mode,
storage: Default::default(),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
}),
Shared(s) => Some(Self::shared_nocvt(unsafe {
this.storage.get_ptr_b(s)
}.clone(), this.ptr)),
}
}
pub fn share(this: &mut Self) -> Self
where OWNED : SafeBorrow<BORROWED>,
SHARED : Clone + TwoStepShared<OWNED, BORROWED> {
match this.mode() {
Owned(ptr) => {
let unboxed = SHARED::new_two_step();
let mut new_storage: STORAGE = Default::default();
let shared_ptr = new_storage.allocate_b(unboxed);
let internal_ptr: *const BORROWED = {
let holder = unsafe {
new_storage.get_mut_b(shared_ptr)
.deref_holder()
};
let owned_base = unsafe {
this.storage.get_ptr_a(ptr)
}.address();
let owned_size = mem::size_of::<OWNED>();
let borrowed_ptr = unsafe {
this.storage.get_ptr_a(ptr)
}.borrow() as *const BORROWED;
*holder = Some(unsafe {
this.storage.deallocate_into_a(ptr)
});
if borrowed_ptr.within(owned_base, owned_size) {
let new_base = holder.as_ref().unwrap().address();
borrowed_ptr.rebase(owned_base, new_base)
} else {
borrowed_ptr
}
};
this.storage = new_storage;
this.mode = shared_ptr.unalign2() as *mut ();
this.ptr.store_ptr(internal_ptr);
Self::shared_nocvt(unsafe {
this.storage.get_ptr_b(shared_ptr)
}.clone(), this.ptr)
},
Borrowed => Supercow {
ptr: this.ptr,
mode: this.mode,
storage: Default::default(),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
},
Shared(s) => Self::shared_nocvt(unsafe {
this.storage.get_ptr_b(s)
}.clone(), this.ptr),
}
}
pub fn extract_ref(this: &Self) -> Option<&'a BORROWED>
where PTR : PtrRead<BORROWED> {
match this.mode() {
Borrowed => Some(unsafe { &*this.ptr.get_ptr() }),
_ => None,
}
}
pub fn into_inner(mut this: Self) -> OWNED
where OWNED : Borrow<BORROWED>,
BORROWED : ToOwned<Owned = OWNED>,
PTR : PtrRead<BORROWED> {
match this.mode() {
Owned(ptr) => {
unsafe { this.storage.deallocate_into_a(ptr) }
},
_ => (*this).to_owned(),
}
}
pub fn to_mut<'b>(&'b mut self) -> Ref<'b, Self>
where OWNED : SafeBorrow<BORROWED>,
BORROWED : ToOwned<Owned = OWNED>,
PTR : PtrRead<BORROWED>
{
match self.mode() {
Owned(_) => (),
_ => *self = Self::owned((*self).to_owned()),
}
let old_ptr = self.ptr.get_ptr();
self.ptr.store_ptr(OWNED::borrow_replacement(
unsafe { &*old_ptr }) as *const BORROWED);
Ref {
r: unsafe { self.storage.get_mut_a(self.mode) } as *mut OWNED,
parent: self,
}
}
pub fn unborrow(mut this: Self)
-> Supercow<'static, OWNED, BORROWED, SHARED, STORAGE, PTR>
where OWNED : SafeBorrow<BORROWED>,
BORROWED : ToOwned<Owned = OWNED>,
PTR : PtrRead<BORROWED> {
let new_storage = STORAGE::default();
match this.mode() {
Owned(_) | Shared(_) => Supercow {
ptr: this.ptr,
mode: mem::replace(&mut this.mode, ptr::null_mut()),
storage: mem::replace(&mut this.storage, new_storage),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
},
Borrowed => Supercow::owned((*this).to_owned()),
}
}
pub fn take_ownership<NS>
(mut this: Self) -> Supercow<'static, OWNED, BORROWED, NS, STORAGE, PTR>
where OWNED : SafeBorrow<BORROWED>,
BORROWED : ToOwned<Owned = OWNED>,
STORAGE : OwnedStorage<OWNED, NS>,
PTR : PtrRead<BORROWED> {
let new_storage = STORAGE::default();
match this.mode() {
Owned(_) => Supercow {
ptr: this.ptr,
mode: mem::replace(&mut this.mode, ptr::null_mut()),
storage: mem::replace(&mut this.storage, new_storage),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
},
_ => Supercow::owned((*this).to_owned()),
}
}
pub fn phantom(mut this: Self)
-> Phantomcow<'a, OWNED, BORROWED, SHARED, STORAGE> {
let new_storage = STORAGE::default();
let ret = Supercow {
ptr: (),
mode: mem::replace(&mut this.mode, ptr::null_mut()),
storage: mem::replace(&mut this.storage, new_storage),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
};
ret
}
unsafe fn borrow_owned(&mut self)
where OWNED : SafeBorrow<BORROWED> {
let mut borrowed_ptr = self.storage.get_ptr_a(self.mode).borrow()
as *const BORROWED;
debug_assert!(
0 == mem::size_of_val(&* borrowed_ptr) ||
borrowed_ptr.address() >= MAX_INTERNAL_BORROW_DISPLACEMENT,
"Supercow: Non-ZST allocated at {:p}, which is below the \
minimum supported allocation address of {}",
borrowed_ptr, MAX_INTERNAL_BORROW_DISPLACEMENT);
if STORAGE::is_internal_storage() {
let self_start = self.address();
let self_size = mem::size_of::<Self>();
if borrowed_ptr.within(self_start, self_size) {
debug_assert!(borrowed_ptr.address() - self_start <=
MAX_INTERNAL_BORROW_DISPLACEMENT * 3/2,
"Borrowed pointer displaced too far from \
base address (supercow at {:x}, self at {:x}, \
borrowed to {:x}", self_start,
(&self.storage).address(),
borrowed_ptr.address());
borrowed_ptr = borrowed_ptr.rebase(self_start, 0);
}
}
self.ptr.store_ptr(borrowed_ptr);
}
unsafe fn empty() -> Self {
Supercow {
ptr: PTR::new(),
mode: ptr::null_mut(),
storage: Default::default(),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
}
}
fn mode(&self) -> SupercowMode {
SupercowMode::from_ptr(self.mode)
}
} }
defimpl! {[] (RefParent for) where {
OWNED : SafeBorrow<BORROWED>
} {
type Owned = OWNED;
unsafe fn supercow_ref_drop(&mut self) {
self.borrow_owned()
}
} }
pub struct Ref<'a, P>
where P : RefParent + 'a {
r: *mut P::Owned,
parent: &'a mut P,
}
impl<'a, P> Deref for Ref<'a, P>
where P : RefParent + 'a {
type Target = P::Owned;
#[inline]
fn deref(&self) -> &P::Owned {
unsafe { &*self.r }
}
}
impl<'a, P> DerefMut for Ref<'a, P>
where P : RefParent + 'a {
#[inline]
fn deref_mut(&mut self) -> &mut P::Owned {
unsafe { &mut*self.r }
}
}
impl<'a, P> Drop for Ref<'a, P>
where P : RefParent + 'a {
#[inline]
fn drop(&mut self) {
unsafe { self.parent.supercow_ref_drop() }
}
}
defimpl! {[] (Deref for) where {
PTR : PtrRead<BORROWED>
} {
type Target = BORROWED;
#[inline]
fn deref(&self) -> &BORROWED {
let mut target_ref = self.ptr.get_ptr();
unsafe {
if STORAGE::is_internal_storage() &&
target_ref.within(0, MAX_INTERNAL_BORROW_DISPLACEMENT)
{
target_ref = target_ref.rebase(0, self.address());
}
&*target_ref
}
}
} }
defimpl! {[] (Borrow<BORROWED> for) where {
PTR : PtrRead<BORROWED>,
} {
fn borrow(&self) -> &BORROWED {
self.deref()
}
} }
defimpl! {[] (AsRef<BORROWED> for) where {
PTR : PtrRead<BORROWED>,
} {
fn as_ref(&self) -> &BORROWED {
self.deref()
}
} }
defimpl! {[] (Clone for) where {
OWNED : Clone + SafeBorrow<BORROWED>,
SHARED : Clone,
} {
fn clone(&self) -> Self {
match self.mode() {
Owned(ptr) => Self::owned(unsafe {
self.storage.get_ptr_a(ptr)
}.clone()),
Borrowed => Supercow {
ptr: self.ptr,
mode: self.mode,
storage: Default::default(),
_owned: PhantomData,
_borrowed: PhantomData,
_shared: PhantomData,
},
Shared(s) => Self::shared_nocvt(unsafe {
self.storage.get_ptr_b(s)
}.clone(), self.ptr),
}
}
} }
defimpl! {[] (From<OWNED> for) where {
OWNED : SafeBorrow<BORROWED>,
} {
fn from(inner: OWNED) -> Self {
Self::owned(inner)
}
} }
defimpl! {[] (From<&'a OWNED> for) where {
OWNED : Borrow<BORROWED>,
} {
fn from(inner: &'a OWNED) -> Self {
Self::borrowed(inner.borrow())
}
} }
impl<'a, OWNED, SHARED, STORAGE> From<Rc<OWNED>>
for Supercow<'a, OWNED, OWNED, SHARED, STORAGE>
where SHARED : SharedFrom<Rc<OWNED>>,
STORAGE : OwnedStorage<OWNED, SHARED>,
OWNED : 'a,
*const OWNED : PointerFirstRef {
fn from(rc: Rc<OWNED>) -> Self {
Self::shared(rc)
}
}
impl<'a, OWNED, SHARED, STORAGE> From<Arc<OWNED>>
for Supercow<'a, OWNED, OWNED, SHARED, STORAGE>
where SHARED : SharedFrom<Arc<OWNED>>,
STORAGE : OwnedStorage<OWNED, SHARED>,
OWNED : 'a,
*const OWNED : PointerFirstRef {
fn from(rc: Arc<OWNED>) -> Self {
Self::shared(rc)
}
}
macro_rules! deleg_fmt { ($tr:ident) => {
defimpl! {[] (fmt::$tr for) where {
BORROWED : fmt::$tr,
PTR : PtrRead<BORROWED>,
} {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt(f)
}
} }
} }
deleg_fmt!(Binary);
deleg_fmt!(Display);
deleg_fmt!(LowerExp);
deleg_fmt!(LowerHex);
deleg_fmt!(Octal);
deleg_fmt!(Pointer);
deleg_fmt!(UpperExp);
deleg_fmt!(UpperHex);
impl<'a, OWNED, BORROWED : ?Sized, SHARED, STORAGE>
fmt::Debug for Supercow<'a, OWNED, BORROWED, SHARED, STORAGE, ()>
where BORROWED : 'a,
*const BORROWED : PointerFirstRef,
STORAGE : OwnedStorage<OWNED, SHARED> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Phantomcow>")
}
}
impl<'a, OWNED, BORROWED : ?Sized, SHARED, STORAGE>
fmt::Debug for Supercow<'a, OWNED, BORROWED, SHARED, STORAGE, *const BORROWED>
where BORROWED : fmt::Debug + 'a,
*const BORROWED : PointerFirstRef,
STORAGE : OwnedStorage<OWNED, SHARED> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt(f)
}
}
defimpl! {[T] (cmp::PartialEq<T> for) where {
T : Borrow<BORROWED>,
BORROWED : PartialEq<BORROWED>,
PTR : PtrRead<BORROWED>,
} {
fn eq(&self, other: &T) -> bool {
**self == *other.borrow()
}
fn ne(&self, other: &T) -> bool {
**self != *other.borrow()
}
} }
defimpl! {[] (cmp::Eq for) where {
BORROWED : Eq,
PTR : PtrRead<BORROWED>,
} { } }
defimpl! {[T] (cmp::PartialOrd<T> for) where {
T : Borrow<BORROWED>,
BORROWED : cmp::PartialOrd<BORROWED>,
PTR : PtrRead<BORROWED>,
} {
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
(**self).partial_cmp(other.borrow())
}
fn lt(&self, other: &T) -> bool {
**self < *other.borrow()
}
fn le(&self, other: &T) -> bool {
**self <= *other.borrow()
}
fn gt(&self, other: &T) -> bool {
**self > *other.borrow()
}
fn ge(&self, other: &T) -> bool {
**self >= *other.borrow()
}
} }
defimpl! {[] (cmp::Ord for) where {
BORROWED : cmp::Ord,
PTR : PtrRead<BORROWED>,
} {
fn cmp(&self, other: &Self) -> cmp::Ordering {
(**self).cmp(other)
}
} }
defimpl! {[] (Hash for) where {
BORROWED : Hash,
PTR : PtrRead<BORROWED>,
} {
fn hash<H : Hasher>(&self, h: &mut H) {
(**self).hash(h)
}
} }
trait ReferenceExt {
fn address(&self) -> usize;
}
impl<'a, T : ?Sized + 'a> ReferenceExt for &'a T {
#[inline]
fn address(&self) -> usize {
(*self) as *const T as *const () as usize
}
}
impl<'a, T : ?Sized + 'a> ReferenceExt for &'a mut T {
#[inline]
fn address(&self) -> usize {
(*self) as *const T as *const () as usize
}
}
unsafe trait PfrExt : Copy {
#[inline]
fn address(self) -> usize {
let saddr: &usize = unsafe {
mem::transmute(&self)
};
*saddr
}
#[inline]
fn with_address(mut self, address: usize) -> Self {
let saddr: &mut usize = unsafe {
mem::transmute(&mut self)
};
*saddr = address;
let saddr: &mut Self = unsafe {
mem::transmute(saddr)
};
*saddr
}
#[inline]
fn within(self, base: usize, size: usize) -> bool {
let a = self.address();
a >= base && a < (base + size)
}
#[inline]
fn rebase(self, old_base: usize, new_base: usize) -> Self {
self.with_address(new_base + (self.address() - old_base))
}
#[inline]
fn is_2_aligned(self) -> bool {
0 == (self.address() & 1usize)
}
#[inline]
fn align2(self) -> Self {
self.with_address(self.address() & !1usize)
}
#[inline]
fn unalign2(self) -> Self {
self.with_address(self.address() | 1usize)
}
}
unsafe impl<T : PointerFirstRef> PfrExt for T { }
unsafe impl<T : ?Sized> PfrExt for *mut T { }
#[cfg(test)]
mod misc_tests {
use std::borrow::Cow;
use super::*;
#[inline(never)]
fn add_two_cow(a: &Cow<u32>, b: &Cow<u32>) -> u32 {
**a + **b
}
#[inline(never)]
fn add_two_supercow(a: &InlineSupercow<u32>,
b: &InlineSupercow<u32>) -> u32 {
**a + **b
}
#[test]
fn do_add_two() {
assert_eq!(42, add_two_cow(&Cow::Owned(40), &Cow::Owned(2)));
assert_eq!(44, add_two_cow(&Cow::Borrowed(&38), &Cow::Borrowed(&6)));
assert_eq!(42, add_two_supercow(&Supercow::owned(40),
&Supercow::owned(2)));
}
}
macro_rules! tests { ($modname:ident, $stype:ident, $ptype:ident) => {
#[cfg(test)]
mod $modname {
use std::sync::Arc;
use super::*;
#[test]
fn ref_to_owned() {
let x = 42u32;
let a: $stype<u32> = Supercow::borrowed(&x);
assert_eq!(x, *a);
assert_eq!(&x as *const u32 as usize,
(&*a) as *const u32 as usize);
let mut b = a.clone();
assert_eq!(x, *b);
assert_eq!(&x as *const u32 as usize,
(&*b) as *const u32 as usize);
*b.to_mut() = 56;
assert_eq!(42, *a);
assert_eq!(x, *a);
assert_eq!(&x as *const u32 as usize,
(&*a) as *const u32 as usize);
assert_eq!(56, *b);
}
#[test]
fn supports_dst() {
let a: $stype<String, str> = Supercow::borrowed("hello");
let b: $stype<String, str> = Supercow::owned("hello".to_owned());
assert_eq!(a, b);
let mut c = a.clone();
c.to_mut().push_str(" world");
assert_eq!(a, b);
assert_eq!(c, "hello world");
}
#[test]
fn default_accepts_arc() {
let x: $stype<u32> = Supercow::shared(Arc::new(42u32));
assert_eq!(42, *x);
}
#[test]
fn ref_safe_even_if_forgotten() {
let mut x: $stype<String, str> = Supercow::owned("foo".to_owned());
{
let mut m = x.to_mut();
for _ in 0..65536 {
m.push('x');
}
::std::mem::forget(m);
}
assert_eq!("", &*x);
assert_eq!(65539, x.to_mut().len());
}
#[test]
#[allow(deprecated)]
fn general_trait_delegs_work() {
use std::borrow::Borrow;
use std::convert::AsRef;
use std::cmp::*;
use std::hash::*;
macro_rules! test_fmt {
($fmt:expr, $x:expr) => {
assert_eq!(format!($fmt, 42u32), format!($fmt, $x));
}
}
let x: $stype<u32> = Supercow::owned(42u32);
test_fmt!("{}", x);
test_fmt!("{:?}", x);
test_fmt!("{:o}", x);
test_fmt!("{:x}", x);
test_fmt!("{:X}", x);
test_fmt!("{:b}", x);
assert!(x == 42);
assert!(x != 43);
assert!(x < 43);
assert!(x <= 43);
assert!(x > 41);
assert!(x >= 41);
assert_eq!(42.partial_cmp(&43), x.partial_cmp(&43));
assert_eq!(42.cmp(&43), x.cmp(&Supercow::owned(43)));
let mut expected_hash = SipHasher::new();
42u32.hash(&mut expected_hash);
let mut actual_hash = SipHasher::new();
x.hash(&mut actual_hash);
assert_eq!(expected_hash.finish(), actual_hash.finish());
assert_eq!(42u32, *x.borrow());
assert_eq!(42u32, *x.as_ref());
}
#[test]
fn owned_mode_survives_moving() {
#[inline(never)]
fn pick_one() -> $stype<'static, String> {
use std::collections::HashMap;
let mut hm = HashMap::new();
hm.insert("hello", Supercow::owned("hello".to_owned()));
hm.insert("world", Supercow::owned("world".to_owned()));
hm.into_iter().map(|(_, v)| v).next().unwrap()
}
let s = pick_one();
assert!("hello".to_owned() == *s ||
"world".to_owned() == *s);
}
#[test]
fn dst_string_str() {
let mut s: $stype<'static, String, str> = String::new().into();
let mut expected = String::new();
for i in 0..1024 {
assert_eq!(expected.as_str(), &*s);
expected.push_str(&format!("{}", i));
s.to_mut().push_str(&format!("{}", i));
assert_eq!(expected.as_str(), &*s);
}
}
#[test]
fn dst_vec_u8s() {
let mut s: $stype<'static, Vec<u8>, [u8]> = Vec::new().into();
let mut expected = Vec::<u8>::new();
for i in 0..1024 {
assert_eq!(&expected[..], &*s);
expected.push((i & 0xFF) as u8);
s.to_mut().push((i & 0xFF) as u8);
assert_eq!(&expected[..], &*s);
}
}
#[test]
fn dst_osstring_osstr() {
use std::ffi::{OsStr, OsString};
let mut s: $stype<'static, OsString, OsStr> = OsString::new().into();
let mut expected = OsString::new();
for i in 0..1024 {
assert_eq!(expected.as_os_str(), &*s);
expected.push(&format!("{}", i));
s.to_mut().push(&format!("{}", i));
assert_eq!(expected.as_os_str(), &*s);
}
}
#[test]
fn dst_cstring_cstr() {
use std::ffi::{CStr, CString};
use std::mem;
use std::ops::Deref;
let mut s: $stype<'static, CString, CStr> =
CString::new("").unwrap().into();
let mut expected = CString::new("").unwrap();
for i in 0..1024 {
assert_eq!(expected.deref(), &*s);
{
let mut ve = expected.into_bytes_with_nul();
ve.pop();
ve.push(((i & 0xFF) | 1) as u8);
ve.push(0);
expected = unsafe {
CString::from_vec_unchecked(ve)
};
}
{
let mut m = s.to_mut();
let mut vs = mem::replace(&mut *m, CString::new("").unwrap())
.into_bytes_with_nul();
vs.pop();
vs.push(((i & 0xFF) | 1) as u8);
vs.push(0);
*m = unsafe {
CString::from_vec_unchecked(vs)
};
}
assert_eq!(expected.deref(), &*s);
}
}
#[test]
fn dst_pathbuf_path() {
use std::path::{Path, PathBuf};
let mut s: $stype<'static, PathBuf, Path> = PathBuf::new().into();
let mut expected = PathBuf::new();
for i in 0..1024 {
assert_eq!(expected.as_path(), &*s);
expected.push(format!("{}", i));
s.to_mut().push(format!("{}", i));
assert_eq!(expected.as_path(), &*s);
}
}
#[test]
fn unborrow_owned() {
let orig: Supercow<String, str> =
Supercow::owned("hello world".to_owned());
let unborrowed = Supercow::unborrow(orig);
assert_eq!(unborrowed, "hello world");
}
#[test]
fn unborrow_borrowed() {
let orig: Supercow<String, str> =
Supercow::borrowed("hello world");
let unborrowed = Supercow::unborrow(orig);
assert_eq!(unborrowed, "hello world");
}
#[test]
fn unborrow_shared() {
let orig: Supercow<String> =
Supercow::shared(Arc::new("hello world".to_owned()));
let unborrowed = Supercow::unborrow(orig);
assert_eq!(unborrowed, "hello world".to_owned());
}
#[test]
fn take_ownership_owned() {
let orig: Supercow<String, str> =
Supercow::owned("hello world".to_owned());
let owned: Supercow<String, str> = Supercow::take_ownership(orig);
assert_eq!(owned, "hello world");
}
#[test]
fn take_ownership_borrowed() {
let orig: Supercow<String, str> =
Supercow::borrowed("hello world");
let owned: Supercow<String, str> = Supercow::take_ownership(orig);
assert_eq!(owned, "hello world");
}
#[test]
fn take_ownership_shared() {
let orig: Supercow<String> =
Supercow::shared(Arc::new("hello world".to_owned()));
let owned: Supercow<String> = Supercow::take_ownership(orig);
assert_eq!(owned, "hello world".to_owned());
}
struct MockNativeResource(*mut u32);
impl Drop for MockNativeResource {
fn drop(&mut self) {
unsafe { *self.0 = 0 };
}
}
unsafe impl Send for MockNativeResource { }
unsafe impl Sync for MockNativeResource { }
struct MockDependentResource<'a> {
ptr: *mut u32,
_handle: $ptype<'a, MockNativeResource>,
}
fn check_dependent_ok(mdr: MockDependentResource) {
assert_eq!(42, unsafe { *mdr.ptr });
}
#[test]
fn borrowed_phantomcow() {
let mut forty_two = 42u32;
let native = MockNativeResource(&mut forty_two);
let sc: $stype<MockNativeResource> = Supercow::borrowed(&native);
check_dependent_ok(MockDependentResource {
ptr: &mut forty_two,
_handle: Supercow::phantom(sc),
});
}
#[test]
fn owned_phantomcow() {
let mut forty_two = 42u32;
let native = MockNativeResource(&mut forty_two);
let sc: $stype<MockNativeResource> = Supercow::owned(native);
check_dependent_ok(MockDependentResource {
ptr: &mut forty_two,
_handle: Supercow::phantom(sc),
});
}
#[test]
fn shared_phantomcow() {
let mut forty_two = 42u32;
let native = MockNativeResource(&mut forty_two);
let sc: $stype<MockNativeResource> =
Supercow::shared(Arc::new(native));
check_dependent_ok(MockDependentResource {
ptr: &mut forty_two,
_handle: Supercow::phantom(sc),
});
}
#[test]
fn clone_owned_phantomcow() {
let sc: $stype<String> = Supercow::owned("hello world".to_owned());
let p1 = Supercow::phantom(sc);
assert!(Supercow::clone_non_owned(&p1).is_none());
let _p2 = p1.clone();
}
#[test]
fn clone_borrowed_phantomcow() {
let sc: $stype<String, str> = Supercow::borrowed("hello world");
let p1 = Supercow::phantom(sc);
assert!(Supercow::clone_non_owned(&p1).is_some());
let _p2 = p1.clone();
}
#[test]
fn clone_shared_phantomcow() {
let sc: $stype<String> = Supercow::shared(
Arc::new("hello world".to_owned()));
let p1 = Supercow::phantom(sc);
assert!(Supercow::clone_non_owned(&p1).is_some());
let _p2 = p1.clone();
}
struct NotCloneable(u32);
impl Drop for NotCloneable {
fn drop(&mut self) {
self.0 = 0;
}
}
#[test]
fn share_owned_supercow() {
let mut a: $stype<NotCloneable> = Supercow::owned(NotCloneable(42));
let b = Supercow::share(&mut a);
assert_eq!(42, (*a).0);
assert_eq!(42, (*b).0);
}
#[test]
fn share_borrowed_supercow() {
let nc = NotCloneable(42);
let mut a: $stype<NotCloneable> = Supercow::borrowed(&nc);
let b = Supercow::share(&mut a);
assert_eq!(42, (*a).0);
assert_eq!(42, (*b).0);
}
#[test]
fn share_shared_supercow() {
let mut a: $stype<NotCloneable> = Supercow::shared(
Arc::new(NotCloneable(42)));
let b = Supercow::share(&mut a);
assert_eq!(42, (*a).0);
assert_eq!(42, (*b).0);
}
#[test]
fn share_owned_dst_supercow() {
let mut a: $stype<String, str> = Supercow::owned("hello world".into());
let b = Supercow::share(&mut a);
assert_eq!("hello world", &*a);
assert_eq!("hello world", &*b);
}
#[test]
fn share_owned_phantomcow() {
let sc: $stype<NotCloneable> = Supercow::owned(NotCloneable(42));
let mut a: $ptype<NotCloneable> = Supercow::phantom(sc);
let _b = Supercow::share(&mut a);
}
#[test]
fn share_borrowed_phantomcow() {
let nc = NotCloneable(42);
let sc: $stype<NotCloneable> = Supercow::borrowed(&nc);
let mut a: $ptype<NotCloneable> = Supercow::phantom(sc);
let _b = Supercow::share(&mut a);
}
#[test]
fn share_shared_phantomcow() {
let sc: $stype<NotCloneable> =
Supercow::shared(Arc::new(NotCloneable(42)));
let mut a: $ptype<NotCloneable> = Supercow::phantom(sc);
let _b = Supercow::share(&mut a);
}
#[test]
fn share_owned_dst_phantomcow() {
let sc: $stype<String, str> = Supercow::owned("hello world".into());
let mut a: $ptype<String, str> = Supercow::phantom(sc);
let _b = Supercow::share(&mut a);
}
} } }
tests!(inline_sync_tests, InlineSupercow, InlinePhantomcow);
tests!(inline_nonsync_tests, InlineNonSyncSupercow, InlineNonSyncPhantomcow);
tests!(boxed_sync_tests, Supercow, Phantomcow);
tests!(boxed_nonsync_tests, NonSyncSupercow, NonSyncPhantomcow);