use crate::ipc::IpcMessage;
use std::collections::VecDeque;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::{ptr, slice};
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LazilyFfiStatus {
Ok = 0,
Empty = 1,
NullPointer = 2,
InvalidMessage = 3,
EncodeFailed = 4,
Panic = 5,
}
#[repr(C)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum LazilyFfiMessageKind {
#[default]
Unknown = 0,
Snapshot = 1,
Delta = 2,
CrdtSync = 3,
ResyncRequest = 4,
OutboxAck = 5,
DeltaSinceRequest = 6,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LazilyFfiBytes {
pub ptr: *mut u8,
pub len: usize,
}
impl LazilyFfiBytes {
pub const fn empty() -> Self {
Self {
ptr: ptr::null_mut(),
len: 0,
}
}
fn from_vec(bytes: Vec<u8>) -> Self {
if bytes.is_empty() {
return Self::empty();
}
let mut bytes = bytes.into_boxed_slice();
let ptr = bytes.as_mut_ptr();
let len = bytes.len();
std::mem::forget(bytes);
Self { ptr, len }
}
}
impl Default for LazilyFfiBytes {
fn default() -> Self {
Self::empty()
}
}
#[derive(Debug, Default)]
pub struct LazilyFfiChannel {
queue: VecDeque<IpcMessage>,
}
#[unsafe(no_mangle)]
pub extern "C" fn lazily_ffi_channel_new() -> *mut LazilyFfiChannel {
Box::into_raw(Box::<LazilyFfiChannel>::default())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_free(channel: *mut LazilyFfiChannel) {
if channel.is_null() {
return;
}
let _ = catch_unwind(AssertUnwindSafe(|| {
unsafe { drop(Box::from_raw(channel)) };
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_send_json(
channel: *mut LazilyFfiChannel,
ptr: *const u8,
len: usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let channel = unsafe { channel_mut(channel) }?;
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_json(bytes)?;
channel.queue.push_back(message);
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_recv_json(
channel: *mut LazilyFfiChannel,
out: *mut LazilyFfiBytes,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out = unsafe { out_bytes_mut(out) }?;
*out = LazilyFfiBytes::empty();
let channel = unsafe { channel_mut(channel) }?;
match channel.queue.pop_front() {
Some(message) => {
*out = LazilyFfiBytes::from_vec(encode_message_json(&message)?);
Ok(())
}
None => Err(LazilyFfiStatus::Empty),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_len(
channel: *const LazilyFfiChannel,
out_len: *mut usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let channel = unsafe { channel_ref(channel) }?;
let out_len = unsafe { out_len.as_mut() }.ok_or(LazilyFfiStatus::NullPointer)?;
*out_len = channel.queue.len();
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_validate_json(
ptr: *const u8,
len: usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
decode_message_json(bytes)?;
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_kind_json(
ptr: *const u8,
len: usize,
out_kind: *mut LazilyFfiMessageKind,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out_kind = unsafe { out_kind.as_mut() }.ok_or(LazilyFfiStatus::NullPointer)?;
*out_kind = LazilyFfiMessageKind::Unknown;
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_json(bytes)?;
*out_kind = match message {
IpcMessage::Snapshot(_) => LazilyFfiMessageKind::Snapshot,
IpcMessage::Delta(_) => LazilyFfiMessageKind::Delta,
IpcMessage::CrdtSync(_) => LazilyFfiMessageKind::CrdtSync,
IpcMessage::ResyncRequest(_) => LazilyFfiMessageKind::ResyncRequest,
IpcMessage::OutboxAck(_) => LazilyFfiMessageKind::OutboxAck,
IpcMessage::DeltaSinceRequest(_) => LazilyFfiMessageKind::DeltaSinceRequest,
};
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_clone_json(
ptr: *const u8,
len: usize,
out: *mut LazilyFfiBytes,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out = unsafe { out_bytes_mut(out) }?;
*out = LazilyFfiBytes::empty();
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_json(bytes)?;
*out = LazilyFfiBytes::from_vec(encode_message_json(&message)?);
Ok(())
})
}
#[cfg(feature = "ipc-binary")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_send_binary(
channel: *mut LazilyFfiChannel,
ptr: *const u8,
len: usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let channel = unsafe { channel_mut(channel) }?;
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_binary(bytes)?;
channel.queue.push_back(message);
Ok(())
})
}
#[cfg(feature = "ipc-binary")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_recv_binary(
channel: *mut LazilyFfiChannel,
out: *mut LazilyFfiBytes,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out = unsafe { out_bytes_mut(out) }?;
*out = LazilyFfiBytes::empty();
let channel = unsafe { channel_mut(channel) }?;
match channel.queue.pop_front() {
Some(message) => {
*out = LazilyFfiBytes::from_vec(encode_message_binary(&message)?);
Ok(())
}
None => Err(LazilyFfiStatus::Empty),
}
})
}
#[cfg(feature = "ipc-binary")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_validate_binary(
ptr: *const u8,
len: usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
decode_message_binary(bytes)?;
Ok(())
})
}
#[cfg(feature = "ipc-binary")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_kind_binary(
ptr: *const u8,
len: usize,
out_kind: *mut LazilyFfiMessageKind,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out_kind = unsafe { out_kind.as_mut() }.ok_or(LazilyFfiStatus::NullPointer)?;
*out_kind = LazilyFfiMessageKind::Unknown;
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_binary(bytes)?;
*out_kind = match message {
IpcMessage::Snapshot(_) => LazilyFfiMessageKind::Snapshot,
IpcMessage::Delta(_) => LazilyFfiMessageKind::Delta,
IpcMessage::CrdtSync(_) => LazilyFfiMessageKind::CrdtSync,
IpcMessage::ResyncRequest(_) => LazilyFfiMessageKind::ResyncRequest,
IpcMessage::OutboxAck(_) => LazilyFfiMessageKind::OutboxAck,
IpcMessage::DeltaSinceRequest(_) => LazilyFfiMessageKind::DeltaSinceRequest,
};
Ok(())
})
}
#[cfg(feature = "ipc-binary")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_clone_binary(
ptr: *const u8,
len: usize,
out: *mut LazilyFfiBytes,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out = unsafe { out_bytes_mut(out) }?;
*out = LazilyFfiBytes::empty();
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_binary(bytes)?;
*out = LazilyFfiBytes::from_vec(encode_message_binary(&message)?);
Ok(())
})
}
#[cfg(feature = "ipc-msgpack")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_send_msgpack(
channel: *mut LazilyFfiChannel,
ptr: *const u8,
len: usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let channel = unsafe { channel_mut(channel) }?;
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_msgpack(bytes)?;
channel.queue.push_back(message);
Ok(())
})
}
#[cfg(feature = "ipc-msgpack")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_channel_recv_msgpack(
channel: *mut LazilyFfiChannel,
out: *mut LazilyFfiBytes,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out = unsafe { out_bytes_mut(out) }?;
*out = LazilyFfiBytes::empty();
let channel = unsafe { channel_mut(channel) }?;
match channel.queue.pop_front() {
Some(message) => {
*out = LazilyFfiBytes::from_vec(encode_message_msgpack(&message)?);
Ok(())
}
None => Err(LazilyFfiStatus::Empty),
}
})
}
#[cfg(feature = "ipc-msgpack")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_validate_msgpack(
ptr: *const u8,
len: usize,
) -> LazilyFfiStatus {
ffi_guard(|| {
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
decode_message_msgpack(bytes)?;
Ok(())
})
}
#[cfg(feature = "ipc-msgpack")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_kind_msgpack(
ptr: *const u8,
len: usize,
out_kind: *mut LazilyFfiMessageKind,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out_kind = unsafe { out_kind.as_mut() }.ok_or(LazilyFfiStatus::NullPointer)?;
*out_kind = LazilyFfiMessageKind::Unknown;
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_msgpack(bytes)?;
*out_kind = match message {
IpcMessage::Snapshot(_) => LazilyFfiMessageKind::Snapshot,
IpcMessage::Delta(_) => LazilyFfiMessageKind::Delta,
IpcMessage::CrdtSync(_) => LazilyFfiMessageKind::CrdtSync,
IpcMessage::ResyncRequest(_) => LazilyFfiMessageKind::ResyncRequest,
IpcMessage::OutboxAck(_) => LazilyFfiMessageKind::OutboxAck,
IpcMessage::DeltaSinceRequest(_) => LazilyFfiMessageKind::DeltaSinceRequest,
};
Ok(())
})
}
#[cfg(feature = "ipc-msgpack")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_ipc_message_clone_msgpack(
ptr: *const u8,
len: usize,
out: *mut LazilyFfiBytes,
) -> LazilyFfiStatus {
ffi_guard(|| {
let out = unsafe { out_bytes_mut(out) }?;
*out = LazilyFfiBytes::empty();
let bytes = unsafe { bytes_from_raw(ptr, len) }?;
let message = decode_message_msgpack(bytes)?;
*out = LazilyFfiBytes::from_vec(encode_message_msgpack(&message)?);
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lazily_ffi_bytes_free(bytes: LazilyFfiBytes) {
if bytes.ptr.is_null() {
return;
}
let _ = catch_unwind(AssertUnwindSafe(|| {
unsafe {
drop(Box::from_raw(ptr::slice_from_raw_parts_mut(
bytes.ptr, bytes.len,
)))
};
}));
}
fn ffi_guard(operation: impl FnOnce() -> Result<(), LazilyFfiStatus>) -> LazilyFfiStatus {
match catch_unwind(AssertUnwindSafe(operation)) {
Ok(Ok(())) => LazilyFfiStatus::Ok,
Ok(Err(status)) => status,
Err(_) => LazilyFfiStatus::Panic,
}
}
fn decode_message_json(bytes: &[u8]) -> Result<IpcMessage, LazilyFfiStatus> {
IpcMessage::decode_json(bytes).map_err(|_| LazilyFfiStatus::InvalidMessage)
}
fn encode_message_json(message: &IpcMessage) -> Result<Vec<u8>, LazilyFfiStatus> {
message
.encode_json()
.map_err(|_| LazilyFfiStatus::EncodeFailed)
}
#[cfg(feature = "ipc-binary")]
fn decode_message_binary(bytes: &[u8]) -> Result<IpcMessage, LazilyFfiStatus> {
IpcMessage::decode_binary(bytes).map_err(|_| LazilyFfiStatus::InvalidMessage)
}
#[cfg(feature = "ipc-binary")]
fn encode_message_binary(message: &IpcMessage) -> Result<Vec<u8>, LazilyFfiStatus> {
message
.encode_binary()
.map_err(|_| LazilyFfiStatus::EncodeFailed)
}
#[cfg(feature = "ipc-msgpack")]
fn decode_message_msgpack(bytes: &[u8]) -> Result<IpcMessage, LazilyFfiStatus> {
IpcMessage::decode_msgpack(bytes).map_err(|_| LazilyFfiStatus::InvalidMessage)
}
#[cfg(feature = "ipc-msgpack")]
fn encode_message_msgpack(message: &IpcMessage) -> Result<Vec<u8>, LazilyFfiStatus> {
message
.encode_msgpack()
.map_err(|_| LazilyFfiStatus::EncodeFailed)
}
unsafe fn channel_ref<'a>(
channel: *const LazilyFfiChannel,
) -> Result<&'a LazilyFfiChannel, LazilyFfiStatus> {
unsafe { channel.as_ref() }.ok_or(LazilyFfiStatus::NullPointer)
}
unsafe fn channel_mut<'a>(
channel: *mut LazilyFfiChannel,
) -> Result<&'a mut LazilyFfiChannel, LazilyFfiStatus> {
unsafe { channel.as_mut() }.ok_or(LazilyFfiStatus::NullPointer)
}
unsafe fn out_bytes_mut<'a>(
out: *mut LazilyFfiBytes,
) -> Result<&'a mut LazilyFfiBytes, LazilyFfiStatus> {
unsafe { out.as_mut() }.ok_or(LazilyFfiStatus::NullPointer)
}
unsafe fn bytes_from_raw<'a>(ptr: *const u8, len: usize) -> Result<&'a [u8], LazilyFfiStatus> {
if ptr.is_null() {
return if len == 0 {
Ok(&[])
} else {
Err(LazilyFfiStatus::NullPointer)
};
}
Ok(unsafe { slice::from_raw_parts(ptr, len) })
}