boltffi_core 0.22.1

Core runtime types for BoltFFI - high-performance FFI bindings generator
Documentation
use std::ffi::c_void;
use std::sync::Arc;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct CallbackHandle {
    handle: u64,
    vtable: *const c_void,
}

unsafe impl Send for CallbackHandle {}
unsafe impl Sync for CallbackHandle {}

impl CallbackHandle {
    pub const NULL: Self = Self {
        handle: 0,
        vtable: std::ptr::null(),
    };

    #[inline]
    pub const fn new(handle: u64, vtable: *const c_void) -> Self {
        Self { handle, vtable }
    }

    #[inline]
    pub fn handle(&self) -> u64 {
        self.handle
    }

    #[inline]
    pub fn vtable(&self) -> *const c_void {
        self.vtable
    }

    #[inline]
    pub fn is_null(&self) -> bool {
        #[cfg(target_arch = "wasm32")]
        {
            return self.handle == 0;
        }

        #[cfg(not(target_arch = "wasm32"))]
        {
            self.handle == 0 || self.vtable.is_null()
        }
    }

    #[inline]
    #[cfg(target_arch = "wasm32")]
    pub fn from_wasm_handle(handle: u32) -> Self {
        Self {
            handle: handle as u64,
            vtable: std::ptr::null(),
        }
    }
}

pub trait FromCallbackHandle {
    unsafe fn arc_from_callback_handle(handle: CallbackHandle) -> Arc<Self>;
    unsafe fn box_from_callback_handle(handle: CallbackHandle) -> Box<Self>;
}

pub trait CallbackForeignType {
    type Foreign: FromCallbackHandle;
}

impl Default for CallbackHandle {
    fn default() -> Self {
        Self::NULL
    }
}

impl std::fmt::Debug for CallbackHandle {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CallbackHandle")
            .field("handle", &self.handle)
            .field("vtable", &self.vtable)
            .finish()
    }
}

#[cfg(target_arch = "wasm32")]
#[unsafe(no_mangle)]
pub extern "C" fn boltffi_create_callback_handle(js_handle: u32) -> u32 {
    js_handle
}

#[cfg(target_arch = "wasm32")]
pub struct WasmCallbackOwner {
    handle: u32,
    free_fn: unsafe extern "C" fn(u32),
}

#[cfg(target_arch = "wasm32")]
impl WasmCallbackOwner {
    #[inline]
    pub fn new(handle: u32, free_fn: unsafe extern "C" fn(u32)) -> Self {
        Self { handle, free_fn }
    }

    #[inline]
    pub fn handle(&self) -> u32 {
        self.handle
    }
}

#[cfg(target_arch = "wasm32")]
impl Drop for WasmCallbackOwner {
    fn drop(&mut self) {
        unsafe { (self.free_fn)(self.handle) }
    }
}

#[cfg(target_arch = "wasm32")]
unsafe impl Send for WasmCallbackOwner {}

#[cfg(target_arch = "wasm32")]
unsafe impl Sync for WasmCallbackOwner {}

#[cfg(test)]
mod tests {
    use super::CallbackHandle;

    #[cfg(not(target_arch = "wasm32"))]
    #[test]
    fn native_handle_with_null_vtable_is_null() {
        let handle = CallbackHandle::new(7, std::ptr::null());
        assert!(handle.is_null());
    }

    #[cfg(target_arch = "wasm32")]
    #[test]
    fn wasm_handle_is_null_only_when_handle_is_zero() {
        let handle = CallbackHandle::from_wasm_handle(7);
        assert!(!handle.is_null());
        assert!(CallbackHandle::from_wasm_handle(0).is_null());
    }
}