use alloc::boxed::Box;
use core::{
any::TypeId,
fmt,
ptr::{addr_of, NonNull},
};
#[cfg(nightly)]
use crate::{
frame::attachment::AttachmentProvider,
provider::{Demand, Provider},
};
use crate::{
frame::{kind::AttachmentKind, ErasableFrame},
Context, FrameKind,
};
pub(in crate::frame) struct VTable {
object_drop: unsafe fn(NonNull<ErasableFrame>),
object_downcast: unsafe fn(&ErasableFrame, target: TypeId) -> Option<NonNull<()>>,
unerase: unsafe fn(&ErasableFrame) -> FrameKind<'_>,
#[cfg(nightly)]
provide: unsafe fn(&ErasableFrame, &mut Demand),
}
impl VTable {
pub fn new_context<C: Context>() -> &'static Self {
&Self {
object_drop: Self::object_drop::<C>,
object_downcast: Self::object_downcast::<C>,
unerase: Self::unerase_context::<C>,
#[cfg(nightly)]
provide: Self::context_provide::<C>,
}
}
pub fn new_printable_attachment<A>() -> &'static Self
where
A: fmt::Display + fmt::Debug + Send + Sync + 'static,
{
&Self {
object_drop: Self::object_drop::<A>,
object_downcast: Self::object_downcast::<A>,
unerase: Self::unerase_printable_attachment::<A>,
#[cfg(nightly)]
provide: Self::self_provide::<A>,
}
}
pub fn new_attachment<A>() -> &'static Self
where
A: Send + Sync + 'static,
{
&Self {
object_drop: Self::object_drop::<A>,
object_downcast: Self::object_downcast::<A>,
unerase: Self::unerase_generic_attachment::<A>,
#[cfg(nightly)]
provide: Self::self_provide::<A>,
}
}
pub(in crate::frame) fn unerase<'f>(&self, frame: &'f ErasableFrame) -> FrameKind<'f> {
unsafe { (self.unerase)(frame) }
}
#[cfg(nightly)]
pub(in crate::frame) fn provide(&self, frame: &ErasableFrame, demand: &mut Demand) {
unsafe { (self.provide)(frame, demand) }
}
pub(in crate::frame) fn downcast_ref<'f, T: Send + Sync + 'static>(
&self,
frame: &'f ErasableFrame,
) -> Option<&'f T> {
unsafe { (self.object_downcast)(frame, TypeId::of::<T>()).map(|ptr| ptr.cast().as_ref()) }
}
pub(in crate::frame) fn downcast_mut<'f, T: Send + Sync + 'static>(
&self,
frame: &'f mut ErasableFrame,
) -> Option<&'f mut T> {
unsafe { (self.object_downcast)(frame, TypeId::of::<T>()).map(|ptr| ptr.cast().as_mut()) }
}
pub(in crate::frame) fn drop(&self, frame: NonNull<ErasableFrame>) {
unsafe { (self.object_drop)(frame) }
}
unsafe fn object_drop<T>(frame: NonNull<ErasableFrame>) {
let unerased: Box<ErasableFrame<T>> = Box::from_raw(frame.as_ptr().cast());
drop(unerased);
}
#[cfg(nightly)]
unsafe fn context_provide<C: Context>(frame: &ErasableFrame, demand: &mut Demand) {
let unerased: *const ErasableFrame<C> = (frame as *const ErasableFrame).cast();
#[allow(clippy::used_underscore_binding)]
(*(unerased))._unerased.provide(demand);
}
#[cfg(nightly)]
unsafe fn self_provide<A: 'static>(frame: &ErasableFrame, demand: &mut Demand) {
let unerased: *const ErasableFrame<AttachmentProvider<A>> =
(frame as *const ErasableFrame).cast();
#[allow(clippy::used_underscore_binding)]
(*(unerased))._unerased.provide(demand);
}
unsafe fn unerase_context<C: Context>(frame: &ErasableFrame) -> FrameKind<'_> {
let unerased: *const ErasableFrame<C> = (frame as *const ErasableFrame).cast();
#[allow(clippy::used_underscore_binding)]
FrameKind::Context(&(*(unerased))._unerased)
}
unsafe fn unerase_generic_attachment<A: Send + Sync + 'static>(
frame: &ErasableFrame,
) -> FrameKind<'_> {
let unerased: *const ErasableFrame<A> = (frame as *const ErasableFrame).cast();
#[allow(clippy::used_underscore_binding)]
FrameKind::Attachment(AttachmentKind::Opaque(&(*(unerased))._unerased))
}
unsafe fn unerase_printable_attachment<A: fmt::Debug + fmt::Display + Send + Sync + 'static>(
frame: &ErasableFrame,
) -> FrameKind<'_> {
let unerased: *const ErasableFrame<A> = (frame as *const ErasableFrame).cast();
#[allow(clippy::used_underscore_binding)]
FrameKind::Attachment(AttachmentKind::Printable(&(*(unerased))._unerased))
}
unsafe fn object_downcast<T: Send + Sync + 'static>(
frame: &ErasableFrame,
target: TypeId,
) -> Option<NonNull<()>> {
if TypeId::of::<T>() == target {
let unerased: *const ErasableFrame<T> = (frame as *const ErasableFrame).cast();
#[allow(clippy::used_underscore_binding)]
let addr = addr_of!((*(unerased))._unerased) as *mut ();
Some(NonNull::new_unchecked(addr))
} else {
None
}
}
}