use std::{
os,
ptr,
any,
cell::RefCell,
rc::Rc,
sync::{Mutex, RwLock, Arc},
};
use emacs_module::emacs_finalizer_function;
use super::*;
use crate::ErrorKind;
pub trait Transfer: Sized + 'static {
fn type_name() -> &'static str {
any::type_name::<Self>()
}
}
impl<'e, T: Transfer> FromLisp<'e> for &'e T {
fn from_lisp(value: Value<'e>) -> Result<Self> {
value.get_raw_pointer().map(|r| unsafe { &*r })
}
}
unsafe extern "C" fn finalize<T: Transfer>(ptr: *mut os::raw::c_void) {
#[cfg(feature = "debug")]
println!("Finalizing {:#?} {}", ptr, T::type_name());
drop(Box::from_raw(ptr as *mut T));
}
impl<T: Transfer> IntoLisp<'_> for Box<T> {
fn into_lisp(self, env: &Env) -> Result<Value<'_>> {
let raw = Box::into_raw(self);
let ptr = raw as *mut os::raw::c_void;
unsafe { env.make_user_ptr(Some(finalize::<T>), ptr) }
}
}
macro_rules! enable_transfers {
($($name:ident;)*) => {$(
impl<T: 'static> Transfer for $name<T> {}
impl<'e, T: 'static> IntoLisp<'e> for $name<T> {
#[inline]
fn into_lisp(self, env: &Env) -> Result<Value<'_>> {
Box::new(self).into_lisp(env)
}
}
)*};
}
enable_transfers! {
RefCell;
Mutex;
RwLock;
Rc;
Arc;
}
impl Env {
#[allow(unused_unsafe)]
#[inline]
pub unsafe fn make_user_ptr(&self, fin: emacs_finalizer_function, ptr: *mut os::raw::c_void) -> Result<Value> {
unsafe_raw_call_value!(self, make_user_ptr, fin, ptr)
}
}
impl<'e> Value<'e> {
#[inline]
pub fn get_user_ptr(self) -> Result<*mut os::raw::c_void> {
unsafe_raw_call!(self.env, get_user_ptr, self.raw)
}
#[inline]
pub fn get_user_finalizer(self) -> Result<emacs_finalizer_function> {
unsafe_raw_call!(self.env, get_user_finalizer, self.raw)
}
pub(crate) fn get_raw_pointer<T: Transfer>(self) -> Result<*mut T> {
match self.get_user_finalizer()? {
Some(fin) if ptr::fn_addr_eq(fin, finalize::<T> as unsafe extern "C" fn(*mut os::raw::c_void)) => {
let ptr = self.get_user_ptr()?;
Ok(ptr as *mut T)
}
_ => {
let expected = T::type_name();
Err(ErrorKind::WrongTypeUserPtr { expected }.into())
}
}
}
}