use core::ffi::{c_char, c_int, c_void};
use core::ptr;
use core::slice;
use crate::{ZeroDdsReader, ZeroDdsRuntime, ZeroDdsStatus, ZeroDdsWriter};
pub type ZeroDdsEncodeFn = unsafe extern "C" fn(
sample: *const c_void,
out_buf: *mut u8,
out_cap: usize,
out_len: *mut usize,
) -> c_int;
pub type ZeroDdsDecodeFn =
unsafe extern "C" fn(buf: *const u8, len: usize, out_sample: *mut c_void) -> c_int;
pub type ZeroDdsKeyHashFn = unsafe extern "C" fn(sample: *const c_void, out_hash: *mut u8) -> c_int;
pub type ZeroDdsSampleFreeFn = unsafe extern "C" fn(sample: *mut c_void);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ZeroDdsTypeSupport {
pub type_hash: [u8; 16],
pub type_name: *const c_char,
pub is_keyed: u8,
pub extensibility: u8,
pub _reserved: [u8; 6],
pub encode: Option<ZeroDdsEncodeFn>,
pub decode: Option<ZeroDdsDecodeFn>,
pub key_hash: Option<ZeroDdsKeyHashFn>,
pub sample_free: Option<ZeroDdsSampleFreeFn>,
}
unsafe impl Send for ZeroDdsTypeSupport {}
unsafe impl Sync for ZeroDdsTypeSupport {}
pub struct ZeroDdsTopic {
pub type_support: *const ZeroDdsTypeSupport,
pub topic_name: alloc::string::String,
pub type_name: alloc::string::String,
}
unsafe impl Send for ZeroDdsTopic {}
unsafe impl Sync for ZeroDdsTopic {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_topic_create_typed(
participant: *mut ZeroDdsRuntime,
topic_name: *const c_char,
type_support: *const ZeroDdsTypeSupport,
out_topic: *mut *mut ZeroDdsTopic,
) -> c_int {
if participant.is_null()
|| topic_name.is_null()
|| type_support.is_null()
|| out_topic.is_null()
{
return ZeroDdsStatus::BadHandle as c_int;
}
let ts = unsafe { &*type_support };
if ts.type_name.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let topic = match unsafe { core::ffi::CStr::from_ptr(topic_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ZeroDdsStatus::InvalidUtf8 as c_int,
};
let typ = match unsafe { core::ffi::CStr::from_ptr(ts.type_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ZeroDdsStatus::InvalidUtf8 as c_int,
};
let handle = alloc::boxed::Box::new(ZeroDdsTopic {
type_support,
topic_name: topic,
type_name: typ,
});
unsafe {
*out_topic = alloc::boxed::Box::into_raw(handle);
}
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_topic_destroy_typed(topic: *mut ZeroDdsTopic) {
if topic.is_null() {
return;
}
let _ = unsafe { alloc::boxed::Box::from_raw(topic) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_xcdr2_encode(
ts: *const ZeroDdsTypeSupport,
sample: *const c_void,
out_buf: *mut u8,
out_cap: usize,
out_len: *mut usize,
) -> c_int {
if ts.is_null() || sample.is_null() || out_len.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
if out_buf.is_null() && out_cap != 0 {
return ZeroDdsStatus::BadParameter as c_int;
}
let ts_ref = unsafe { &*ts };
let Some(enc) = ts_ref.encode else {
return ZeroDdsStatus::Unsupported as c_int;
};
unsafe { enc(sample, out_buf, out_cap, out_len) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_xcdr2_decode(
ts: *const ZeroDdsTypeSupport,
buf: *const u8,
len: usize,
out_sample: *mut c_void,
) -> c_int {
if ts.is_null() || buf.is_null() || out_sample.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let ts_ref = unsafe { &*ts };
let Some(dec) = ts_ref.decode else {
return ZeroDdsStatus::Unsupported as c_int;
};
unsafe { dec(buf, len, out_sample) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_write_typed(
writer: *mut ZeroDdsWriter,
ts: *const ZeroDdsTypeSupport,
sample: *const c_void,
) -> c_int {
if writer.is_null() || ts.is_null() || sample.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let ts_ref = unsafe { &*ts };
let Some(enc) = ts_ref.encode else {
return ZeroDdsStatus::Unsupported as c_int;
};
let mut needed: usize = 0;
let probe_rc = unsafe { enc(sample, ptr::null_mut(), 0, &mut needed as *mut usize) };
if probe_rc != ZeroDdsStatus::Ok as c_int && probe_rc != ZeroDdsStatus::Unsupported as c_int {
}
let mut buf = alloc::vec![0u8; needed];
let mut written: usize = 0;
let buf_ptr = if needed == 0 {
ptr::null_mut()
} else {
buf.as_mut_ptr()
};
let enc_rc = unsafe { enc(sample, buf_ptr, needed, &mut written as *mut usize) };
if enc_rc != ZeroDdsStatus::Ok as c_int {
return enc_rc;
}
unsafe { crate::zerodds_writer_write(writer, buf.as_ptr(), written) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_take_typed(
reader: *mut ZeroDdsReader,
ts: *const ZeroDdsTypeSupport,
out_sample: *mut c_void,
out_info: *mut c_void,
) -> c_int {
let _ = out_info;
if reader.is_null() || ts.is_null() || out_sample.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let ts_ref = unsafe { &*ts };
let Some(dec) = ts_ref.decode else {
return ZeroDdsStatus::Unsupported as c_int;
};
let mut buf: *mut u8 = ptr::null_mut();
let mut len: usize = 0;
let rc = unsafe {
crate::zerodds_reader_take(reader, &mut buf as *mut *mut u8, &mut len as *mut usize)
};
if rc != ZeroDdsStatus::Ok as c_int {
return rc;
}
if buf.is_null() || len == 0 {
return ZeroDdsStatus::NoData as c_int;
}
let dec_rc = unsafe { dec(buf, len, out_sample) };
unsafe { crate::zerodds_buffer_free(buf, len) };
dec_rc
}
pub unsafe fn write_out_len(out_len: *mut usize, value: usize) {
if !out_len.is_null() {
unsafe { *out_len = value };
}
}
pub unsafe fn copy_to_out_buf(
bytes: &[u8],
out_buf: *mut u8,
out_cap: usize,
out_len: *mut usize,
) -> c_int {
unsafe { write_out_len(out_len, bytes.len()) };
if bytes.is_empty() {
return ZeroDdsStatus::Ok as c_int;
}
if out_buf.is_null() || out_cap < bytes.len() {
return ZeroDdsStatus::Unsupported as c_int;
}
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), out_buf, bytes.len());
}
ZeroDdsStatus::Ok as c_int
}
pub unsafe fn input_slice<'a>(buf: *const u8, len: usize) -> &'a [u8] {
if buf.is_null() || len == 0 {
return &[];
}
unsafe { slice::from_raw_parts(buf, len) }
}
#[cfg(test)]
mod tests {
use super::*;
use core::ffi::c_void;
#[repr(C)]
struct PointSample {
x: i32,
y: i32,
}
unsafe extern "C" fn point_encode(
sample: *const c_void,
out_buf: *mut u8,
out_cap: usize,
out_len: *mut usize,
) -> c_int {
let s = unsafe { &*(sample as *const PointSample) };
let mut bytes = alloc::vec::Vec::with_capacity(8);
bytes.extend_from_slice(&s.x.to_le_bytes());
bytes.extend_from_slice(&s.y.to_le_bytes());
unsafe { copy_to_out_buf(&bytes, out_buf, out_cap, out_len) }
}
unsafe extern "C" fn point_decode(
buf: *const u8,
len: usize,
out_sample: *mut c_void,
) -> c_int {
if len < 8 {
return ZeroDdsStatus::BadParameter as c_int;
}
let bytes = unsafe { input_slice(buf, len) };
let x = i32::from_le_bytes(bytes[0..4].try_into().unwrap());
let y = i32::from_le_bytes(bytes[4..8].try_into().unwrap());
unsafe {
(*(out_sample as *mut PointSample)).x = x;
(*(out_sample as *mut PointSample)).y = y;
}
ZeroDdsStatus::Ok as c_int
}
static POINT_TYPE_NAME: &[u8] = b"Point\0";
fn point_typesupport() -> ZeroDdsTypeSupport {
ZeroDdsTypeSupport {
type_hash: [0u8; 16],
type_name: POINT_TYPE_NAME.as_ptr() as *const c_char,
is_keyed: 0,
extensibility: 0,
_reserved: [0u8; 6],
encode: Some(point_encode),
decode: Some(point_decode),
key_hash: None,
sample_free: None,
}
}
#[test]
fn xcdr2_encode_size_probe_returns_needed() {
let ts = point_typesupport();
let s = PointSample { x: 1, y: -2 };
let mut needed: usize = 0;
let rc = unsafe {
zerodds_xcdr2_encode(
&ts,
&s as *const _ as *const c_void,
core::ptr::null_mut(),
0,
&mut needed,
)
};
assert_eq!(rc, ZeroDdsStatus::Unsupported as c_int);
assert_eq!(needed, 8);
}
#[test]
fn xcdr2_encode_writes_le_bytes() {
let ts = point_typesupport();
let s = PointSample { x: 1, y: -2 };
let mut buf = [0u8; 8];
let mut written: usize = 0;
let rc = unsafe {
zerodds_xcdr2_encode(
&ts,
&s as *const _ as *const c_void,
buf.as_mut_ptr(),
buf.len(),
&mut written,
)
};
assert_eq!(rc, ZeroDdsStatus::Ok as c_int);
assert_eq!(written, 8);
assert_eq!(buf, [0x01, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn xcdr2_decode_roundtrip() {
let ts = point_typesupport();
let bytes = [0x01u8, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF];
let mut out = PointSample { x: 0, y: 0 };
let rc = unsafe {
zerodds_xcdr2_decode(
&ts,
bytes.as_ptr(),
bytes.len(),
&mut out as *mut _ as *mut c_void,
)
};
assert_eq!(rc, ZeroDdsStatus::Ok as c_int);
assert_eq!(out.x, 1);
assert_eq!(out.y, -2);
}
#[test]
fn xcdr2_encode_null_ts_rejected() {
let s = PointSample { x: 1, y: 2 };
let mut buf = [0u8; 8];
let mut written: usize = 0;
let rc = unsafe {
zerodds_xcdr2_encode(
core::ptr::null(),
&s as *const _ as *const c_void,
buf.as_mut_ptr(),
buf.len(),
&mut written,
)
};
assert_eq!(rc, ZeroDdsStatus::BadHandle as c_int);
}
#[test]
fn xcdr2_decode_null_buf_rejected() {
let ts = point_typesupport();
let mut out = PointSample { x: 0, y: 0 };
let rc = unsafe {
zerodds_xcdr2_decode(&ts, core::ptr::null(), 0, &mut out as *mut _ as *mut c_void)
};
assert_eq!(rc, ZeroDdsStatus::BadHandle as c_int);
}
}