use core::fmt; use core::marker::PhantomData; use core::ptr; use core::usize; use super::{Arc, ArcBorrow}; /// A tagged union that can represent `Arc` or `Arc` while only consuming a /// single word. The type is also `NonNull`, and thus can be stored in an Option /// without increasing size. /// /// This is functionally equivalent to /// `enum ArcUnion { First(Arc), Second(Arc)` but only takes up /// up a single word of stack space. /// /// This could probably be extended to support four types if necessary. pub struct ArcUnion { p: ptr::NonNull<()>, phantom_a: PhantomData, phantom_b: PhantomData, } unsafe impl Send for ArcUnion {} unsafe impl Sync for ArcUnion {} impl PartialEq for ArcUnion { fn eq(&self, other: &Self) -> bool { use crate::ArcUnionBorrow::*; match (self.borrow(), other.borrow()) { (First(x), First(y)) => x == y, (Second(x), Second(y)) => x == y, (_, _) => false, } } } /// This represents a borrow of an `ArcUnion`. #[derive(Debug)] pub enum ArcUnionBorrow<'a, A: 'a, B: 'a> { First(ArcBorrow<'a, A>), Second(ArcBorrow<'a, B>), } impl ArcUnion { unsafe fn new(ptr: *mut ()) -> Self { ArcUnion { p: ptr::NonNull::new_unchecked(ptr), phantom_a: PhantomData, phantom_b: PhantomData, } } /// Returns true if the two values are pointer-equal. #[inline] pub fn ptr_eq(this: &Self, other: &Self) -> bool { this.p == other.p } /// Returns an enum representing a borrow of either A or B. pub fn borrow(&self) -> ArcUnionBorrow { if self.is_first() { let ptr = self.p.as_ptr() as *const A; let borrow = unsafe { ArcBorrow::from_ref(&*ptr) }; ArcUnionBorrow::First(borrow) } else { let ptr = ((self.p.as_ptr() as usize) & !0x1) as *const B; let borrow = unsafe { ArcBorrow::from_ref(&*ptr) }; ArcUnionBorrow::Second(borrow) } } /// Creates an `ArcUnion` from an instance of the first type. #[inline] pub fn from_first(other: Arc) -> Self { unsafe { Self::new(Arc::into_raw(other) as *mut _) } } /// Creates an `ArcUnion` from an instance of the second type. #[inline] pub fn from_second(other: Arc) -> Self { unsafe { Self::new(((Arc::into_raw(other) as usize) | 0x1) as *mut _) } } /// Returns true if this `ArcUnion` contains the first type. #[inline] pub fn is_first(&self) -> bool { self.p.as_ptr() as usize & 0x1 == 0 } /// Returns true if this `ArcUnion` contains the second type. #[inline] pub fn is_second(&self) -> bool { !self.is_first() } /// Returns a borrow of the first type if applicable, otherwise `None`. pub fn as_first(&self) -> Option> { match self.borrow() { ArcUnionBorrow::First(x) => Some(x), ArcUnionBorrow::Second(_) => None, } } /// Returns a borrow of the second type if applicable, otherwise None. pub fn as_second(&self) -> Option> { match self.borrow() { ArcUnionBorrow::First(_) => None, ArcUnionBorrow::Second(x) => Some(x), } } } impl Clone for ArcUnion { fn clone(&self) -> Self { match self.borrow() { ArcUnionBorrow::First(x) => ArcUnion::from_first(x.clone_arc()), ArcUnionBorrow::Second(x) => ArcUnion::from_second(x.clone_arc()), } } } impl Drop for ArcUnion { fn drop(&mut self) { match self.borrow() { ArcUnionBorrow::First(x) => unsafe { let _ = Arc::from_raw(&*x); }, ArcUnionBorrow::Second(x) => unsafe { let _ = Arc::from_raw(&*x); }, } } } impl fmt::Debug for ArcUnion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.borrow(), f) } }