use std::ffi::{CString, c_char, c_void};
pub const NATIVE_DYLIB_ENTRYPOINT_V1: &str = "sim_native_abi_v1";
pub const NATIVE_LIB_ABI_V1_MAJOR: u16 = 1;
pub const NATIVE_LIB_ABI_V1_MINOR: u16 = 0;
pub type NativeAbiInstantiate = unsafe extern "C" fn() -> *mut c_void;
pub type NativeAbiDestroyInstance = unsafe extern "C" fn(instance: *mut c_void);
pub type NativeAbiManifest = unsafe extern "C" fn(instance: *mut c_void) -> NativeAbiCallResponse;
pub type NativeAbiCall = unsafe extern "C" fn(
instance: *mut c_void,
function: *const c_char,
args: NativeAbiBorrowedBytes,
) -> NativeAbiCallResponse;
pub type NativeAbiDestroyBytes = unsafe extern "C" fn(bytes: NativeAbiOwnedBytes);
pub type NativeAbiDestroyError = unsafe extern "C" fn(error: *mut NativeAbiError);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct NativeLibAbiHeaderV1 {
pub struct_size: usize,
pub abi_major: u16,
pub abi_minor: u16,
}
impl NativeLibAbiHeaderV1 {
pub const fn new(struct_size: usize, abi_major: u16, abi_minor: u16) -> Self {
Self {
struct_size,
abi_major,
abi_minor,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct NativeLibAbiV1 {
pub struct_size: usize,
pub abi_major: u16,
pub abi_minor: u16,
pub instantiate: NativeAbiInstantiate,
pub destroy_instance: NativeAbiDestroyInstance,
pub manifest: NativeAbiManifest,
pub call: NativeAbiCall,
pub destroy_bytes: NativeAbiDestroyBytes,
pub destroy_error: NativeAbiDestroyError,
}
impl NativeLibAbiV1 {
pub const HEADER_SIZE: usize = std::mem::size_of::<NativeLibAbiHeaderV1>();
pub const fn new(
instantiate: NativeAbiInstantiate,
destroy_instance: NativeAbiDestroyInstance,
manifest: NativeAbiManifest,
call: NativeAbiCall,
destroy_bytes: NativeAbiDestroyBytes,
destroy_error: NativeAbiDestroyError,
) -> Self {
Self {
struct_size: std::mem::size_of::<Self>(),
abi_major: NATIVE_LIB_ABI_V1_MAJOR,
abi_minor: NATIVE_LIB_ABI_V1_MINOR,
instantiate,
destroy_instance,
manifest,
call,
destroy_bytes,
destroy_error,
}
}
pub const fn header(&self) -> NativeLibAbiHeaderV1 {
NativeLibAbiHeaderV1::new(self.struct_size, self.abi_major, self.abi_minor)
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct NativeAbiBorrowedBytes {
pub ptr: *const u8,
pub len: usize,
}
impl NativeAbiBorrowedBytes {
pub const fn empty() -> Self {
Self {
ptr: std::ptr::null(),
len: 0,
}
}
pub fn borrow(bytes: &[u8]) -> Self {
Self {
ptr: bytes.as_ptr(),
len: bytes.len(),
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct NativeAbiOwnedBytes {
pub ptr: *mut u8,
pub len: usize,
pub cap: usize,
}
impl NativeAbiOwnedBytes {
pub const fn empty() -> Self {
Self {
ptr: std::ptr::null_mut(),
len: 0,
cap: 0,
}
}
}
#[repr(C)]
pub struct NativeAbiCallResponse {
pub bytes: NativeAbiOwnedBytes,
pub error: *mut NativeAbiError,
}
impl NativeAbiCallResponse {
pub const fn success(bytes: NativeAbiOwnedBytes) -> Self {
Self {
bytes,
error: std::ptr::null_mut(),
}
}
pub const fn failure(error: *mut NativeAbiError) -> Self {
Self {
bytes: NativeAbiOwnedBytes::empty(),
error,
}
}
}
#[repr(C)]
pub struct NativeAbiError {
pub message: *mut c_char,
}
impl NativeAbiError {
pub fn boxed(message: impl Into<String>) -> *mut Self {
let sanitized = message.into().replace('\0', " ");
let message = CString::new(sanitized)
.expect("sanitized native ABI error strings must not contain interior NULs");
Box::into_raw(Box::new(Self {
message: message.into_raw(),
}))
}
}
pub fn native_abi_owned_bytes(mut bytes: Vec<u8>) -> NativeAbiOwnedBytes {
let result = NativeAbiOwnedBytes {
ptr: bytes.as_mut_ptr(),
len: bytes.len(),
cap: bytes.capacity(),
};
std::mem::forget(bytes);
result
}