use core::any::Any;
use core::ffi::c_void;
use core::marker::PhantomData;
use crate::low;
pub use crate::low::{ffi_abi as FfiAbi, ffi_abi_FFI_DEFAULT_ABI, Callback, CallbackMut, CodePtr};
mod util;
mod types;
pub use types::Type;
mod builder;
pub use builder::Builder;
#[derive(Clone, Debug)]
#[repr(C)]
pub struct Arg(*mut c_void);
impl Arg {
pub fn new<T>(r: &T) -> Self {
Arg(r as *const T as *mut c_void)
}
}
pub fn arg<T>(r: &T) -> Arg {
Arg::new(r)
}
#[derive(Debug)]
pub struct Cif {
cif: low::ffi_cif,
args: types::TypeArray,
result: Type,
}
impl Clone for Cif {
fn clone(&self) -> Self {
let mut copy = Cif {
cif: self.cif,
args: self.args.clone(),
result: self.result.clone(),
};
copy.cif.arg_types = copy.args.as_raw_ptr();
copy.cif.rtype = copy.result.as_raw_ptr();
copy
}
}
impl Cif {
pub fn new<I>(args: I, result: Type) -> Self
where
I: IntoIterator<Item = Type>,
I::IntoIter: ExactSizeIterator<Item = Type>,
{
let args = args.into_iter();
let nargs = args.len();
let args = types::TypeArray::new(args);
let mut cif = low::ffi_cif::default();
unsafe {
low::prep_cif(
&mut cif,
low::ffi_abi_FFI_DEFAULT_ABI,
nargs,
result.as_raw_ptr(),
args.as_raw_ptr(),
)
}
.expect("low::prep_cif");
Cif { cif, args, result }
}
pub fn new_variadic<I>(args: I, fixed_args: usize, result: Type) -> Self
where
I: IntoIterator<Item = Type>,
I::IntoIter: ExactSizeIterator<Item = Type>,
{
let args = args.into_iter();
let nargs = args.len();
let args = types::TypeArray::new(args);
let mut cif: low::ffi_cif = Default::default();
unsafe {
low::prep_cif_var(
&mut cif,
low::ffi_abi_FFI_DEFAULT_ABI,
fixed_args,
nargs,
result.as_raw_ptr(),
args.as_raw_ptr(),
)
}
.expect("low::prep_cif_var");
Cif { cif, args, result }
}
pub unsafe fn call<R>(&self, fun: CodePtr, args: &[Arg]) -> R {
assert_eq!(
self.cif.nargs as usize,
args.len(),
"Cif::call: passed wrong number of arguments"
);
low::call::<R>(
&self.cif as *const _ as *mut _,
fun,
args.as_ptr() as *mut *mut c_void,
)
}
pub fn set_abi(&mut self, abi: FfiAbi) {
self.cif.abi = abi;
}
pub fn as_raw_ptr(&self) -> *mut low::ffi_cif {
&self.cif as *const _ as *mut _
}
}
#[derive(Debug)]
pub struct Closure<'a> {
_cif: Box<Cif>,
alloc: *mut low::ffi_closure,
code: CodePtr,
_marker: PhantomData<&'a ()>,
}
impl Drop for Closure<'_> {
fn drop(&mut self) {
unsafe {
low::closure_free(self.alloc);
}
}
}
impl<'a> Closure<'a> {
pub fn new<U, R>(cif: Cif, callback: Callback<U, R>, userdata: &'a U) -> Self {
let cif = Box::new(cif);
let (alloc, code) = low::closure_alloc();
unsafe {
low::prep_closure(
alloc,
cif.as_raw_ptr(),
callback,
userdata as *const U,
code,
)
.unwrap();
}
Closure {
_cif: cif,
alloc,
code,
_marker: PhantomData,
}
}
pub fn new_mut<U, R>(cif: Cif, callback: CallbackMut<U, R>, userdata: &'a mut U) -> Self {
let cif = Box::new(cif);
let (alloc, code) = low::closure_alloc();
unsafe {
low::prep_closure_mut(alloc, cif.as_raw_ptr(), callback, userdata as *mut U, code)
.unwrap();
}
Closure {
_cif: cif,
alloc,
code,
_marker: PhantomData,
}
}
pub fn code_ptr(&self) -> &unsafe extern "C" fn() {
self.code.as_fun()
}
pub unsafe fn instantiate_code_ptr<T>(&self) -> &T {
self.code.as_any_ref_()
}
}
pub type CallbackOnce<U, R> = CallbackMut<Option<U>, R>;
#[derive(Debug)]
pub struct ClosureOnce {
alloc: *mut low::ffi_closure,
code: CodePtr,
_cif: Box<Cif>,
_userdata: Box<dyn Any>,
}
impl Drop for ClosureOnce {
fn drop(&mut self) {
unsafe {
low::closure_free(self.alloc);
}
}
}
impl ClosureOnce {
pub fn new<U: Any, R>(cif: Cif, callback: CallbackOnce<U, R>, userdata: U) -> Self {
let _cif = Box::new(cif);
let _userdata = Box::new(Some(userdata)) as Box<dyn Any>;
let (alloc, code) = low::closure_alloc();
assert!(!alloc.is_null(), "closure_alloc: returned null");
{
let borrow = _userdata.downcast_ref::<Option<U>>().unwrap();
unsafe {
low::prep_closure_mut(
alloc,
_cif.as_raw_ptr(),
callback,
borrow as *const _ as *mut _,
code,
)
.unwrap();
}
}
ClosureOnce {
alloc,
code,
_cif,
_userdata,
}
}
pub fn code_ptr(&self) -> &unsafe extern "C" fn() {
self.code.as_fun()
}
pub unsafe fn instantiate_code_ptr<T>(&self) -> &T {
self.code.as_any_ref_()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::low;
use core::ffi::{c_char, c_void};
use std::ffi::CStr;
#[test]
fn call() {
let cif = Cif::new(vec![Type::i64(), Type::i64()], Type::i64());
let f = |m: i64, n: i64| -> i64 {
unsafe { cif.call(CodePtr(add_it as *mut c_void), &[arg(&m), arg(&n)]) }
};
assert_eq!(12, f(5, 7));
assert_eq!(13, f(6, 7));
assert_eq!(15, f(8, 7));
}
extern "C" fn add_it(n: i64, m: i64) -> i64 {
n + m
}
#[test]
fn closure() {
let cif = Cif::new(vec![Type::u64()], Type::u64());
let env: u64 = 5;
let closure = Closure::new(cif, callback, &env);
let fun: &extern "C" fn(u64) -> u64 = unsafe { closure.instantiate_code_ptr() };
assert_eq!(11, fun(6));
assert_eq!(12, fun(7));
}
unsafe extern "C" fn callback(
_cif: &low::ffi_cif,
result: &mut u64,
args: *const *const c_void,
userdata: &u64,
) {
let args = args as *const &u64;
*result = **args + *userdata;
}
#[test]
fn rust_lambda() {
let cif = Cif::new(vec![Type::u64(), Type::u64()], Type::u64());
let env = |x: u64, y: u64| x + y;
let closure = Closure::new(cif, callback2, &env);
let fun: &extern "C" fn(u64, u64) -> u64 = unsafe { closure.instantiate_code_ptr() };
assert_eq!(11, fun(5, 6));
}
unsafe extern "C" fn callback2<F: Fn(u64, u64) -> u64>(
_cif: &low::ffi_cif,
result: &mut u64,
args: *const *const c_void,
userdata: &F,
) {
let args = args as *const &u64;
let arg1 = **args.offset(0);
let arg2 = **args.offset(1);
*result = userdata(arg1, arg2);
}
#[test]
fn clone_cif() {
let cif = Cif::new(
vec![
Type::structure(vec![
Type::structure(vec![Type::u64(), Type::u8(), Type::f64()]),
Type::i8(),
Type::i64(),
]),
Type::u64(),
],
Type::u64(),
);
let clone_cif = cif.clone();
unsafe {
let args = std::slice::from_raw_parts(cif.cif.arg_types, cif.cif.nargs as usize);
let struct_arg = args
.first()
.expect("CIF arguments slice was empty")
.as_ref()
.expect("CIF first argument was null");
let struct_size = struct_arg.size;
let struct_parts = std::slice::from_raw_parts(struct_arg.elements, 1);
let substruct_size = struct_parts
.first()
.expect("CIF struct argument's elements slice was empty")
.as_ref()
.expect("CIF struct argument's first element was null")
.size;
let clone_args =
std::slice::from_raw_parts(clone_cif.cif.arg_types, clone_cif.cif.nargs as usize);
let clone_struct_arg = clone_args
.first()
.expect("CIF arguments slice was empty")
.as_ref()
.expect("CIF first argument was null");
let clone_struct_size = clone_struct_arg.size;
let clone_struct_parts = std::slice::from_raw_parts(clone_struct_arg.elements, 1);
let clone_substruct_size = clone_struct_parts
.first()
.expect("Cloned CIF struct argument's elements slice was empty")
.as_ref()
.expect("Cloned CIF struct argument's first element was null")
.size;
assert_eq!(struct_size, clone_struct_size);
assert_eq!(substruct_size, clone_substruct_size);
}
}
#[test]
fn call_snprintf() {
extern "C" {
fn snprintf(s: *mut c_char, n: usize, format: *const c_char, ...) -> i32;
}
let mut output_buffer = [0u8; 50];
let expected = c"num: 123, pi: 3.14, This is a &Cstr";
let format_str = c"num: %d, pi: %.2f, %s";
let num = 123;
let pi = core::f64::consts::PI;
let cstr = c"This is a &Cstr";
let cif = Cif::new_variadic(
[
Type::pointer(),
Type::usize(),
Type::pointer(),
Type::i32(),
Type::f64(),
Type::pointer(),
],
3,
Type::i32(),
);
let result: i32 = unsafe {
cif.call(
CodePtr(snprintf as *mut _),
&[
arg(&output_buffer.as_mut_ptr()),
arg(&output_buffer.len()),
arg(&format_str.as_ptr()),
arg(&num),
arg(&pi),
arg(&cstr.as_ptr()),
],
)
};
let output_cstr = CStr::from_bytes_until_nul(&output_buffer).unwrap();
assert_eq!(result, expected.to_bytes().len().try_into().unwrap());
assert_eq!(expected, output_cstr);
}
}