use std::{
any::TypeId,
hash::{Hash, Hasher},
marker::PhantomPinned,
ops::{Deref, DerefMut},
ptr::NonNull,
};
use fnv::FnvHasher;
use super::{Managed, private::ManagedPriv};
use crate::{
data::{
managed::{datatype::DataType, module::Module, symbol::Symbol, value::Value},
types::foreign_type::{ForeignType, OpaqueType},
},
memory::target::{Target, unrooted::Unrooted},
prelude::LocalScope,
private::Private,
};
#[repr(transparent)]
struct Pinned<T> {
data: T,
_pinned: PhantomPinned,
}
pub struct WithParachute<'scope, T> {
data: &'scope mut Pinned<Option<T>>,
}
impl<'scope, T> WithParachute<'scope, T> {
pub fn remove_parachute(self) -> T {
self.data.data.take().expect("Data is None")
}
}
impl<'scope, T> Deref for WithParachute<'scope, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data.data.as_ref().expect("Data is None")
}
}
impl<'scope, T> DerefMut for WithParachute<'scope, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data.data.as_mut().expect("Data is None")
}
}
pub trait AttachParachute: 'static + Sized + Send + Sync {
fn attach_parachute<'scope, Tgt: Target<'scope>>(
self,
target: Tgt,
) -> WithParachute<'scope, Self> {
let parachute = Parachute {
_data: Pinned {
data: Some(self),
_pinned: PhantomPinned,
},
};
let data = Value::new(&target, parachute);
unsafe {
data.root(target);
let mut ptr: NonNull<Pinned<Option<Self>>> = data.ptr().cast();
WithParachute { data: ptr.as_mut() }
}
}
}
impl<T: 'static + Sized + Send + Sync> AttachParachute for T {}
#[repr(transparent)]
pub(crate) struct Parachute<T: Sync + Send + 'static> {
_data: Pinned<Option<T>>,
}
unsafe impl<T: Send + Sync + 'static> ForeignType for Parachute<T> {
const TYPE_FN: Option<unsafe fn() -> DataType<'static>> = Some(init_foreign::<Self>);
unsafe fn mark<P>(_: crate::memory::PTls, _: &Self, _: &P) -> usize {
0
}
}
#[doc(hidden)]
unsafe fn init_foreign<T: ForeignType>() -> DataType<'static> {
let mut hasher = FnvHasher::default();
let type_id = TypeId::of::<T>();
type_id.hash(&mut hasher);
let type_id_hash = hasher.finish();
let name = format!("__Parachute_{:x}__", type_id_hash);
unsafe {
let unrooted = Unrooted::new();
let dt = unrooted.local_scope::<_, 1>(|mut frame| {
let sym = Symbol::new(&frame, name.as_str());
let module = Module::main(&frame);
let dt = <T as OpaqueType>::create_type(&mut frame, sym, module);
module.set_const_unchecked(sym, dt.as_value());
dt.unwrap_non_null(Private)
});
DataType::wrap_non_null(dt, Private)
}
}