|
| 1 | +use libffi::low::CodePtr; |
| 2 | +use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType}; |
| 3 | + |
| 4 | +/// Perform the actual FFI call. |
| 5 | +/// |
| 6 | +/// SAFETY: The `FfiArg`s passed must have been correctly instantiated (i.e. their |
| 7 | +/// type layout must match the data they point to), and the safety invariants of |
| 8 | +/// the foreign function being called must be upheld (if any). |
| 9 | +pub unsafe fn call<'a, R: libffi::high::CType>(fun: CodePtr, args: Vec<FfiArg<'a>>) -> R { |
| 10 | + let mut arg_tys = vec![]; |
| 11 | + let mut arg_ptrs = vec![]; |
| 12 | + for arg in args { |
| 13 | + arg_tys.push(arg.ty); |
| 14 | + arg_ptrs.push(arg.ptr) |
| 15 | + } |
| 16 | + let cif = Cif::new(arg_tys, R::reify().into_middle()); |
| 17 | + unsafe { cif.call(fun, &arg_ptrs) } |
| 18 | +} |
| 19 | + |
| 20 | +/// A wrapper type for `libffi::middle::Type` which also holds a pointer to the data. |
| 21 | +pub struct FfiArg<'a> { |
| 22 | + /// The type layout information for the pointed-to data. |
| 23 | + ty: FfiType, |
| 24 | + /// A pointer to the data described in `ty`. |
| 25 | + ptr: ArgPtr, |
| 26 | + /// Lifetime of the actual pointed-to data. |
| 27 | + _p: std::marker::PhantomData<&'a [u8]>, |
| 28 | +} |
| 29 | + |
| 30 | +impl<'a> FfiArg<'a> { |
| 31 | + fn new(ty: FfiType, ptr: ArgPtr) -> Self { |
| 32 | + Self { ty, ptr, _p: std::marker::PhantomData } |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +/// An owning form of `FfiArg`. |
| 37 | +/// We introduce this enum instead of just calling `Arg::new` and storing a list of |
| 38 | +/// `libffi::middle::Arg` directly, because the `libffi::middle::Arg` just wraps a reference to |
| 39 | +/// the value it represents and we need to store a copy of the value, and pass a reference to |
| 40 | +/// this copy to C instead. |
| 41 | +#[derive(Debug, Clone)] |
| 42 | +pub enum CArg { |
| 43 | + /// Primitive type. |
| 44 | + Primitive(CPrimitive), |
| 45 | + /// Struct with its computed type layout and bytes. |
| 46 | + Struct(FfiType, Box<[u8]>), |
| 47 | +} |
| 48 | + |
| 49 | +impl CArg { |
| 50 | + /// Convert a `CArg` to the required FFI argument type. |
| 51 | + pub fn arg_downcast(&self) -> FfiArg<'_> { |
| 52 | + match self { |
| 53 | + CArg::Primitive(cprim) => cprim.arg_downcast(), |
| 54 | + CArg::Struct(cstruct, items) => FfiArg::new(cstruct.clone(), ArgPtr::new(&items[0])), |
| 55 | + } |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +impl From<CPrimitive> for CArg { |
| 60 | + fn from(prim: CPrimitive) -> Self { |
| 61 | + Self::Primitive(prim) |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +#[derive(Debug, Clone)] |
| 66 | +/// Enum of supported primitive arguments to external C functions. |
| 67 | +pub enum CPrimitive { |
| 68 | + /// 8-bit signed integer. |
| 69 | + Int8(i8), |
| 70 | + /// 16-bit signed integer. |
| 71 | + Int16(i16), |
| 72 | + /// 32-bit signed integer. |
| 73 | + Int32(i32), |
| 74 | + /// 64-bit signed integer. |
| 75 | + Int64(i64), |
| 76 | + /// isize. |
| 77 | + ISize(isize), |
| 78 | + /// 8-bit unsigned integer. |
| 79 | + UInt8(u8), |
| 80 | + /// 16-bit unsigned integer. |
| 81 | + UInt16(u16), |
| 82 | + /// 32-bit unsigned integer. |
| 83 | + UInt32(u32), |
| 84 | + /// 64-bit unsigned integer. |
| 85 | + UInt64(u64), |
| 86 | + /// usize. |
| 87 | + USize(usize), |
| 88 | + /// Raw pointer, stored as C's `void*`. |
| 89 | + RawPtr(*mut std::ffi::c_void), |
| 90 | +} |
| 91 | + |
| 92 | +impl CPrimitive { |
| 93 | + /// Convert a primitive to the required FFI argument type. |
| 94 | + fn arg_downcast(&self) -> FfiArg<'_> { |
| 95 | + match self { |
| 96 | + CPrimitive::Int8(i) => FfiArg::new(FfiType::i8(), ArgPtr::new(i)), |
| 97 | + CPrimitive::Int16(i) => FfiArg::new(FfiType::i16(), ArgPtr::new(i)), |
| 98 | + CPrimitive::Int32(i) => FfiArg::new(FfiType::i32(), ArgPtr::new(i)), |
| 99 | + CPrimitive::Int64(i) => FfiArg::new(FfiType::i64(), ArgPtr::new(i)), |
| 100 | + CPrimitive::ISize(i) => FfiArg::new(FfiType::isize(), ArgPtr::new(i)), |
| 101 | + CPrimitive::UInt8(i) => FfiArg::new(FfiType::u8(), ArgPtr::new(i)), |
| 102 | + CPrimitive::UInt16(i) => FfiArg::new(FfiType::u16(), ArgPtr::new(i)), |
| 103 | + CPrimitive::UInt32(i) => FfiArg::new(FfiType::u32(), ArgPtr::new(i)), |
| 104 | + CPrimitive::UInt64(i) => FfiArg::new(FfiType::u64(), ArgPtr::new(i)), |
| 105 | + CPrimitive::USize(i) => FfiArg::new(FfiType::usize(), ArgPtr::new(i)), |
| 106 | + CPrimitive::RawPtr(i) => FfiArg::new(FfiType::pointer(), ArgPtr::new(i)), |
| 107 | + } |
| 108 | + } |
| 109 | +} |
0 commit comments