#![warn(missing_docs, missing_debug_implementations)]
#![no_std]
use {
core::{
fmt,
hash::{self, Hash},
hint::unreachable_unchecked,
marker::PhantomData,
mem::{self, ManuallyDrop},
ops::Deref,
},
erasable::{ErasablePtr, ErasedPtr},
};
const MASK_2: usize = 0b01;
const MASK_4: usize = 0b11;
const MASK_A: usize = 0b00;
const MASK_B: usize = 0b01;
const MASK_C: usize = 0b10;
const MASK_D: usize = 0b11;
fn check(ptr: ErasedPtr, mask: usize, value: usize) -> bool {
debug_assert_eq!(value & mask, value);
(ptr.as_ptr() as usize & mask) == value
}
fn mask(ptr: ErasedPtr, mask: usize, value: usize) -> ErasedPtr {
debug_assert_eq!(value & mask, value);
debug_assert!(check(ptr, mask, 0));
let high = ptr.as_ptr() as usize & !mask;
let low = value & mask;
unsafe { ErasedPtr::new_unchecked((high | low) as *mut _) }
}
fn unmask(ptr: ErasedPtr, mask: usize, value: usize) -> ErasedPtr {
debug_assert!(check(ptr, mask, value));
unsafe { ErasedPtr::new_unchecked((ptr.as_ptr() as usize & !mask) as *mut _) }
}
pub struct UnionBuilder<U> {
private: PhantomData<U>,
}
impl<U> Copy for UnionBuilder<U> {}
impl<U> Clone for UnionBuilder<U> {
fn clone(&self) -> Self {
*self
}
}
impl<U> fmt::Debug for UnionBuilder<U> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("UnionBuilder")
.field(&format_args!("{}", core::any::type_name::<U>()))
.finish()
}
}
impl<A, B> UnionBuilder<Union2<A, B>> {
pub const unsafe fn new() -> Self {
UnionBuilder {
private: PhantomData,
}
}
}
impl<A, B, C, D> UnionBuilder<Union4<A, B, C, D>> {
pub const unsafe fn new() -> Self {
UnionBuilder {
private: PhantomData,
}
}
}
pub struct Union2<A, B> {
raw: ErasedPtr,
a: PhantomData<A>,
b: PhantomData<B>,
}
pub struct Union4<A, B, C, D = NeverPtr> {
raw: ErasedPtr,
a: PhantomData<A>,
b: PhantomData<B>,
c: PhantomData<C>,
d: PhantomData<D>,
}
impl<A: ErasablePtr, B: ErasablePtr> UnionBuilder<Union2<A, B>> {
pub fn a(self, a: A) -> Union2<A, B> {
Union2 {
raw: mask(A::erase(a), MASK_2, MASK_A),
a: PhantomData,
b: PhantomData,
}
}
pub fn b(self, b: B) -> Union2<A, B> {
Union2 {
raw: mask(B::erase(b), MASK_2, MASK_B),
a: PhantomData,
b: PhantomData,
}
}
}
impl<A: ErasablePtr, B: ErasablePtr, C: ErasablePtr, D: ErasablePtr>
UnionBuilder<Union4<A, B, C, D>>
{
pub fn a(self, a: A) -> Union4<A, B, C, D> {
Union4 {
raw: mask(A::erase(a), MASK_4, MASK_A),
a: PhantomData,
b: PhantomData,
c: PhantomData,
d: PhantomData,
}
}
pub fn b(self, b: B) -> Union4<A, B, C, D> {
Union4 {
raw: mask(B::erase(b), MASK_4, MASK_B),
a: PhantomData,
b: PhantomData,
c: PhantomData,
d: PhantomData,
}
}
pub fn c(self, c: C) -> Union4<A, B, C, D> {
Union4 {
raw: mask(C::erase(c), MASK_4, MASK_C),
a: PhantomData,
b: PhantomData,
c: PhantomData,
d: PhantomData,
}
}
pub fn d(self, d: D) -> Union4<A, B, C, D> {
Union4 {
raw: mask(D::erase(d), MASK_4, MASK_D),
a: PhantomData,
b: PhantomData,
c: PhantomData,
d: PhantomData,
}
}
}
macro_rules! union_methods {
($Union:ident: $mask:ident $([$a:ident $A:ident])*) => {
impl<$($A: ErasablePtr),*> $Union<$($A),*> {
$(
paste::item! {
pub fn [<is_ $a>](&self) -> bool {
check(self.raw, $mask, [<MASK_ $A>])
}
}
paste::item! {
pub fn [<into_ $a>](self) -> Result<$A, Self> {
if self.[<is_ $a>]() {
unsafe { Ok($A::unerase(unmask(self.raw, $mask, [<MASK_ $A>]))) }
} else {
Err(self)
}
}
}
paste::item! {
pub fn [<with_ $a>]<R>(&self, f: impl FnOnce(&$A) -> R) -> Option<R> {
if self.[<is_ $a>]() {
unsafe {
let $a = ManuallyDrop::new($A::unerase(unmask(self.raw, $mask, [<MASK_ $A>])));
Some(f(&$a))
}
} else {
None
}
}
}
paste::item! {
pub fn $a(&self) -> Option<&$A::Target>
where
$A: Deref,
{
self.[<with_ $a>](|$a| unsafe { erase_lt(&**$a) })
}
}
paste::item! {
pub fn [<clone_ $a>](&self) -> Option<$A>
where
$A: Clone,
{
self.[<with_ $a>](|$a| $a.clone() )
}
}
paste::item! {
pub fn [<copy_ $a>](&self) -> Option<$A>
where
$A: Copy,
{
self.[<with_ $a>](|$a| *$a)
}
}
)*
unsafe fn builder(&self) -> UnionBuilder<Self> {
UnionBuilder::<Self>::new()
}
pub fn ptr_eq(&self, other: &Self) -> bool {
self.raw == other.raw
}
pub fn as_deref<'a>(
&'a self,
proof: UnionBuilder<$Union<$(&'a $A::Target),*>>,
) -> $Union<$(&'a $A::Target),*>
where
$($A: Deref,)*
$(&'a $A::Target: ErasablePtr,)*
{
$(if let Some($a) = self.$a() {
proof.$a($a)
} else)* {
unsafe { unreachable_unchecked() }
}
}
}
};
}
union_methods!(Union2: MASK_2 [a A] [b B]);
union_methods!(Union4: MASK_4 [a A] [b B] [c C] [d D]);
impl<A: ErasablePtr, B: ErasablePtr> Union2<A, B> {
pub unsafe fn as_deref_unchecked<'a>(&'a self) -> Union2<&'a A::Target, &'a B::Target>
where
A: Deref,
B: Deref,
&'a A::Target: ErasablePtr,
&'a B::Target: ErasablePtr,
{
self.as_deref(UnionBuilder::<Union2<_, _>>::new())
}
pub fn unpack(self) -> Enum2<A, B> {
Err(self)
.or_else(|this| this.into_a().map(Enum2::A))
.or_else(|this| this.into_b().map(Enum2::B))
.unwrap_or_else(|_| unsafe { unreachable_unchecked() })
}
}
impl<A: ErasablePtr, B: ErasablePtr, C: ErasablePtr, D: ErasablePtr> Union4<A, B, C, D> {
pub unsafe fn as_deref_unchecked<'a>(
&'a self,
) -> Union4<&'a A::Target, &'a B::Target, &'a C::Target, &'a D::Target>
where
A: Deref,
B: Deref,
C: Deref,
D: Deref,
&'a A::Target: ErasablePtr,
&'a B::Target: ErasablePtr,
&'a C::Target: ErasablePtr,
&'a D::Target: ErasablePtr,
{
self.as_deref(UnionBuilder::<Union4<_, _, _, _>>::new())
}
pub fn unpack(self) -> Enum4<A, B, C, D> {
Err(self)
.or_else(|this| this.into_a().map(Enum4::A))
.or_else(|this| this.into_b().map(Enum4::B))
.or_else(|this| this.into_c().map(Enum4::C))
.or_else(|this| this.into_d().map(Enum4::D))
.unwrap_or_else(|_| unsafe { unreachable_unchecked() })
}
}
macro_rules! union_traits {
($Union:ident $([$a:ident $A:ident])*) => {
impl<$($A: ErasablePtr),*> fmt::Debug for $Union<$($A),*>
where
$($A: fmt::Debug,)*
{
paste::item! {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
None
$(.or_else(|| self.[<with_ $a>](|$a| f
.debug_tuple(concat!(stringify!($Union), "::", stringify!($A)))
.field($a)
.finish()
)))*
.unwrap_or_else(|| unsafe { unreachable_unchecked() })
}
}
}
impl<$($A: ErasablePtr),*> Copy for $Union<$($A),*> where $($A: Copy,)* {}
impl<$($A: ErasablePtr),*> Clone for $Union<$($A),*>
where
$($A: Clone,)*
{
paste::item! {
fn clone(&self) -> Self {
let builder = unsafe { self.builder() };
None
$(.or_else(|| self.[<clone_ $a>]().map(|$a| builder.$a($a))))*
.unwrap_or_else(|| unsafe { unreachable_unchecked() })
}
}
}
impl<$($A: ErasablePtr,)*> Eq for $Union<$($A),*> where $($A: Eq,)* {}
impl<$($A: ErasablePtr),*> PartialEq for $Union<$($A),*>
where
$($A: PartialEq,)*
{
paste::item! {
fn eq(&self, other: &Self) -> bool {
None
$(.or_else(|| self.[<with_ $a>](|this|
other.[<with_ $a>](|that|
this == that
).unwrap_or(false)
)))*
.unwrap_or(false)
}
}
}
impl<$($A: ErasablePtr,)*> Hash for $Union<$($A),*>
where
$($A: Hash,)*
{
paste::item! {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher
{
None
$(.or_else(|| self.[<with_ $a>](|$a| $a.hash(state))))*
.unwrap_or_else(|| unsafe { unreachable_unchecked() })
}
}
}
};
}
union_traits!(Union2 [a A] [b B]);
union_traits!(Union4 [a A] [b B] [c C] [d D]);
unsafe fn erase_lt<'a, 'b, T: ?Sized>(r: &'a T) -> &'b T {
mem::transmute(r)
}
#[cfg(not(has_never))]
use priv_in_pub::NeverPtr;
#[cfg(not(has_never))]
mod priv_in_pub {
use super::*;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum NeverPtr {}
unsafe impl ErasablePtr for NeverPtr {
fn erase(_: Self) -> ErasedPtr {
unreachable!()
}
unsafe fn unerase(_: ErasedPtr) -> Self {
unreachable!()
}
}
}
#[cfg(has_never)]
pub type NeverPtr = !;
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Enum2<A, B> {
A(A),
B(B),
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Enum4<A, B, C, D = NeverPtr> {
A(A),
B(B),
C(C),
D(D),
}
impl<A: ErasablePtr, B: ErasablePtr> Enum2<A, B> {
pub fn pack(self, proof: UnionBuilder<Union2<A, B>>) -> Union2<A, B> {
match self {
Enum2::A(a) => proof.a(a),
Enum2::B(b) => proof.b(b),
}
}
pub unsafe fn pack_unchecked(self) -> Union2<A, B> {
self.pack(UnionBuilder::<Union2<A, B>>::new())
}
}
impl<A: ErasablePtr, B: ErasablePtr, C: ErasablePtr, D: ErasablePtr> Enum4<A, B, C, D> {
pub fn pack(self, proof: UnionBuilder<Union4<A, B, C, D>>) -> Union4<A, B, C, D> {
match self {
Enum4::A(a) => proof.a(a),
Enum4::B(b) => proof.b(b),
Enum4::C(c) => proof.c(c),
Enum4::D(d) => proof.d(d),
}
}
pub unsafe fn pack_unchecked(self) -> Union4<A, B, C, D> {
self.pack(UnionBuilder::<Union4<A, B, C, D>>::new())
}
}