use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::marker::PhantomData;
use crate::{Error, Result, XabiOption, XabiOwnedBytes, XabiOwnedBytesOwner};
pub trait XabiContract<P: 'static> {
const ID: &'static str;
fn export(plugin: P) -> *mut c_void;
}
pub trait XabiType: Sized {
type Wire: Copy + 'static;
const WIRE_TYPE_NAME: &'static str;
fn into_wire(self) -> Self::Wire;
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self>;
#[doc(hidden)]
unsafe fn xabi_take_from_wire(wire: *mut Self::Wire) -> Result<Self> {
unsafe { Self::from_wire(wire.cast_const()) }
}
#[doc(hidden)]
unsafe fn xabi_drop_wire(_wire: *mut Self::Wire) {}
fn collect_xabi_layout(_collector: &mut dyn crate::XabiLayoutCollector) {}
#[doc(hidden)]
fn retain_module(&mut self, _module: &std::sync::Arc<crate::ModuleHandle>) {}
fn into_payload(self) -> XabiOwnedBytes {
let wire = self.into_wire();
let bytes = unsafe {
std::slice::from_raw_parts(
std::ptr::addr_of!(wire).cast::<u8>(),
std::mem::size_of::<Self::Wire>(),
)
};
XabiOwnedBytes::from_vec(bytes.to_vec())
}
unsafe fn from_payload(payload: XabiOwnedBytes) -> Result<Self> {
let bytes = unsafe { payload.to_vec_and_free() }?;
if bytes.len() != std::mem::size_of::<Self::Wire>() {
return Err(Error::AbiMismatch(format!(
"xabi payload size {} does not match expected {}",
bytes.len(),
std::mem::size_of::<Self::Wire>()
)));
}
let mut wire = std::mem::MaybeUninit::<Self::Wire>::uninit();
unsafe {
std::ptr::copy_nonoverlapping(
bytes.as_ptr(),
wire.as_mut_ptr().cast::<u8>(),
bytes.len(),
);
Self::from_wire(wire.as_ptr())
}
}
}
#[doc(hidden)]
pub struct XabiWire<T: XabiType> {
wire: UnsafeCell<T::Wire>,
}
impl<T: XabiType> XabiWire<T> {
pub fn new(value: T) -> Self {
Self {
wire: UnsafeCell::new(value.into_wire()),
}
}
pub fn as_ptr(&self) -> *const T::Wire {
self.wire.get().cast_const()
}
}
impl<T: XabiType> Drop for XabiWire<T> {
fn drop(&mut self) {
unsafe { T::xabi_drop_wire(self.wire.get()) };
}
}
macro_rules! impl_xabi_type_for_int {
($($ty:ty),* $(,)?) => {
$(
impl XabiType for $ty {
type Wire = $ty;
const WIRE_TYPE_NAME: &'static str = stringify!($ty);
fn into_wire(self) -> Self::Wire {
self
}
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self> {
unsafe {
wire.as_ref()
.copied()
.ok_or(Error::NullPointer(concat!(stringify!($ty), " pointer")))
}
}
}
)*
};
}
impl_xabi_type_for_int!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
impl XabiType for bool {
type Wire = u8;
const WIRE_TYPE_NAME: &'static str = "u8";
fn into_wire(self) -> Self::Wire {
self as u8
}
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self> {
unsafe {
match wire
.as_ref()
.copied()
.ok_or(Error::NullPointer("bool pointer"))?
{
0 => Ok(false),
1 => Ok(true),
other => Err(Error::AbiMismatch(format!(
"bool wire value {other} is not 0 or 1"
))),
}
}
}
}
impl XabiType for XabiOwnedBytesOwner {
type Wire = XabiOwnedBytes;
const WIRE_TYPE_NAME: &'static str = "XabiOwnedBytes";
fn into_wire(self) -> Self::Wire {
self.into_raw()
}
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self> {
let raw = unsafe {
wire.as_ref()
.copied()
.ok_or(Error::NullPointer("XabiOwnedBytesOwner pointer"))?
};
unsafe { XabiOwnedBytesOwner::from_raw(raw) }
}
unsafe fn xabi_take_from_wire(wire: *mut Self::Wire) -> Result<Self> {
let wire = unsafe {
wire.as_mut()
.ok_or(Error::NullPointer("XabiOwnedBytesOwner pointer"))?
};
let raw = std::mem::replace(wire, XabiOwnedBytes::empty());
unsafe { XabiOwnedBytesOwner::from_raw(raw) }
}
unsafe fn xabi_drop_wire(wire: *mut Self::Wire) {
let Some(wire) = (unsafe { wire.as_mut() }) else {
return;
};
let raw = std::mem::replace(wire, XabiOwnedBytes::empty());
drop(unsafe { XabiOwnedBytesOwner::from_raw(raw) });
}
fn into_payload(self) -> XabiOwnedBytes {
self.into_raw()
}
unsafe fn from_payload(payload: XabiOwnedBytes) -> Result<Self> {
unsafe { XabiOwnedBytesOwner::from_raw(payload) }
}
fn retain_module(&mut self, module: &std::sync::Arc<crate::ModuleHandle>) {
XabiOwnedBytesOwner::retain_module(self, module);
}
}
impl XabiType for Vec<u8> {
type Wire = XabiOwnedBytes;
const WIRE_TYPE_NAME: &'static str = "XabiOwnedBytes";
fn into_wire(self) -> Self::Wire {
XabiOwnedBytes::from_vec(self)
}
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self> {
let owner = unsafe { XabiOwnedBytesOwner::from_wire(wire) }?;
Ok(owner.into_vec())
}
unsafe fn xabi_take_from_wire(wire: *mut Self::Wire) -> Result<Self> {
let owner = unsafe { <XabiOwnedBytesOwner as XabiType>::xabi_take_from_wire(wire) }?;
Ok(owner.into_vec())
}
unsafe fn xabi_drop_wire(wire: *mut Self::Wire) {
unsafe { <XabiOwnedBytesOwner as XabiType>::xabi_drop_wire(wire) };
}
fn into_payload(self) -> XabiOwnedBytes {
XabiOwnedBytes::from_vec(self)
}
unsafe fn from_payload(payload: XabiOwnedBytes) -> Result<Self> {
let owner = unsafe { XabiOwnedBytesOwner::from_payload(payload) }?;
Ok(owner.into_vec())
}
}
impl XabiType for String {
type Wire = XabiOwnedBytes;
const WIRE_TYPE_NAME: &'static str = "XabiOwnedBytes";
fn into_wire(self) -> Self::Wire {
XabiOwnedBytes::from_string(self)
}
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self> {
let wire = unsafe {
wire.as_ref()
.copied()
.ok_or(Error::NullPointer("String pointer"))?
};
unsafe { wire.to_string_and_free() }
}
fn into_payload(self) -> XabiOwnedBytes {
XabiOwnedBytes::from_string(self)
}
unsafe fn from_payload(payload: XabiOwnedBytes) -> Result<Self> {
unsafe { payload.to_string_and_free() }
}
}
impl<T> XabiType for Option<T>
where
T: XabiType + 'static,
{
type Wire = XabiOption;
const WIRE_TYPE_NAME: &'static str = "XabiOption";
fn collect_xabi_layout(collector: &mut dyn crate::XabiLayoutCollector) {
T::collect_xabi_layout(collector);
}
fn retain_module(&mut self, module: &std::sync::Arc<crate::ModuleHandle>) {
if let Some(value) = self {
T::retain_module(value, module);
}
}
fn into_wire(self) -> Self::Wire {
match self {
Some(value) => XabiOption::some(value.into_payload()),
None => XabiOption::none(),
}
}
unsafe fn from_wire(wire: *const Self::Wire) -> Result<Self> {
let wire = unsafe {
wire.as_ref()
.ok_or(Error::NullPointer("XabiOption pointer"))?
};
wire.validate()?;
if wire.is_some == 0 {
return Ok(None);
}
unsafe { T::from_payload(wire.payload).map(Some) }
}
}
pub struct SendPtr<T> {
value: usize,
_marker: PhantomData<*mut T>,
}
impl<T> SendPtr<T> {
pub fn new(ptr: *mut T) -> Self {
Self {
value: ptr as usize,
_marker: PhantomData,
}
}
pub fn as_ptr(self) -> *mut T {
self.value as *mut T
}
}
unsafe impl<T> Send for SendPtr<T> {}
#[cfg(test)]
mod tests {
use super::*;
fn assert_send<T: Send>() {}
#[test]
fn send_ptr_is_send() {
assert_send::<SendPtr<u8>>();
}
#[test]
fn send_ptr_roundtrips_pointer_value() {
let mut value = 5_u32;
let ptr = &mut value as *mut u32;
assert_eq!(SendPtr::new(ptr).as_ptr(), ptr);
}
}