use crate::{ForeignBytes, Handle, RustBuffer, RustCallStatus, RustCallStatusCode};
use std::{mem::ManuallyDrop, ptr::NonNull};
#[repr(C)]
#[derive(Clone, Copy)]
pub union FfiBufferElement {
pub u8: u8,
pub i8: i8,
pub u16: u16,
pub i16: i16,
pub u32: u32,
pub i32: i32,
pub u64: u64,
pub i64: i64,
pub float: std::ffi::c_float,
pub double: std::ffi::c_double,
pub ptr: *const std::ffi::c_void,
}
impl Default for FfiBufferElement {
fn default() -> Self {
Self { u64: 0 }
}
}
pub trait FfiSerialize: Sized {
const SIZE: usize;
fn get(buf: &[FfiBufferElement]) -> Self;
fn put(buf: &mut [FfiBufferElement], value: Self);
fn read(buf: &mut &[FfiBufferElement]) -> Self {
let value = Self::get(buf);
*buf = &buf[Self::SIZE..];
value
}
fn write(buf: &mut &mut [FfiBufferElement], value: Self) {
Self::put(buf, value);
let (_, new_buf) = ::core::mem::take(buf).split_at_mut(Self::SIZE);
*buf = new_buf;
}
}
#[macro_export]
macro_rules! ffi_buffer_size {
($($T:ty),* $(,)?) => {
(
0
$(
+ <$T as $crate::FfiSerialize>::SIZE
)*
)
}
}
macro_rules! define_ffi_serialize_simple_cases {
($(($name: ident, $T:ty)),* $(,)?) => {
$(
impl FfiSerialize for $T {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { buf[0].$name }
}
fn put(buf: &mut[FfiBufferElement], value: Self) {
buf[0].$name = value
}
}
)*
};
}
define_ffi_serialize_simple_cases! {
(i8, i8),
(u8, u8),
(i16, i16),
(u16, u16),
(i32, i32),
(u32, u32),
(i64, i64),
(u64, u64),
(ptr, *const std::ffi::c_void),
}
impl FfiSerialize for f32 {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { buf[0].float as Self }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].float = value as std::ffi::c_float;
}
}
impl FfiSerialize for f64 {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { buf[0].double as Self }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].double = value as std::ffi::c_double;
}
}
impl FfiSerialize for bool {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { buf[0].i8 == 1 }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].i8 = if value { 1 } else { 0 }
}
}
impl FfiSerialize for () {
const SIZE: usize = 0;
fn get(_buf: &[FfiBufferElement]) -> Self {}
fn put(_buf: &mut [FfiBufferElement], _value: Self) {}
}
impl<T> FfiSerialize for NonNull<T> {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { Self::new_unchecked(buf[0].ptr as *mut T) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].ptr = value.as_ptr() as *const std::ffi::c_void
}
}
impl FfiSerialize for Handle {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { Handle::from_raw_unchecked(buf[0].u64) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].u64 = value.as_raw()
}
}
impl FfiSerialize for RustBuffer {
const SIZE: usize = 3;
fn get(buf: &[FfiBufferElement]) -> Self {
let (capacity, len, data) = unsafe { (buf[0].u64, buf[1].u64, buf[2].ptr as *mut u8) };
unsafe { RustBuffer::from_raw_parts(data, len, capacity) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].u64 = value.capacity;
buf[1].u64 = value.len;
buf[2].ptr = value.data as *const std::ffi::c_void;
}
}
impl FfiSerialize for ForeignBytes {
const SIZE: usize = 2;
fn get(buf: &[FfiBufferElement]) -> Self {
let (len, data) = unsafe { (buf[0].i32, buf[1].ptr as *const u8) };
unsafe { ForeignBytes::from_raw_parts(data, len) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].i32 = value.len;
buf[1].ptr = value.data as *const std::ffi::c_void;
}
}
impl FfiSerialize for RustCallStatus {
const SIZE: usize = 4;
fn get(buf: &[FfiBufferElement]) -> Self {
let code = unsafe { buf[0].i8 };
Self {
code: RustCallStatusCode::try_from(code).unwrap_or(RustCallStatusCode::UnexpectedError),
error_buf: ManuallyDrop::new(RustBuffer::get(&buf[1..])),
}
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].i8 = value.code as i8;
RustBuffer::put(&mut buf[1..], ManuallyDrop::into_inner(value.error_buf))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{ForeignBytes, Handle, RustBuffer, RustCallStatus, RustCallStatusCode};
#[test]
fn test_ffi_buffer_size() {
assert_eq!(ffi_buffer_size!(u8), 1);
assert_eq!(ffi_buffer_size!(i8), 1);
assert_eq!(ffi_buffer_size!(u16), 1);
assert_eq!(ffi_buffer_size!(i16), 1);
assert_eq!(ffi_buffer_size!(u32), 1);
assert_eq!(ffi_buffer_size!(i32), 1);
assert_eq!(ffi_buffer_size!(u64), 1);
assert_eq!(ffi_buffer_size!(i64), 1);
assert_eq!(ffi_buffer_size!(f32), 1);
assert_eq!(ffi_buffer_size!(f64), 1);
assert_eq!(ffi_buffer_size!(bool), 1);
assert_eq!(ffi_buffer_size!(*const std::ffi::c_void), 1);
assert_eq!(ffi_buffer_size!(RustBuffer), 3);
assert_eq!(ffi_buffer_size!(ForeignBytes), 2);
assert_eq!(ffi_buffer_size!(RustCallStatus), 4);
assert_eq!(ffi_buffer_size!(Handle), 1);
assert_eq!(ffi_buffer_size!(()), 0);
assert_eq!(ffi_buffer_size!(u8, f32, bool, Handle, (), RustBuffer), 7);
}
#[test]
fn test_ffi_serialize_foreign_bytes() {
let data = [7u8, 8, 9];
let fb = unsafe { ForeignBytes::from_raw_parts(data.as_ptr(), 3) };
let mut buf = [FfiBufferElement::default(); 2];
<ForeignBytes as FfiSerialize>::put(&mut buf, fb);
let fb2 = <ForeignBytes as FfiSerialize>::get(&buf);
assert_eq!(fb2.as_slice(), &[7, 8, 9]);
}
#[test]
fn test_ffi_serialize() {
let mut some_data = vec![1, 2, 3];
let void_ptr = some_data.as_mut_ptr() as *const std::ffi::c_void;
let rust_buffer = unsafe { RustBuffer::from_raw_parts(some_data.as_mut_ptr(), 2, 3) };
let orig_rust_buffer_data = (
rust_buffer.data_pointer(),
rust_buffer.len(),
rust_buffer.capacity(),
);
let handle = unsafe { Handle::from_raw(101).unwrap() };
let rust_call_status = RustCallStatus::default();
let rust_call_status_error_buf = &rust_call_status.error_buf;
let orig_rust_call_status_buffer_data = (
rust_call_status_error_buf.data_pointer(),
rust_call_status_error_buf.len(),
rust_call_status_error_buf.capacity(),
);
let mut buf = [FfiBufferElement::default(); 21];
let mut buf_writer = buf.as_mut_slice();
<u8 as FfiSerialize>::write(&mut buf_writer, 0);
<i8 as FfiSerialize>::write(&mut buf_writer, 1);
<u16 as FfiSerialize>::write(&mut buf_writer, 2);
<i16 as FfiSerialize>::write(&mut buf_writer, 3);
<u32 as FfiSerialize>::write(&mut buf_writer, 4);
<i32 as FfiSerialize>::write(&mut buf_writer, 5);
<u64 as FfiSerialize>::write(&mut buf_writer, 6);
<i64 as FfiSerialize>::write(&mut buf_writer, 7);
<f32 as FfiSerialize>::write(&mut buf_writer, 0.1);
<f64 as FfiSerialize>::write(&mut buf_writer, 0.2);
<bool as FfiSerialize>::write(&mut buf_writer, true);
<*const std::ffi::c_void as FfiSerialize>::write(&mut buf_writer, void_ptr);
<RustBuffer as FfiSerialize>::write(&mut buf_writer, rust_buffer);
<RustCallStatus as FfiSerialize>::write(&mut buf_writer, rust_call_status);
<Handle as FfiSerialize>::write(&mut buf_writer, handle.clone());
#[allow(clippy::needless_borrows_for_generic_args)]
<() as FfiSerialize>::write(&mut buf_writer, ());
let mut buf_reader = buf.as_slice();
assert_eq!(<u8 as FfiSerialize>::read(&mut buf_reader), 0);
assert_eq!(<i8 as FfiSerialize>::read(&mut buf_reader), 1);
assert_eq!(<u16 as FfiSerialize>::read(&mut buf_reader), 2);
assert_eq!(<i16 as FfiSerialize>::read(&mut buf_reader), 3);
assert_eq!(<u32 as FfiSerialize>::read(&mut buf_reader), 4);
assert_eq!(<i32 as FfiSerialize>::read(&mut buf_reader), 5);
assert_eq!(<u64 as FfiSerialize>::read(&mut buf_reader), 6);
assert_eq!(<i64 as FfiSerialize>::read(&mut buf_reader), 7);
assert_eq!(<f32 as FfiSerialize>::read(&mut buf_reader), 0.1);
assert_eq!(<f64 as FfiSerialize>::read(&mut buf_reader), 0.2);
assert!(<bool as FfiSerialize>::read(&mut buf_reader));
assert_eq!(
<*const std::ffi::c_void as FfiSerialize>::read(&mut buf_reader),
void_ptr
);
let rust_buffer2 = <RustBuffer as FfiSerialize>::read(&mut buf_reader);
assert_eq!(
(
rust_buffer2.data_pointer(),
rust_buffer2.len(),
rust_buffer2.capacity()
),
orig_rust_buffer_data,
);
let rust_call_status2 = <RustCallStatus as FfiSerialize>::read(&mut buf_reader);
assert_eq!(rust_call_status2.code, RustCallStatusCode::Success);
let rust_call_status2_error_buf = ManuallyDrop::into_inner(rust_call_status2.error_buf);
assert_eq!(
(
rust_call_status2_error_buf.data_pointer(),
rust_call_status2_error_buf.len(),
rust_call_status2_error_buf.capacity(),
),
orig_rust_call_status_buffer_data
);
assert_eq!(<Handle as FfiSerialize>::read(&mut buf_reader), handle);
<() as FfiSerialize>::read(&mut buf_reader);
}
}