/// The foreign function interfaces for the dynamic data mod dynamic_data; /// The foreign function interfaces for that static data mod static_data; /// Helper type for clearer functions. type OwnedPtr = *mut T; /// Helper type for clearer functions. type BorrowedPtr = *const T; /// Generates a basic getter foreign function interface. macro_rules! ffi_arc_getter { ( $type:ty, $func:ident, $returns: ty ) => { paste::paste! { #[no_mangle] extern "C" fn [< $type:snake _ $func >](ptr: ExternPointer>) -> $returns { ptr.as_ref().$func() } } }; } /// Generates a basic getter foreign function interface. macro_rules! ffi_arc_dyn_getter { ( $type:ty, $func:ident, $returns: ty ) => { paste::paste! { #[no_mangle] extern "C" fn [< $type:snake _ $func >](ptr: ExternPointer>) -> $returns { ptr.as_ref().$func() } } }; } /// Generates a basic getter foreign function interface where the return type is a [`crate::StringKey`]. macro_rules! ffi_arc_stringkey_getter { ( $type:ty, $func:ident ) => { paste::paste! { #[no_mangle] extern "C" fn [< $type:lower _ $func >](ptr: ExternPointer>) -> OwnedPtr { std::ffi::CString::new(ptr.as_ref().$func().str()).unwrap().into_raw() } } }; } /// Generates a foreign function interface for a vec. This generates a length function, and a getter. macro_rules! ffi_vec_value_getters { ( $name:ident, $type:ty, $func:ident, $returns: ty ) => { paste::paste! { #[no_mangle] extern "C" fn [< $name:lower _ $func _length>](ptr: ExternPointer>) -> usize { ptr.as_ref().$func().len() } #[no_mangle] extern "C" fn [< $name:lower _ $func _get>](ptr: ExternPointer>, index: usize) -> $returns { *ptr.as_ref().$func().get(index).unwrap() } } }; } /// Generates a foreign function interface for a vec of [`crate::StringKey`]. This generates a /// length function, and a getter. macro_rules! ffi_vec_stringkey_getters { ( $type:ty, $func:ident ) => { paste::paste! { #[no_mangle] extern "C" fn [< $type:lower _ $func _length>](ptr: ExternPointer>) -> usize { ptr.as_ref().$func().len() } #[no_mangle] extern "C" fn [< $type:lower _ $func _get>](ptr: ExternPointer>, index: usize) -> OwnedPtr { CString::new(ptr.as_ref().$func().get(index).unwrap().str()).unwrap().into_raw() } } }; } use crate::{ValueIdentifiable, ValueIdentifier}; pub(self) use ffi_arc_dyn_getter; pub(self) use ffi_arc_getter; pub(self) use ffi_arc_stringkey_getter; pub(self) use ffi_vec_stringkey_getters; pub(self) use ffi_vec_value_getters; use std::ffi::{c_char, CString}; use std::mem::transmute; use std::sync::Arc; /// Helper utility class to wrap a pointer for extern functions. #[repr(C)] pub(self) struct ExternPointer { /// The wrapped pointer. ptr: *mut T, } impl ExternPointer { /// Get the internal pointer as reference. #[allow(clippy::panic)] // We currently allow this as these should never be null, but we might want to change this in the future. pub(self) fn as_ref(&self) -> &T { unsafe { self.ptr .as_ref() .unwrap_or_else(|| panic!("Given pointer of type '{}' was null", std::any::type_name::())) } } /// Get the internal pointer as mutable reference. #[allow(clippy::panic)] // We currently allow this as these should never be null, but we might want to change this in the future. pub(self) fn as_mut(&mut self) -> &mut T { unsafe { self.ptr .as_mut() .unwrap_or_else(|| panic!("Given pointer of type '{}' was null", std::any::type_name::())) } } } /// Helper utility class to give both the pointer and identifier to a FFI. #[repr(C)] pub(self) struct IdentifiablePointer { /// The wrapped pointer. pub ptr: *const T, /// The identifier of the pointer. pub id: usize, } impl Clone for IdentifiablePointer { fn clone(&self) -> Self { Self { id: self.id, ptr: self.ptr, } } } impl Copy for IdentifiablePointer {} impl IdentifiablePointer { /// Creates a new IdentifiablePointer. pub(self) fn new(ptr: *const T, id: ValueIdentifier) -> Self { unsafe { Self { ptr, id: transmute(id) } } } } impl From<*mut T> for ExternPointer { fn from(ptr: *mut T) -> Self { ExternPointer { ptr } } } impl From> for IdentifiablePointer> { fn from(v: Arc) -> Self { let id = v.value_identifier().value(); Self { ptr: Box::into_raw(Box::new(v)), id, } } } impl From>> for IdentifiablePointer> { fn from(v: Option>) -> Self { if let Some(v) = v { let id = unsafe { transmute(v.value_identifier()) }; Self { ptr: Box::into_raw(Box::new(v)), id, } } else { IdentifiablePointer::none() } } } impl From> for IdentifiablePointer> { fn from(v: Box) -> Self { let id = unsafe { transmute(v.value_identifier()) }; Self { ptr: Box::into_raw(Box::new(v)), id, } } } impl From<&Box> for IdentifiablePointer> { fn from(v: &Box) -> Self { let id = unsafe { transmute(v.value_identifier()) }; Self { ptr: v as *const Box, id, } } } impl From>> for IdentifiablePointer> { fn from(v: Option>) -> Self { if let Some(v) = v { let id = unsafe { transmute(v.value_identifier()) }; Self { ptr: Box::into_raw(Box::new(v)), id, } } else { IdentifiablePointer::none() } } } impl From<&Option>> for IdentifiablePointer> { fn from(v: &Option>) -> Self { if let Some(v) = v { let id = unsafe { transmute(v.value_identifier()) }; unsafe { Self { ptr: *Box::into_raw(Box::new(v as *const Box)), id, } } } else { IdentifiablePointer::none() } } } impl From> for IdentifiablePointer { fn from(v: Box) -> Self { let id = unsafe { transmute(v.value_identifier()) }; Self { ptr: Box::into_raw(v), id, } } } impl From<*const T> for IdentifiablePointer { #[allow(clippy::unwrap_used)] // We currently allow this as these should never be null, but we might want to change this in the future. fn from(v: *const T) -> Self { let id = unsafe { transmute(v.as_ref().unwrap().value_identifier()) }; Self { ptr: v, id } } } impl IdentifiablePointer { /// Returns an identifiable pointer with null as pointer, and 0 as identifier. pub fn none() -> Self { Self { ptr: std::ptr::null(), id: Default::default(), } } } /// Helper utility class to give either the data or an error to a FFI. #[repr(C)] union ResultUnion { /// If the result is ok, this contains the value. ok: T, /// If the result is an error, this contains the error message. err: OwnedPtr, } /// The result of a FFI call that can either be an error or a value. #[repr(C)] pub struct NativeResult { /// If the result is ok, this is 1, otherwise 0. ok: u8, /// The value or error. value: ResultUnion, } impl NativeResult { /// Creates a new NativeResult with the given value. pub fn ok(value: T) -> Self { Self { ok: 1, value: ResultUnion { ok: value }, } } /// Creates a new NativeResult with the given error. #[allow(clippy::unwrap_used)] // We know for certain this is not empty. pub fn err(err: anyhow_ext::Error) -> Self { Self { ok: 0, value: ResultUnion { err: CString::new(err.to_string()).unwrap().into_raw(), }, } } /// Creates a new NativeResult with the given error string. #[allow(clippy::unwrap_used)] // We know for certain this is not empty. pub fn err_from_str(err: &str) -> Self { Self { ok: 0, value: ResultUnion { err: CString::new(err).unwrap().into_raw(), }, } } } impl From> for NativeResult { fn from(value: anyhow::Result) -> Self { match value { Ok(v) => Self::ok(v), Err(e) => Self::err(e), } } } impl From for NativeResult { fn from(value: T) -> Self { Self::ok(value) } }