#![allow(clippy::ptr_as_ptr, clippy::borrow_as_ptr, clippy::cast_ptr_alignment, clippy::ref_as_ptr)]
use interoptopus::ffi;
use interoptopus::pattern::guard::Version;
use std::mem;
const PTR_SIZE: usize = mem::size_of::<*const u8>();
#[test]
fn api_version() {
assert_eq!(mem::size_of::<Version>(), mem::size_of::<u64>());
assert_eq!(mem::align_of::<Version>(), mem::align_of::<u64>());
let v = Version::new(0x1234_5678_9ABC_DEF0);
let raw = unsafe { *(&v as *const Version as *const u64) };
assert_eq!(raw, 0x1234_5678_9ABC_DEF0);
}
#[test]
fn bool() {
assert_eq!(mem::size_of::<ffi::Bool>(), 1);
assert_eq!(mem::align_of::<ffi::Bool>(), 1);
let raw_t = unsafe { *(&ffi::Bool::TRUE as *const ffi::Bool as *const u8) };
let raw_f = unsafe { *(&ffi::Bool::FALSE as *const ffi::Bool as *const u8) };
assert_eq!(raw_t, 1);
assert_eq!(raw_f, 0);
}
#[test]
fn cchar() {
assert_eq!(mem::size_of::<ffi::CChar>(), 1);
assert_eq!(mem::align_of::<ffi::CChar>(), 1);
}
#[test]
fn cstr_ptr() {
assert_eq!(mem::size_of::<ffi::CStrPtr>(), PTR_SIZE);
assert_eq!(mem::align_of::<ffi::CStrPtr>(), PTR_SIZE);
let cstr = c"hello";
let ffi_cstr = ffi::CStrPtr::from_cstr(cstr);
let raw_ptr = unsafe { *(&ffi_cstr as *const ffi::CStrPtr as *const *const u8) };
assert_eq!(raw_ptr, cstr.as_ptr() as *const u8);
}
#[test]
fn utf8_string() {
assert_eq!(mem::size_of::<ffi::String>(), PTR_SIZE + 8 + 8);
assert_eq!(mem::align_of::<ffi::String>(), PTR_SIZE);
let s = ffi::String::from_string("hello".to_string());
let raw = &s as *const ffi::String as *const u8;
unsafe {
let ptr = *(raw as *const *const u8);
let len = *(raw.add(PTR_SIZE) as *const u64);
let cap = *(raw.add(PTR_SIZE + 8) as *const u64);
assert!(!ptr.is_null());
assert_eq!(len, 5);
assert!(cap >= 5);
}
let _ = s.into_string();
}
#[test]
fn slice() {
assert_eq!(mem::size_of::<ffi::Slice<u8>>(), PTR_SIZE + 8);
assert_eq!(mem::align_of::<ffi::Slice<u8>>(), PTR_SIZE);
assert_eq!(mem::size_of::<ffi::Slice<u64>>(), PTR_SIZE + 8);
let data: [u32; 3] = [10, 20, 30];
let s = ffi::Slice::from(&data[..]);
let raw = &s as *const ffi::Slice<u32> as *const u8;
unsafe {
let ptr = *(raw as *const *const u32);
let len = *(raw.add(PTR_SIZE) as *const u64);
assert_eq!(ptr, data.as_ptr());
assert_eq!(len, 3);
}
}
#[test]
fn slice_mut() {
assert_eq!(mem::size_of::<ffi::SliceMut<u8>>(), PTR_SIZE + 8);
assert_eq!(mem::align_of::<ffi::SliceMut<u8>>(), PTR_SIZE);
let mut data: [u32; 4] = [1, 2, 3, 4];
let expected_ptr = data.as_mut_ptr();
let s = ffi::SliceMut::from(&mut data[..]);
let raw = &s as *const ffi::SliceMut<u32> as *const u8;
unsafe {
let ptr = *(raw as *const *mut u32);
let len = *(raw.add(PTR_SIZE) as *const u64);
assert_eq!(ptr, expected_ptr);
assert_eq!(len, 4);
}
}
#[test]
fn vec() {
assert_eq!(mem::size_of::<ffi::Vec<u8>>(), PTR_SIZE + 8 + 8);
assert_eq!(mem::align_of::<ffi::Vec<u8>>(), PTR_SIZE);
let v = ffi::Vec::from_vec(vec![1u32, 2, 3]);
let raw = &v as *const ffi::Vec<u32> as *const u8;
unsafe {
let ptr = *(raw as *const *const u32);
let len = *(raw.add(PTR_SIZE) as *const u64);
let cap = *(raw.add(PTR_SIZE + 8) as *const u64);
assert!(!ptr.is_null());
assert_eq!(len, 3);
assert!(cap >= 3);
}
let _ = v.into_vec();
}
#[test]
fn option() {
assert_eq!(mem::align_of::<ffi::Option<u8>>(), 4);
assert_eq!(mem::size_of::<ffi::Option<u8>>(), 8);
assert_eq!(mem::size_of::<ffi::Option<u64>>(), 16);
assert_eq!(mem::align_of::<ffi::Option<u64>>(), 8);
let some: ffi::Option<u32> = ffi::Some(0xDEAD_BEEF);
let raw = unsafe { &*(&some as *const ffi::Option<u32> as *const [u32; 2]) };
assert_eq!(raw[0], 0, "Some discriminant must be 0");
assert_eq!(raw[1], 0xDEAD_BEEF);
let none: ffi::Option<u32> = ffi::None;
let tag = unsafe { *(&none as *const ffi::Option<u32> as *const u32) };
assert_eq!(tag, 1, "None discriminant must be 1");
}
#[test]
fn named_callback() {
use interoptopus::callback;
callback!(LayoutCallback(x: i32) -> i32);
assert_eq!(mem::size_of::<LayoutCallback>(), 3 * PTR_SIZE);
assert_eq!(mem::align_of::<LayoutCallback>(), PTR_SIZE);
let cb = LayoutCallback::from_fn(|x| x + 1);
unsafe {
let callback_ptr = *std::ptr::addr_of!(cb.callback).cast::<*const u8>();
assert!(!callback_ptr.is_null(), "callback must be non-null");
let data_ptr = *std::ptr::addr_of!(cb.data).cast::<*const u8>();
assert!(!data_ptr.is_null(), "data must be non-null");
let dtor_ptr = *std::ptr::addr_of!(cb.destructor).cast::<*const u8>();
assert!(!dtor_ptr.is_null(), "destructor must be non-null");
}
}
#[test]
fn result() {
assert_eq!(mem::align_of::<ffi::Result<u32, u32>>(), 4);
assert_eq!(mem::size_of::<ffi::Result<u32, u32>>(), 8);
assert_eq!(mem::size_of::<ffi::Result<u64, u8>>(), 16);
assert_eq!(mem::align_of::<ffi::Result<u64, u8>>(), 8);
let ok: ffi::Result<u32, u32> = ffi::Ok(0xCAFE);
let raw = unsafe { &*(&ok as *const ffi::Result<u32, u32> as *const [u32; 2]) };
assert_eq!(raw[0], 0, "Ok discriminant must be 0");
assert_eq!(raw[1], 0xCAFE);
let err: ffi::Result<u32, u32> = ffi::Result::Err(0xBEEF);
let raw = unsafe { &*(&err as *const ffi::Result<u32, u32> as *const [u32; 2]) };
assert_eq!(raw[0], 1, "Err discriminant must be 1");
assert_eq!(raw[1], 0xBEEF);
let panic: ffi::Result<u32, u32> = ffi::Result::Panic;
let tag = unsafe { *(&panic as *const ffi::Result<u32, u32> as *const u32) };
assert_eq!(tag, 2, "Panic discriminant must be 2");
let null: ffi::Result<u32, u32> = ffi::Result::Null;
let tag = unsafe { *(&null as *const ffi::Result<u32, u32> as *const u32) };
assert_eq!(tag, 3, "Null discriminant must be 3");
}