use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;
use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };
#[doc(hidden)]
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;
#[doc(hidden)]
pub struct NifResourceType<T> {
pub res: NIF_RESOURCE_TYPE,
pub struct_type: PhantomData<T>,
}
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync + 'static {
fn get_type() -> &'static NifResourceType<Self>;
}
impl<T> NifEncoder for ResourceArc<T> where T: NifResourceTypeProvider {
fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
self.as_term(env)
}
}
impl<'a, T> NifDecoder<'a> for ResourceArc<T> where T: NifResourceTypeProvider + 'a {
fn decode(term: NifTerm<'a>) -> NifResult<Self> {
ResourceArc::from_term(term)
}
}
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
unsafe {
let aligned = align_alloced_mem_for_struct::<T>(handle);
let res = aligned as *mut T;
ptr::read(res);
}
}
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
flags: NifResourceFlags) -> Option<NifResourceType<T>> {
let res: Option<NIF_RESOURCE_TYPE> = unsafe {
::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
};
if res.is_some() {
Some(NifResourceType {
res: res.unwrap(),
struct_type: PhantomData,
})
} else {
None
}
}
fn get_alloc_size_struct<T>() -> usize {
mem::size_of::<T>() + mem::align_of::<T>()
}
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
ptr.offset(offset as isize)
}
pub struct ResourceArc<T> where T: NifResourceTypeProvider {
raw: *const c_void,
inner: *mut T,
}
unsafe impl<T> Send for ResourceArc<T> where T: NifResourceTypeProvider {}
unsafe impl<T> Sync for ResourceArc<T> where T: NifResourceTypeProvider {}
impl<T> ResourceArc<T> where T: NifResourceTypeProvider {
pub fn new(data: T) -> Self {
let alloc_size = get_alloc_size_struct::<T>();
let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };
unsafe { ptr::write(aligned_mem, data) };
ResourceArc {
raw: mem_raw,
inner: aligned_mem,
}
}
fn from_term(term: NifTerm) -> Result<Self, NifError> {
let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
Some(res) => res,
None => return Err(NifError::BadArg),
};
unsafe { ::wrapper::resource::keep_resource(res_resource); }
let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
Ok(ResourceArc {
raw: res_resource,
inner: casted_ptr,
})
}
fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
}
fn as_c_arg(&mut self) -> *const c_void {
self.raw
}
fn inner(&self) -> &T {
unsafe { &*self.inner }
}
}
impl<T> Deref for ResourceArc<T> where T: NifResourceTypeProvider {
type Target = T;
fn deref(&self) -> &T {
self.inner()
}
}
impl<T> Clone for ResourceArc<T> where T: NifResourceTypeProvider {
fn clone(&self) -> Self {
unsafe { ::wrapper::resource::keep_resource(self.raw); }
ResourceArc {
raw: self.raw,
inner: self.inner,
}
}
}
impl<T> Drop for ResourceArc<T> where T: NifResourceTypeProvider {
fn drop(&mut self) {
unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
}
}
#[macro_export]
macro_rules! resource_struct_init {
($struct_name:ty, $env: ident) => {
{
static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;
let temp_struct_type =
match $crate::resource::open_struct_resource_type::<$struct_name>(
$env,
concat!(stringify!($struct_name), "\x00"),
$crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
) {
Some(inner) => inner,
None => {
println!("Failure in creating resource type");
return false;
}
};
unsafe { STRUCT_TYPE = Some(temp_struct_type) };
impl $crate::resource::NifResourceTypeProvider for $struct_name {
fn get_type() -> &'static $crate::resource::NifResourceType<Self> {
unsafe { &STRUCT_TYPE }.as_ref()
.expect("The resource type hasn't been inited. Did you remember to call the function where you used the `resource_struct_init!` macro?")
}
}
}
}
}