#![warn(missing_docs)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::field_reassign_with_default)]
#![allow(clippy::needless_lifetimes)]
#![allow(missing_docs)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::map_unwrap_or)]
#![allow(clippy::print_stderr)]
extern crate alloc;
pub mod builtin_ffi;
pub mod condition_ffi;
pub mod entities;
pub mod extra_ffi;
pub mod factory_ffi;
pub(crate) mod ffi_helpers;
pub mod listener_ffi;
pub mod participant_ffi;
pub mod publisher_ffi;
pub mod qos_ffi;
#[cfg(feature = "security")]
pub mod security_ffi;
#[cfg(feature = "flatdata-loan")]
pub mod shm_loan_ffi;
pub mod subscriber_ffi;
pub mod topic_ffi;
pub mod xcdr2;
use core::ffi::{c_char, c_int, c_void};
use core::ptr;
use core::slice;
use std::ffi::CStr;
use std::sync::{Arc, Mutex, mpsc};
use std::time::Duration;
use zerodds_dcps::runtime::{DcpsRuntime, RuntimeConfig, UserReaderConfig, UserWriterConfig};
use zerodds_qos::{
DeadlineQosPolicy, DurabilityKind, LifespanQosPolicy, LivelinessKind, LivelinessQosPolicy,
OwnershipKind,
};
use zerodds_rtps::wire_types::{EntityId, GuidPrefix};
pub(crate) fn random_guid_prefix() -> GuidPrefix {
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
let pid = std::process::id();
let t = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let c = COUNTER.fetch_add(1, Ordering::Relaxed);
let mut bytes = [0u8; 12];
bytes[0..4].copy_from_slice(&zerodds_dcps::participant::host_id_bytes());
bytes[4..8].copy_from_slice(&pid.to_le_bytes());
bytes[8..12].copy_from_slice(&(t as u32).wrapping_add(c).to_le_bytes());
GuidPrefix::from_bytes(bytes)
}
#[repr(i32)]
#[derive(Debug, Clone, Copy)]
pub enum ZeroDdsStatus {
Ok = 0,
Error = -1,
BadHandle = -2,
InvalidUtf8 = -3,
Timeout = -4,
PreconditionNotMet = -5,
BadParameter = -6,
NoData = -7,
OutOfResources = -8,
NotEnabled = -9,
ImmutablePolicy = -10,
InconsistentPolicy = -11,
AlreadyDeleted = -12,
Unsupported = -13,
IllegalOperation = -14,
}
pub struct ZeroDdsRuntime {
rt: Arc<DcpsRuntime>,
_shutdown: (),
}
pub struct ZeroDdsWriter {
rt: Arc<DcpsRuntime>,
eid: EntityId,
}
pub struct ZeroDdsReader {
rt: Arc<DcpsRuntime>,
eid: EntityId,
rx: Mutex<mpsc::Receiver<zerodds_dcps::runtime::UserSample>>,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_create(domain_id: u32) -> *mut ZeroDdsRuntime {
let cfg = RuntimeConfig::default();
let rt = match DcpsRuntime::start(domain_id as i32, random_guid_prefix(), cfg) {
Ok(r) => r,
Err(e) => {
eprintln!("zerodds_runtime_create(domain_id={domain_id}) failed: {e:?}");
return ptr::null_mut();
}
};
let handle = Box::new(ZeroDdsRuntime { rt, _shutdown: () });
Box::into_raw(handle)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_destroy(runtime: *mut ZeroDdsRuntime) {
if runtime.is_null() {
return;
}
let _ = unsafe { Box::from_raw(runtime) };
}
#[unsafe(no_mangle)]
pub extern "C" fn zerodds_phase_dump() {
use zerodds_dcps::runtime::{
PHASE_HANDLE_SUB_NS, PHASE_HANDLE_USER_CALLS, PHASE_HANDLE_USER_NS, PHASE_WRITE_SUB_NS,
PHASE_WRITE_USER_CALLS, PHASE_WRITE_USER_NS,
};
let hu_ns = PHASE_HANDLE_USER_NS.load(core::sync::atomic::Ordering::Relaxed);
let hu_n = PHASE_HANDLE_USER_CALLS.load(core::sync::atomic::Ordering::Relaxed);
let wu_ns = PHASE_WRITE_USER_NS.load(core::sync::atomic::Ordering::Relaxed);
let wu_n = PHASE_WRITE_USER_CALLS.load(core::sync::atomic::Ordering::Relaxed);
let hu_us = if hu_n > 0 {
hu_ns as f64 / hu_n as f64 / 1000.0
} else {
0.0
};
let wu_us = if wu_n > 0 {
wu_ns as f64 / wu_n as f64 / 1000.0
} else {
0.0
};
eprintln!(
"[ZERODDS_PHASE] handle_user_datagram: N={} avg={:.3}us total={:.1}ms",
hu_n,
hu_us,
hu_ns as f64 / 1_000_000.0
);
eprintln!(
"[ZERODDS_PHASE] write_user_sample: N={} avg={:.3}us total={:.1}ms",
wu_n,
wu_us,
wu_ns as f64 / 1_000_000.0
);
if wu_n > 0 {
let names = [
"write[lookup]",
"write[lock] ",
"write[wwh] ",
"write[send] ",
];
for (i, name) in names.iter().enumerate() {
let ns = PHASE_WRITE_SUB_NS[i].load(core::sync::atomic::Ordering::Relaxed);
eprintln!(
"[ZERODDS_PHASE] sub[{}]: avg={:.3}us total={:.1}ms",
name,
ns as f64 / wu_n as f64 / 1000.0,
ns as f64 / 1_000_000.0
);
}
}
if hu_n > 0 {
let names = [
"handle[decode] ",
"handle[slot-look]",
"handle[reader+lk]",
"handle[reserved] ",
"handle[dispatch] ",
];
for (i, name) in names.iter().enumerate() {
let ns = PHASE_HANDLE_SUB_NS[i].load(core::sync::atomic::Ordering::Relaxed);
if ns > 0 {
eprintln!(
"[ZERODDS_PHASE] sub[{}]: avg={:.3}us total={:.1}ms",
name,
ns as f64 / hu_n as f64 / 1000.0,
ns as f64 / 1_000_000.0
);
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_wait_for_peers(
runtime: *mut ZeroDdsRuntime,
min_count: c_int,
timeout_ms: u64,
) -> c_int {
if runtime.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let rt_clone = unsafe { (*runtime).rt.clone() };
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
loop {
let n = rt_clone.discovered_participants().len();
if (n as c_int) >= min_count {
return ZeroDdsStatus::Ok as c_int;
}
if std::time::Instant::now() >= deadline {
return ZeroDdsStatus::Timeout as c_int;
}
std::thread::sleep(Duration::from_millis(50));
}
}
pub type ZeroDdsTopicCallback =
extern "C" fn(*mut core::ffi::c_void, *const core::ffi::c_char, *const core::ffi::c_char);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_for_each_publication(
runtime: *mut ZeroDdsRuntime,
callback: Option<ZeroDdsTopicCallback>,
user_data: *mut core::ffi::c_void,
) -> c_int {
zerodds_runtime_for_each_topic(runtime, callback, user_data, true)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_for_each_subscription(
runtime: *mut ZeroDdsRuntime,
callback: Option<ZeroDdsTopicCallback>,
user_data: *mut core::ffi::c_void,
) -> c_int {
zerodds_runtime_for_each_topic(runtime, callback, user_data, false)
}
fn zerodds_runtime_for_each_topic(
runtime: *mut ZeroDdsRuntime,
callback: Option<ZeroDdsTopicCallback>,
user_data: *mut core::ffi::c_void,
publications: bool,
) -> c_int {
if runtime.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let Some(cb) = callback else {
return ZeroDdsStatus::BadHandle as c_int;
};
let rt = unsafe { (*runtime).rt.clone() };
let topics = if publications {
rt.discovered_publication_topics()
} else {
rt.discovered_subscription_topics()
};
for (topic, typ) in topics {
if let (Ok(t), Ok(y)) = (std::ffi::CString::new(topic), std::ffi::CString::new(typ)) {
cb(user_data, t.as_ptr(), y.as_ptr());
}
}
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_participant_guid(
runtime: *mut ZeroDdsRuntime,
out_guid: *mut u8,
) -> c_int {
if runtime.is_null() || out_guid.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let rt = unsafe { (*runtime).rt.clone() };
let mut g = [0u8; 16];
g[..12].copy_from_slice(&rt.guid_prefix.to_bytes());
g[12..16].copy_from_slice(&[0x00, 0x00, 0x01, 0xc1]);
unsafe { core::ptr::copy_nonoverlapping(g.as_ptr(), out_guid, 16) };
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_guid(
writer: *mut ZeroDdsWriter,
out_guid: *mut u8,
) -> c_int {
if writer.is_null() || out_guid.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let w = unsafe { &*writer };
let mut g = [0u8; 16];
g[..12].copy_from_slice(&w.rt.guid_prefix.to_bytes());
g[12..16].copy_from_slice(&w.eid.to_bytes());
unsafe { core::ptr::copy_nonoverlapping(g.as_ptr(), out_guid, 16) };
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_guid(
reader: *mut ZeroDdsReader,
out_guid: *mut u8,
) -> c_int {
if reader.is_null() || out_guid.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let r = unsafe { &*reader };
let mut g = [0u8; 16];
g[..12].copy_from_slice(&r.rt.guid_prefix.to_bytes());
g[12..16].copy_from_slice(&r.eid.to_bytes());
unsafe { core::ptr::copy_nonoverlapping(g.as_ptr(), out_guid, 16) };
ZeroDdsStatus::Ok as c_int
}
#[repr(C)]
pub struct ZeroDdsEndpointInfo {
pub topic_name: *const core::ffi::c_char,
pub type_name: *const core::ffi::c_char,
pub endpoint_guid: *const u8,
pub reliable: u8,
pub transient_local: u8,
pub deadline_seconds: i32,
pub lifespan_seconds: i32,
pub liveliness_lease_seconds: i32,
}
pub type ZeroDdsEndpointCallback =
extern "C" fn(*mut core::ffi::c_void, *const ZeroDdsEndpointInfo);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_for_each_publication_endpoint(
runtime: *mut ZeroDdsRuntime,
callback: Option<ZeroDdsEndpointCallback>,
user_data: *mut core::ffi::c_void,
) -> c_int {
zerodds_runtime_for_each_endpoint(runtime, callback, user_data, true)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_runtime_for_each_subscription_endpoint(
runtime: *mut ZeroDdsRuntime,
callback: Option<ZeroDdsEndpointCallback>,
user_data: *mut core::ffi::c_void,
) -> c_int {
zerodds_runtime_for_each_endpoint(runtime, callback, user_data, false)
}
fn zerodds_runtime_for_each_endpoint(
runtime: *mut ZeroDdsRuntime,
callback: Option<ZeroDdsEndpointCallback>,
user_data: *mut core::ffi::c_void,
publications: bool,
) -> c_int {
if runtime.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let Some(cb) = callback else {
return ZeroDdsStatus::BadHandle as c_int;
};
let rt = unsafe { (*runtime).rt.clone() };
let eps = if publications {
rt.discovered_publication_endpoints()
} else {
rt.discovered_subscription_endpoints()
};
for e in eps {
let (Ok(t), Ok(y)) = (
std::ffi::CString::new(e.topic_name),
std::ffi::CString::new(e.type_name),
) else {
continue;
};
let info = ZeroDdsEndpointInfo {
topic_name: t.as_ptr(),
type_name: y.as_ptr(),
endpoint_guid: e.endpoint_guid.as_ptr(),
reliable: u8::from(e.reliable),
transient_local: u8::from(e.transient_local),
deadline_seconds: e.deadline_seconds,
lifespan_seconds: e.lifespan_seconds,
liveliness_lease_seconds: e.liveliness_lease_seconds,
};
cb(user_data, &info);
}
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_create(
runtime: *mut ZeroDdsRuntime,
topic_name: *const c_char,
type_name: *const c_char,
reliable: c_int,
) -> *mut ZeroDdsWriter {
if runtime.is_null() || topic_name.is_null() || type_name.is_null() {
return ptr::null_mut();
}
let (rt_clone, topic, typ) = unsafe {
let topic = match CStr::from_ptr(topic_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let typ = match CStr::from_ptr(type_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
((*runtime).rt.clone(), topic, typ)
};
let cfg = UserWriterConfig {
topic_name: topic,
type_name: typ,
reliable: reliable != 0,
durability: DurabilityKind::Volatile,
deadline: DeadlineQosPolicy::default(),
lifespan: LifespanQosPolicy::default(),
liveliness: LivelinessQosPolicy {
kind: LivelinessKind::Automatic,
..Default::default()
},
ownership: OwnershipKind::Shared,
ownership_strength: 0,
partition: Vec::new(),
user_data: Vec::new(),
topic_data: Vec::new(),
group_data: Vec::new(),
type_identifier: zerodds_types::TypeIdentifier::None,
data_representation_offer: None,
};
let eid = match rt_clone.register_user_writer(cfg) {
Ok(e) => e,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(ZeroDdsWriter { rt: rt_clone, eid }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_create_kind(
runtime: *mut ZeroDdsRuntime,
topic_name: *const c_char,
type_name: *const c_char,
reliable: c_int,
is_keyed: c_int,
) -> *mut ZeroDdsWriter {
if runtime.is_null() || topic_name.is_null() || type_name.is_null() {
return ptr::null_mut();
}
let (rt_clone, topic, typ) = unsafe {
let topic = match CStr::from_ptr(topic_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let typ = match CStr::from_ptr(type_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
((*runtime).rt.clone(), topic, typ)
};
let cfg = UserWriterConfig {
topic_name: topic,
type_name: typ,
reliable: reliable != 0,
durability: DurabilityKind::Volatile,
deadline: DeadlineQosPolicy::default(),
lifespan: LifespanQosPolicy::default(),
liveliness: LivelinessQosPolicy {
kind: LivelinessKind::Automatic,
..Default::default()
},
ownership: OwnershipKind::Shared,
ownership_strength: 0,
partition: Vec::new(),
user_data: Vec::new(),
topic_data: Vec::new(),
group_data: Vec::new(),
type_identifier: zerodds_types::TypeIdentifier::None,
data_representation_offer: None,
};
let eid = match rt_clone.register_user_writer_kind(cfg, is_keyed != 0) {
Ok(e) => e,
Err(e) => {
eprintln!(
"zerodds_writer_create_kind(topic={topic_name:?}, is_keyed={is_keyed}) failed: {e:?}"
);
return ptr::null_mut();
}
};
Box::into_raw(Box::new(ZeroDdsWriter { rt: rt_clone, eid }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_write(
writer: *mut ZeroDdsWriter,
payload: *const u8,
len: usize,
) -> c_int {
if writer.is_null() || (payload.is_null() && len > 0) {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid) = unsafe {
let w = &*writer;
(w.rt.clone(), w.eid)
};
let bytes: &[u8] = if len == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(payload, len) }
};
match rt.write_user_sample_borrowed(eid, bytes) {
Ok(()) => ZeroDdsStatus::Ok as c_int,
Err(_) => ZeroDdsStatus::Error as c_int,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_set_wire_extensibility(
writer: *mut ZeroDdsWriter,
extensibility: c_int,
) -> c_int {
if writer.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
use zerodds_types::qos::ExtensibilityForRepr;
let ext = match extensibility {
0 => ExtensibilityForRepr::Final,
1 => ExtensibilityForRepr::Appendable,
2 => ExtensibilityForRepr::Mutable,
_ => return ZeroDdsStatus::BadParameter as c_int,
};
let (rt, eid) = unsafe {
let w = &*writer;
(w.rt.clone(), w.eid)
};
match rt.set_user_writer_wire_extensibility(eid, ext) {
Ok(()) => ZeroDdsStatus::Ok as c_int,
Err(_) => ZeroDdsStatus::Error as c_int,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_wait_for_matched(
writer: *mut ZeroDdsWriter,
min_count: c_int,
timeout_ms: u64,
) -> c_int {
if writer.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid) = unsafe { ((*writer).rt.clone(), (*writer).eid) };
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
loop {
let n = rt.user_writer_matched_count(eid);
if (n as c_int) >= min_count {
return ZeroDdsStatus::Ok as c_int;
}
if std::time::Instant::now() >= deadline {
return ZeroDdsStatus::Timeout as c_int;
}
std::thread::sleep(Duration::from_millis(10));
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_dispose(
writer: *mut ZeroDdsWriter,
key_hash: *const u8,
) -> c_int {
if writer.is_null() || key_hash.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid, kh) = unsafe {
let w = &*writer;
let mut kh = [0u8; 16];
std::ptr::copy_nonoverlapping(key_hash, kh.as_mut_ptr(), 16);
(w.rt.clone(), w.eid, kh)
};
match rt.write_user_lifecycle(eid, kh, zerodds_rtps::inline_qos::status_info::DISPOSED) {
Ok(()) => ZeroDdsStatus::Ok as c_int,
Err(_) => ZeroDdsStatus::Error as c_int,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_unregister(
writer: *mut ZeroDdsWriter,
key_hash: *const u8,
) -> c_int {
if writer.is_null() || key_hash.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid, kh) = unsafe {
let w = &*writer;
let mut kh = [0u8; 16];
std::ptr::copy_nonoverlapping(key_hash, kh.as_mut_ptr(), 16);
(w.rt.clone(), w.eid, kh)
};
match rt.write_user_lifecycle(eid, kh, zerodds_rtps::inline_qos::status_info::UNREGISTERED) {
Ok(()) => ZeroDdsStatus::Ok as c_int,
Err(_) => ZeroDdsStatus::Error as c_int,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_unregister_with_dispose(
writer: *mut ZeroDdsWriter,
key_hash: *const u8,
) -> c_int {
if writer.is_null() || key_hash.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid, kh) = unsafe {
let w = &*writer;
let mut kh = [0u8; 16];
std::ptr::copy_nonoverlapping(key_hash, kh.as_mut_ptr(), 16);
(w.rt.clone(), w.eid, kh)
};
let bits = zerodds_rtps::inline_qos::status_info::DISPOSED
| zerodds_rtps::inline_qos::status_info::UNREGISTERED;
match rt.write_user_lifecycle(eid, kh, bits) {
Ok(()) => ZeroDdsStatus::Ok as c_int,
Err(_) => ZeroDdsStatus::Error as c_int,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_destroy(writer: *mut ZeroDdsWriter) {
if writer.is_null() {
return;
}
#[cfg(feature = "flatdata-loan")]
unsafe {
let w = &*writer;
crate::shm_loan_ffi::forget_writer(&w.rt, w.eid);
}
let _ = unsafe { Box::from_raw(writer) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_create(
runtime: *mut ZeroDdsRuntime,
topic_name: *const c_char,
type_name: *const c_char,
reliable: c_int,
) -> *mut ZeroDdsReader {
if runtime.is_null() || topic_name.is_null() || type_name.is_null() {
return ptr::null_mut();
}
let (rt_clone, topic, typ) = unsafe {
let topic = match CStr::from_ptr(topic_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let typ = match CStr::from_ptr(type_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
((*runtime).rt.clone(), topic, typ)
};
let cfg = UserReaderConfig {
topic_name: topic,
type_name: typ,
reliable: reliable != 0,
durability: DurabilityKind::Volatile,
deadline: DeadlineQosPolicy::default(),
liveliness: LivelinessQosPolicy {
kind: LivelinessKind::Automatic,
..Default::default()
},
ownership: OwnershipKind::Shared,
partition: Vec::new(),
user_data: Vec::new(),
topic_data: Vec::new(),
group_data: Vec::new(),
type_identifier: zerodds_types::TypeIdentifier::None,
type_consistency: zerodds_types::qos::TypeConsistencyEnforcement::default(),
data_representation_offer: None,
};
let (eid, rx) = match rt_clone.register_user_reader(cfg) {
Ok(p) => p,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(ZeroDdsReader {
rt: rt_clone,
eid,
rx: Mutex::new(rx),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_create_kind(
runtime: *mut ZeroDdsRuntime,
topic_name: *const c_char,
type_name: *const c_char,
reliable: c_int,
is_keyed: c_int,
) -> *mut ZeroDdsReader {
if runtime.is_null() || topic_name.is_null() || type_name.is_null() {
return ptr::null_mut();
}
let (rt_clone, topic, typ) = unsafe {
let topic = match CStr::from_ptr(topic_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let typ = match CStr::from_ptr(type_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
((*runtime).rt.clone(), topic, typ)
};
let cfg = UserReaderConfig {
topic_name: topic,
type_name: typ,
reliable: reliable != 0,
durability: DurabilityKind::Volatile,
deadline: DeadlineQosPolicy::default(),
liveliness: LivelinessQosPolicy {
kind: LivelinessKind::Automatic,
..Default::default()
},
ownership: OwnershipKind::Shared,
partition: Vec::new(),
user_data: Vec::new(),
topic_data: Vec::new(),
group_data: Vec::new(),
type_identifier: zerodds_types::TypeIdentifier::None,
type_consistency: zerodds_types::qos::TypeConsistencyEnforcement::default(),
data_representation_offer: None,
};
let (eid, rx) = match rt_clone.register_user_reader_kind(cfg, is_keyed != 0) {
Ok(p) => p,
Err(e) => {
eprintln!(
"zerodds_reader_create_kind(topic={topic_name:?}, is_keyed={is_keyed}) failed: {e:?}"
);
return ptr::null_mut();
}
};
Box::into_raw(Box::new(ZeroDdsReader {
rt: rt_clone,
eid,
rx: Mutex::new(rx),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_take(
reader: *mut ZeroDdsReader,
out_buf: *mut *mut u8,
out_len: *mut usize,
out_repr: *mut u8,
) -> c_int {
if reader.is_null() || out_buf.is_null() || out_len.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
unsafe {
let r = &*reader;
let bytes = match r.rx.lock() {
Ok(rx) => loop {
match rx.try_recv().ok() {
Some(zerodds_dcps::runtime::UserSample::Alive {
payload: b,
representation,
..
}) => {
break Some((b, representation));
}
Some(zerodds_dcps::runtime::UserSample::Lifecycle { .. }) => continue,
None => break None,
}
},
Err(_) => {
*out_buf = ptr::null_mut();
*out_len = 0;
return ZeroDdsStatus::PreconditionNotMet as c_int;
}
};
match bytes {
Some((bs, repr)) => {
let mut boxed = bs.to_vec().into_boxed_slice();
*out_buf = boxed.as_mut_ptr();
*out_len = boxed.len();
let _ = Box::into_raw(boxed);
if !out_repr.is_null() {
*out_repr = repr;
}
}
None => {
*out_buf = ptr::null_mut();
*out_len = 0;
if !out_repr.is_null() {
*out_repr = 0;
}
}
}
}
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_wait_for_matched(
reader: *mut ZeroDdsReader,
min_count: c_int,
timeout_ms: u64,
) -> c_int {
if reader.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid) = unsafe { ((*reader).rt.clone(), (*reader).eid) };
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
loop {
let matched = rt.user_reader_matched_count(eid) as c_int;
if matched >= min_count {
return ZeroDdsStatus::Ok as c_int;
}
if std::time::Instant::now() >= deadline {
return ZeroDdsStatus::Timeout as c_int;
}
std::thread::sleep(Duration::from_millis(10));
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_unknown_src_count(reader: *mut ZeroDdsReader) -> u64 {
if reader.is_null() {
return 0;
}
let r = unsafe { &*reader };
r.rt.user_reader_unknown_src_count(r.eid)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_destroy(reader: *mut ZeroDdsReader) {
if reader.is_null() {
return;
}
unsafe {
let r = &*reader;
r.rt.set_user_reader_listener(r.eid, None);
#[cfg(feature = "flatdata-loan")]
crate::shm_loan_ffi::forget_reader(&r.rt, r.eid);
let _ = Box::from_raw(reader);
}
}
pub type ZeroDdsDataCallback = extern "C" fn(
user_data: *mut core::ffi::c_void,
payload: *const u8,
payload_len: usize,
representation: u8,
);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_set_data_callback(
reader: *mut ZeroDdsReader,
callback: Option<ZeroDdsDataCallback>,
user_data: *mut core::ffi::c_void,
) -> c_int {
if reader.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let (rt, eid) = unsafe { ((*reader).rt.clone(), (*reader).eid) };
let listener: Option<zerodds_dcps::runtime::UserReaderListener> = match callback {
Some(cb) => {
let ud_addr = user_data as usize;
Some(Box::new(move |bytes: &[u8], repr: u8| {
cb(
ud_addr as *mut core::ffi::c_void,
bytes.as_ptr(),
bytes.len(),
repr,
);
}))
}
None => None,
};
if rt.set_user_reader_listener(eid, listener) {
ZeroDdsStatus::Ok as c_int
} else {
ZeroDdsStatus::BadHandle as c_int
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_buffer_free(buf: *mut u8, len: usize) {
if buf.is_null() || len == 0 {
return;
}
let _ = unsafe { Box::from_raw(slice::from_raw_parts_mut(buf, len)) };
}
type ZeroDdsReadLoanHandle = zerodds_dcps::sample_bytes::SampleBytes;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_loan(
reader: *mut ZeroDdsReader,
out_buf: *mut *const u8,
out_len: *mut usize,
out_loan_handle: *mut *mut c_void,
) -> c_int {
if reader.is_null() || out_buf.is_null() || out_len.is_null() || out_loan_handle.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
unsafe {
let r = &*reader;
let bytes = match r.rx.lock() {
Ok(rx) => loop {
match rx.try_recv().ok() {
Some(zerodds_dcps::runtime::UserSample::Alive { payload: b, .. }) => {
break Some(b);
}
Some(zerodds_dcps::runtime::UserSample::Lifecycle { .. }) => continue,
None => break None,
}
},
Err(_) => {
*out_buf = ptr::null();
*out_len = 0;
*out_loan_handle = ptr::null_mut();
return ZeroDdsStatus::PreconditionNotMet as c_int;
}
};
match bytes {
Some(bs) => {
let len = bs.as_slice().len();
let boxed: Box<ZeroDdsReadLoanHandle> = Box::new(bs);
let buf_ptr = boxed.as_slice().as_ptr();
let handle = Box::into_raw(boxed);
*out_buf = buf_ptr;
*out_len = len;
*out_loan_handle = handle.cast::<c_void>();
}
None => {
*out_buf = ptr::null();
*out_len = 0;
*out_loan_handle = ptr::null_mut();
}
}
}
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_return_loan(loan_handle: *mut c_void) {
if loan_handle.is_null() {
return;
}
let _ = unsafe { Box::from_raw(loan_handle.cast::<ZeroDdsReadLoanHandle>()) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_loan_message(
writer: *mut ZeroDdsWriter,
len: usize,
out_ptr: *mut *mut u8,
out_len: *mut usize,
) -> c_int {
if writer.is_null() || out_ptr.is_null() || out_len.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
if len == 0 {
return ZeroDdsStatus::BadParameter as c_int;
}
#[cfg(feature = "flatdata-loan")]
if let Some(rc) = unsafe {
let w = &*writer;
crate::shm_loan_ffi::try_loan(&w.rt, w.eid, len, out_ptr, out_len)
} {
return rc;
}
let mut v = alloc::vec![0u8; len].into_boxed_slice();
let ptr = v.as_mut_ptr();
let _ = Box::into_raw(v);
unsafe {
*out_ptr = ptr;
*out_len = len;
}
ZeroDdsStatus::Ok as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_commit_loan(
writer: *mut ZeroDdsWriter,
ptr: *mut u8,
len: usize,
) -> c_int {
if writer.is_null() || ptr.is_null() || len == 0 {
return ZeroDdsStatus::BadHandle as c_int;
}
#[cfg(feature = "flatdata-loan")]
if let Some(rc) = unsafe {
let w = &*writer;
crate::shm_loan_ffi::try_commit(&w.rt, w.eid, ptr, len)
} {
return rc;
}
let (rt, eid, payload) = unsafe {
let w = &*writer;
let payload = slice::from_raw_parts(ptr, len);
(w.rt.clone(), w.eid, payload)
};
let rc = match rt.write_user_sample_borrowed(eid, payload) {
Ok(()) => ZeroDdsStatus::Ok as c_int,
Err(_) => ZeroDdsStatus::Error as c_int,
};
unsafe {
let _ = Box::from_raw(slice::from_raw_parts_mut(ptr, len));
}
rc
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_discard_loan(
_writer: *mut ZeroDdsWriter,
ptr: *mut u8,
len: usize,
) -> c_int {
if ptr.is_null() || len == 0 {
return ZeroDdsStatus::BadHandle as c_int;
}
#[cfg(feature = "flatdata-loan")]
if !_writer.is_null() {
if let Some(rc) = unsafe {
let w = &*_writer;
crate::shm_loan_ffi::try_discard(&w.rt, w.eid, ptr)
} {
return rc;
}
}
unsafe {
let _ = Box::from_raw(slice::from_raw_parts_mut(ptr, len));
}
ZeroDdsStatus::Ok as c_int
}
#[cfg(feature = "flatdata-loan")]
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ZeroDdsDeliveryMode {
Portable = 0,
RawSameHost = 1,
Iceoryx = 2,
}
#[cfg(feature = "flatdata-loan")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_set_delivery_mode(
writer: *mut ZeroDdsWriter,
mode: c_int,
) -> c_int {
if writer.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let Ok(mode) = u8::try_from(mode) else {
return ZeroDdsStatus::BadParameter as c_int;
};
let w = unsafe { &*writer };
crate::shm_loan_ffi::set_delivery_mode(&w.rt, w.eid, mode)
}
#[cfg(feature = "flatdata-loan")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_enable_shm_loan(
writer: *mut ZeroDdsWriter,
name: *const c_char,
slot_count: usize,
slot_capacity: usize,
) -> c_int {
if writer.is_null() || name.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let path = match unsafe { CStr::from_ptr(name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ZeroDdsStatus::InvalidUtf8 as c_int,
};
let w = unsafe { &*writer };
crate::shm_loan_ffi::enable_writer(&w.rt, w.eid, path, slot_count, slot_capacity)
}
#[cfg(feature = "flatdata-loan")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_enable_shm(
reader: *mut ZeroDdsReader,
name: *const c_char,
reader_index: u8,
) -> c_int {
if reader.is_null() || name.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let path = match unsafe { CStr::from_ptr(name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ZeroDdsStatus::InvalidUtf8 as c_int,
};
let r = unsafe { &*reader };
crate::shm_loan_ffi::enable_reader(&r.rt, r.eid, path, reader_index)
}
#[cfg(feature = "flatdata-loan")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_take_shm(
reader: *mut ZeroDdsReader,
out_ptr: *mut *const u8,
out_len: *mut usize,
out_slot: *mut u32,
) -> c_int {
if reader.is_null() || out_ptr.is_null() || out_len.is_null() || out_slot.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let r = unsafe { &*reader };
unsafe { crate::shm_loan_ffi::try_take(&r.rt, r.eid, out_ptr, out_len, out_slot) }
}
#[cfg(feature = "flatdata-loan")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_release_shm(
reader: *mut ZeroDdsReader,
slot_index: u32,
) -> c_int {
if reader.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let r = unsafe { &*reader };
crate::shm_loan_ffi::try_release(&r.rt, r.eid, slot_index)
}
#[cfg(feature = "flatdata-loan")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_raw_wait(
reader: *mut ZeroDdsReader,
timeout_ms: u64,
) -> c_int {
if reader.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let r = unsafe { &*reader };
crate::shm_loan_ffi::try_raw_wait(&r.rt, r.eid, timeout_ms)
}
#[cfg(feature = "delivery-iceoryx")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_writer_enable_iceoryx(
writer: *mut ZeroDdsWriter,
service_name: *const c_char,
max_len: usize,
) -> c_int {
if writer.is_null() || service_name.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let service = match unsafe { CStr::from_ptr(service_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ZeroDdsStatus::InvalidUtf8 as c_int,
};
let w = unsafe { &*writer };
crate::shm_loan_ffi::enable_iceoryx_writer(&w.rt, w.eid, service, max_len)
}
#[cfg(feature = "delivery-iceoryx")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zerodds_reader_enable_iceoryx(
reader: *mut ZeroDdsReader,
service_name: *const c_char,
) -> c_int {
if reader.is_null() || service_name.is_null() {
return ZeroDdsStatus::BadParameter as c_int;
}
let service = match unsafe { CStr::from_ptr(service_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ZeroDdsStatus::InvalidUtf8 as c_int,
};
let r = unsafe { &*reader };
crate::shm_loan_ffi::enable_iceoryx_reader(&r.rt, r.eid, service)
}
#[unsafe(no_mangle)]
pub extern "C" fn zerodds_version() -> *const c_char {
static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
VERSION.as_ptr() as *const c_char
}