#![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)]
extern crate alloc;
pub mod builtin_ffi;
pub mod condition_ffi;
pub mod entities;
pub mod extra_ffi;
pub mod factory_ffi;
pub mod listener_ffi;
pub mod participant_ffi;
pub mod publisher_ffi;
pub mod qos_ffi;
pub mod subscriber_ffi;
pub mod topic_ffi;
pub mod xcdr2;
use core::ffi::{c_char, c_int};
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(&pid.to_le_bytes());
bytes[4..12].copy_from_slice(&(t.wrapping_add(u64::from(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(_) => 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 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 r = unsafe { &*runtime };
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
loop {
let n = r.rt.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));
}
}
#[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 = unsafe { &*runtime };
let topic = match unsafe { CStr::from_ptr(topic_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let typ = match unsafe { CStr::from_ptr(type_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
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.rt.register_user_writer(cfg) {
Ok(e) => e,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(ZeroDdsWriter {
rt: 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 w = unsafe { &*writer };
let bytes = if len == 0 {
Vec::new()
} else {
unsafe { slice::from_raw_parts(payload, len) }.to_vec()
};
match w.rt.write_user_sample(w.eid, bytes) {
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 w = unsafe { &*writer };
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
loop {
let n = w.rt.user_writer_matched_count(w.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 w = unsafe { &*writer };
let mut kh = [0u8; 16];
unsafe {
std::ptr::copy_nonoverlapping(key_hash, kh.as_mut_ptr(), 16);
}
match w
.rt
.write_user_lifecycle(w.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 w = unsafe { &*writer };
let mut kh = [0u8; 16];
unsafe {
std::ptr::copy_nonoverlapping(key_hash, kh.as_mut_ptr(), 16);
}
match w.rt.write_user_lifecycle(
w.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 w = unsafe { &*writer };
let mut kh = [0u8; 16];
unsafe {
std::ptr::copy_nonoverlapping(key_hash, kh.as_mut_ptr(), 16);
}
let bits = zerodds_rtps::inline_qos::status_info::DISPOSED
| zerodds_rtps::inline_qos::status_info::UNREGISTERED;
match w.rt.write_user_lifecycle(w.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;
}
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 = unsafe { &*runtime };
let topic = match unsafe { CStr::from_ptr(topic_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let typ = match unsafe { CStr::from_ptr(type_name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
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.rt.register_user_reader(cfg) {
Ok(p) => p,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(ZeroDdsReader {
rt: 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,
) -> c_int {
if reader.is_null() || out_buf.is_null() || out_len.is_null() {
return ZeroDdsStatus::BadHandle as c_int;
}
let r = unsafe { &*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(_) => {
unsafe {
*out_buf = ptr::null_mut();
*out_len = 0;
}
return ZeroDdsStatus::PreconditionNotMet as c_int;
}
};
match bytes {
Some(bs) => {
let mut boxed = bs.into_boxed_slice();
unsafe {
*out_buf = boxed.as_mut_ptr();
*out_len = boxed.len();
}
let _ = Box::into_raw(boxed);
}
None => unsafe {
*out_buf = ptr::null_mut();
*out_len = 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 r = unsafe { &*reader };
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
loop {
let matched = r.rt.user_reader_matched_count(r.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_destroy(reader: *mut ZeroDdsReader) {
if reader.is_null() {
return;
}
let r = unsafe { &*reader };
r.rt.set_user_reader_listener(r.eid, None);
let _ = unsafe { Box::from_raw(reader) };
}
pub type ZeroDdsDataCallback =
extern "C" fn(user_data: *mut core::ffi::c_void, payload: *const u8, payload_len: usize);
#[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 r = unsafe { &*reader };
let listener: Option<zerodds_dcps::runtime::UserReaderListener> = match callback {
Some(cb) => {
let ud_addr = user_data as usize;
Some(Box::new(move |bytes: &[u8]| {
cb(
ud_addr as *mut core::ffi::c_void,
bytes.as_ptr(),
bytes.len(),
);
}))
}
None => None,
};
if r.rt.set_user_reader_listener(r.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)) };
}
#[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;
}
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;
}
let rc = unsafe { zerodds_writer_write(writer, ptr, len) };
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;
}
unsafe {
let _ = Box::from_raw(slice::from_raw_parts_mut(ptr, len));
}
ZeroDdsStatus::Ok as c_int
}
#[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
}