# must implement [`FfiType`]."
)]
#![cfg_attr(
not(feature = "closure"),
doc = " or non-void return type from Rust closures used with `Closure` must implement [`FfiType`]."
)]
extern crate alloc;
#[cfg(not(test))]
use alloc::vec::Vec;
use core::ptr::null_mut;
use crate::errors::{EmptyStructError, InvalidVariadicTypeError, LibffiError};
use crate::types::raw::LibffiType;
pub(crate) mod raw;
pub(crate) mod internal {
use super::Type;
#[cfg(not(test))]
use super::Vec;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct StructTypeVec(Vec<Type>);
impl StructTypeVec {
pub fn new(types: Vec<Type>) -> Option<Self> {
if types.is_empty() {
None
} else {
Some(Self(types))
}
}
pub fn new_from_slice(types: &[Type]) -> Option<Self> {
Self::new(types.to_vec())
}
pub unsafe fn new_unchecked(types: Vec<Type>) -> Self {
Self(types)
}
pub unsafe fn new_from_slice_unchecked(types: &[Type]) -> Self {
unsafe { Self::new_unchecked(types.to_vec()) }
}
pub fn as_vec(&self) -> &Vec<Type> {
&self.0
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type {
I8,
U8,
I16,
U16,
I32,
U32,
I64,
U64,
Isize,
Usize,
F32,
F64,
Pointer,
Struct(internal::StructTypeVec),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum VariadicType {
I32,
U32,
I64,
U64,
Isize,
Usize,
F64,
Pointer,
Struct(internal::StructTypeVec),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FfiTypeLayout {
pub align: usize,
pub size: usize,
}
impl Type {
pub fn create_struct(types: Vec<Type>) -> Result<Self, EmptyStructError> {
internal::StructTypeVec::new(types)
.map(Self::Struct)
.ok_or(EmptyStructError)
}
pub fn create_struct_from_slice(types: &[Type]) -> Result<Self, EmptyStructError> {
Self::create_struct(types.to_vec())
}
pub unsafe fn create_struct_unchecked(types: Vec<Type>) -> Self {
unsafe { Self::Struct(internal::StructTypeVec::new_unchecked(types)) }
}
pub unsafe fn create_struct_from_slice_unchecked(types: &[Type]) -> Self {
unsafe { Self::create_struct_unchecked(types.to_vec()) }
}
pub fn layout(&self) -> FfiTypeLayout {
let libffi_type = LibffiType::new(self);
let ffi_type_ptr = libffi_type.0.as_ptr();
if let Type::Struct(_) = self {
let status = unsafe {
libffi_sys::ffi_get_struct_offsets(
libffi_sys::ffi_abi_FFI_DEFAULT_ABI,
ffi_type_ptr,
null_mut(),
)
};
#[allow(
clippy::missing_panics_doc,
reason = "Internal sanity check only fails if there is an internal bug in this crate."
)]
{
assert!(
LibffiError::from_status(status).is_none(),
"Libffi returned the error code {status} from `ffi_get_struct_offsets`."
);
}
}
unsafe {
FfiTypeLayout {
align: (*ffi_type_ptr).alignment.into(),
size: (*ffi_type_ptr).size,
}
}
}
pub fn field_offsets(&self) -> Vec<usize> {
if let Type::Struct(children) = self {
let libffi_type = LibffiType::new(self);
let mut offsets = alloc::vec![0usize; children.as_vec().len()];
let status = unsafe {
libffi_sys::ffi_get_struct_offsets(
libffi_sys::ffi_abi_FFI_DEFAULT_ABI,
libffi_type.0.as_ptr(),
offsets.as_mut_ptr().cast(),
)
};
#[allow(
clippy::missing_panics_doc,
reason = "Internal sanity check only fails if there is an internal bug in this crate."
)]
{
assert!(
LibffiError::from_status(status).is_none(),
"Libffi returned the error code {status} from `ffi_get_struct_offsets`."
);
}
offsets
} else {
Vec::new()
}
}
}
impl VariadicType {
pub fn create_struct(types: Vec<Type>) -> Result<Self, EmptyStructError> {
internal::StructTypeVec::new(types)
.map(Self::Struct)
.ok_or(EmptyStructError)
}
pub fn create_struct_from_slice(types: &[Type]) -> Result<Self, EmptyStructError> {
Self::create_struct(types.to_vec())
}
pub unsafe fn create_struct_unchecked(types: Vec<Type>) -> Self {
unsafe { Self::Struct(internal::StructTypeVec::new_unchecked(types)) }
}
pub unsafe fn create_struct_from_slice_unchecked(types: &[Type]) -> Self {
unsafe { Self::create_struct_unchecked(types.to_vec()) }
}
pub fn to_type(&self) -> Type {
match &self {
VariadicType::I32 => Type::I32,
VariadicType::U32 => Type::U32,
VariadicType::I64 => Type::I64,
VariadicType::U64 => Type::U64,
VariadicType::Isize => Type::Isize,
VariadicType::Usize => Type::Usize,
VariadicType::F64 => Type::F64,
VariadicType::Pointer => Type::Pointer,
VariadicType::Struct(types) => Type::Struct(types.clone()),
}
}
}
impl TryFrom<Type> for VariadicType {
type Error = InvalidVariadicTypeError;
fn try_from(value: Type) -> Result<Self, Self::Error> {
match value {
Type::I32 => Ok(Self::I32),
Type::U32 => Ok(Self::U32),
Type::I64 => Ok(Self::I64),
Type::U64 => Ok(Self::U64),
Type::Isize => Ok(Self::Isize),
Type::Usize => Ok(Self::Usize),
Type::F64 => Ok(Self::F64),
Type::Pointer => Ok(Self::Pointer),
Type::Struct(types) => Ok(Self::Struct(types)),
Type::I8 | Type::U8 | Type::I16 | Type::U16 | Type::F32 => {
Err(InvalidVariadicTypeError(value))
}
}
}
}
impl From<VariadicType> for Type {
fn from(value: VariadicType) -> Self {
value.to_type()
}
}
pub unsafe trait FfiType: Copy {
fn ffi_type() -> Type;
}
unsafe impl FfiType for i8 {
fn ffi_type() -> Type {
Type::I8
}
}
unsafe impl FfiType for u8 {
fn ffi_type() -> Type {
Type::U8
}
}
unsafe impl FfiType for i16 {
fn ffi_type() -> Type {
Type::I16
}
}
unsafe impl FfiType for u16 {
fn ffi_type() -> Type {
Type::U16
}
}
unsafe impl FfiType for i32 {
fn ffi_type() -> Type {
Type::I32
}
}
unsafe impl FfiType for u32 {
fn ffi_type() -> Type {
Type::U32
}
}
unsafe impl FfiType for i64 {
fn ffi_type() -> Type {
Type::I64
}
}
unsafe impl FfiType for u64 {
fn ffi_type() -> Type {
Type::U64
}
}
unsafe impl FfiType for isize {
fn ffi_type() -> Type {
Type::Isize
}
}
unsafe impl FfiType for usize {
fn ffi_type() -> Type {
Type::Usize
}
}
unsafe impl FfiType for f32 {
fn ffi_type() -> Type {
Type::F32
}
}
unsafe impl FfiType for f64 {
fn ffi_type() -> Type {
Type::F64
}
}
unsafe impl<T> FfiType for *const T {
fn ffi_type() -> Type {
Type::Pointer
}
}
unsafe impl<T> FfiType for *mut T {
fn ffi_type() -> Type {
Type::Pointer
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use core::ffi::c_void;
use core::mem::offset_of;
use super::*;
#[derive(Copy, Clone)]
#[repr(C)]
struct TestStruct(i8, u16, i32, u64, isize, f64, *const c_void, SubStruct);
unsafe impl FfiType for TestStruct {
fn ffi_type() -> Type {
unsafe {
Type::create_struct_unchecked(vec![
Type::I8,
Type::U16,
Type::I32,
Type::U64,
Type::Isize,
Type::F64,
Type::Pointer,
SubStruct::ffi_type(),
])
}
}
}
#[derive(Copy, Clone)]
#[repr(C)]
struct SubStruct(f32, i16);
unsafe impl FfiType for SubStruct {
fn ffi_type() -> Type {
unsafe { Type::create_struct_unchecked(vec![Type::F32, Type::I16]) }
}
}
#[test]
fn test_struct_size_alignment_offsets() {
let sub_struct_type = SubStruct::ffi_type();
let sub_struct_layout = sub_struct_type.layout();
assert_eq!(size_of::<SubStruct>(), sub_struct_layout.size);
assert_eq!(align_of::<SubStruct>(), sub_struct_layout.align);
let sub_struct_offsets = sub_struct_type.field_offsets();
assert_eq!(offset_of!(SubStruct, 0), sub_struct_offsets[0]);
assert_eq!(offset_of!(SubStruct, 1), sub_struct_offsets[1]);
let test_struct_type = TestStruct::ffi_type();
let test_struct_layout = test_struct_type.layout();
assert_eq!(size_of::<TestStruct>(), test_struct_layout.size);
assert_eq!(align_of::<TestStruct>(), test_struct_layout.align);
let test_struct_offsets = test_struct_type.field_offsets();
assert_eq!(offset_of!(TestStruct, 0), test_struct_offsets[0]);
assert_eq!(offset_of!(TestStruct, 1), test_struct_offsets[1]);
assert_eq!(offset_of!(TestStruct, 2), test_struct_offsets[2]);
assert_eq!(offset_of!(TestStruct, 3), test_struct_offsets[3]);
assert_eq!(offset_of!(TestStruct, 4), test_struct_offsets[4]);
assert_eq!(offset_of!(TestStruct, 5), test_struct_offsets[5]);
assert_eq!(offset_of!(TestStruct, 6), test_struct_offsets[6]);
assert_eq!(offset_of!(TestStruct, 7), test_struct_offsets[7]);
}
#[test]
fn verify_ffi_type_size_and_alignment() {
macro_rules! verify_type {
($t:ty) => {
let libffi_type_layout = <$t>::ffi_type().layout();
assert_eq!(
size_of::<$t>(),
libffi_type_layout.size,
"The size of type `{}` is not the same in libffi.",
core::any::type_name::<$t>()
);
assert_eq!(
align_of::<$t>(),
libffi_type_layout.align,
"The alignment of type `{}` is not the same in libffi.",
core::any::type_name::<$t>()
);
};
}
verify_type!(i8);
verify_type!(u8);
verify_type!(i16);
verify_type!(u16);
verify_type!(i32);
verify_type!(u32);
verify_type!(i64);
verify_type!(u64);
verify_type!(isize);
verify_type!(usize);
verify_type!(f32);
verify_type!(f64);
verify_type!(*const c_void);
verify_type!(*mut c_void);
}
#[test]
fn variadic_type_conversions_accept_supported_types() {
let supported_types = [
Type::I32,
Type::U32,
Type::I64,
Type::U64,
Type::Isize,
Type::Usize,
Type::F64,
Type::Pointer,
Type::create_struct(vec![Type::I8]).unwrap(),
];
for ty in supported_types {
let variadic_type = VariadicType::try_from(ty.clone()).unwrap();
assert_eq!(Type::from(variadic_type), ty);
}
}
#[test]
fn variadic_type_conversions_reject_unsupported_types() {
let unsupported_types = [Type::I8, Type::U8, Type::I16, Type::U16, Type::F32];
for ty in unsupported_types {
assert_eq!(
VariadicType::try_from(ty.clone()),
Err(InvalidVariadicTypeError(ty))
);
}
}
#[test]
fn struct_constructors_disallow_empty_structs() {
assert_eq!(Type::create_struct(vec![]), Err(EmptyStructError));
assert_eq!(VariadicType::create_struct(vec![]), Err(EmptyStructError));
assert_eq!(Type::create_struct_from_slice(&[]), Err(EmptyStructError));
assert_eq!(
VariadicType::create_struct_from_slice(&[]),
Err(EmptyStructError)
);
}
}