#![warn(missing_docs)]
#![allow(clippy::missing_safety_doc)]
extern crate alloc;
use core::ffi::{c_char, c_int, c_void};
use core::ptr;
use std::ffi::CStr;
use std::sync::Mutex;
use std::time::Duration;
use zerodds_ros2_rmw::ffi_api::RMW_IMPLEMENTATION_IDENTIFIER;
#[allow(non_upper_case_globals)]
pub const RMW_RET_OK: i32 = 0;
#[allow(non_upper_case_globals)]
pub const RMW_RET_ERROR: i32 = 1;
#[allow(non_upper_case_globals)]
pub const RMW_RET_TIMEOUT: i32 = 2;
#[allow(non_upper_case_globals)]
pub const RMW_RET_UNSUPPORTED: i32 = 3;
#[allow(non_upper_case_globals)]
pub const RMW_RET_BAD_ALLOC: i32 = 10;
#[allow(non_upper_case_globals)]
pub const RMW_RET_INVALID_ARGUMENT: i32 = 11;
#[allow(non_upper_case_globals)]
pub const RMW_RET_INCORRECT_RMW_IMPLEMENTATION: i32 = 12;
#[unsafe(no_mangle)]
pub extern "C" fn rmw_zerodds_get_implementation_identifier() -> *const c_char {
static IDENT: &[u8] = b"rmw_zerodds_cpp\0";
IDENT.as_ptr().cast()
}
#[unsafe(no_mangle)]
pub extern "C" fn rmw_zerodds_get_serialization_format() -> *const c_char {
static FMT: &[u8] = b"cdr\0";
FMT.as_ptr().cast()
}
pub struct RmwZerodsContext {
domain_id: u32,
runtime: *mut c_void, }
impl Drop for RmwZerodsContext {
fn drop(&mut self) {
if !self.runtime.is_null() {
unsafe {
zerodds::zerodds_runtime_destroy(self.runtime as *mut zerodds::ZeroDdsRuntime);
}
self.runtime = ptr::null_mut();
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_init(domain_id: u32) -> *mut RmwZerodsContext {
let rt = unsafe { zerodds::zerodds_runtime_create(domain_id) };
if rt.is_null() {
return ptr::null_mut();
}
Box::into_raw(Box::new(RmwZerodsContext {
domain_id,
runtime: rt as *mut c_void,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_shutdown(ctx: *mut RmwZerodsContext) -> i32 {
if ctx.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let _ = unsafe { Box::from_raw(ctx) };
RMW_RET_OK
}
pub struct RmwZerodsNode {
ctx: *mut RmwZerodsContext,
pub identity: zerodds_ros2_rmw::ffi_api::RmwNode,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_create_node(
ctx: *mut RmwZerodsContext,
name: *const c_char,
namespace_: *const c_char,
) -> *mut RmwZerodsNode {
if ctx.is_null() || name.is_null() || namespace_.is_null() {
return ptr::null_mut();
}
let cref = unsafe { &*ctx };
let n = match unsafe { CStr::from_ptr(name) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
let ns = match unsafe { CStr::from_ptr(namespace_) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(RmwZerodsNode {
ctx,
identity: zerodds_ros2_rmw::ffi_api::RmwNode {
implementation_identifier: RMW_IMPLEMENTATION_IDENTIFIER.into(),
name: n,
namespace: ns,
domain_id: cref.domain_id,
},
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_destroy_node(node: *mut RmwZerodsNode) -> i32 {
if node.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let _ = unsafe { Box::from_raw(node) };
RMW_RET_OK
}
pub struct RmwZerodsPublisher {
inner: Mutex<*mut zerodds::ZeroDdsWriter>,
pub ros_topic: alloc::string::String,
pub dds_topic: alloc::string::String,
pub type_name: alloc::string::String,
}
pub struct RmwZerodsSubscription {
inner: Mutex<*mut zerodds::ZeroDdsReader>,
pub ros_topic: alloc::string::String,
pub dds_topic: alloc::string::String,
pub type_name: alloc::string::String,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_create_publisher(
node: *mut RmwZerodsNode,
type_name: *const c_char,
topic_name: *const c_char,
reliable: c_int,
) -> *mut RmwZerodsPublisher {
if node.is_null() || type_name.is_null() || topic_name.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*node };
let ctx = unsafe { &*n.ctx };
let topic_ros = 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 topic_dds = zerodds_ros2_rmw::topic_mangling::mangle_topic_name(
&topic_ros,
zerodds_ros2_rmw::topic_mangling::RosKind::Topic,
)
.unwrap_or_else(|_| topic_ros.clone());
let dds_topic_c = match std::ffi::CString::new(topic_dds.clone()) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let typ_c = match std::ffi::CString::new(typ.clone()) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let writer = unsafe {
zerodds::zerodds_writer_create(
ctx.runtime as *mut zerodds::ZeroDdsRuntime,
dds_topic_c.as_ptr(),
typ_c.as_ptr(),
reliable,
)
};
if writer.is_null() {
return ptr::null_mut();
}
Box::into_raw(Box::new(RmwZerodsPublisher {
inner: Mutex::new(writer),
ros_topic: topic_ros,
dds_topic: topic_dds,
type_name: typ,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_destroy_publisher(pub_: *mut RmwZerodsPublisher) -> i32 {
if pub_.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let p = unsafe { Box::from_raw(pub_) };
if let Ok(g) = p.inner.lock() {
unsafe { zerodds::zerodds_writer_destroy(*g) };
}
RMW_RET_OK
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_publish(
pub_: *mut RmwZerodsPublisher,
payload: *const u8,
len: usize,
) -> i32 {
if pub_.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let p = unsafe { &*pub_ };
let writer = match p.inner.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_writer_write(writer, payload, len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_create_subscription(
node: *mut RmwZerodsNode,
type_name: *const c_char,
topic_name: *const c_char,
reliable: c_int,
) -> *mut RmwZerodsSubscription {
if node.is_null() || type_name.is_null() || topic_name.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*node };
let ctx = unsafe { &*n.ctx };
let topic_ros = 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 topic_dds = zerodds_ros2_rmw::topic_mangling::mangle_topic_name(
&topic_ros,
zerodds_ros2_rmw::topic_mangling::RosKind::Topic,
)
.unwrap_or_else(|_| topic_ros.clone());
let dds_topic_c = match std::ffi::CString::new(topic_dds.clone()) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let typ_c = match std::ffi::CString::new(typ.clone()) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let reader = unsafe {
zerodds::zerodds_reader_create(
ctx.runtime as *mut zerodds::ZeroDdsRuntime,
dds_topic_c.as_ptr(),
typ_c.as_ptr(),
reliable,
)
};
if reader.is_null() {
return ptr::null_mut();
}
Box::into_raw(Box::new(RmwZerodsSubscription {
inner: Mutex::new(reader),
ros_topic: topic_ros,
dds_topic: topic_dds,
type_name: typ,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_destroy_subscription(sub: *mut RmwZerodsSubscription) -> i32 {
if sub.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let s = unsafe { Box::from_raw(sub) };
if let Ok(g) = s.inner.lock() {
unsafe { zerodds::zerodds_reader_destroy(*g) };
}
RMW_RET_OK
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_take(
sub: *mut RmwZerodsSubscription,
out_buf: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
if sub.is_null() || out_buf.is_null() || out_len.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let s = unsafe { &*sub };
let reader = match s.inner.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_reader_take(reader, out_buf, out_len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_buffer_free(buf: *mut u8, len: usize) {
unsafe { zerodds::zerodds_buffer_free(buf, len) }
}
pub struct RmwZerodsClient {
request_writer: Mutex<*mut zerodds::ZeroDdsWriter>,
reply_reader: Mutex<*mut zerodds::ZeroDdsReader>,
pub service_name: alloc::string::String,
}
pub struct RmwZerodsService {
request_reader: Mutex<*mut zerodds::ZeroDdsReader>,
reply_writer: Mutex<*mut zerodds::ZeroDdsWriter>,
pub service_name: alloc::string::String,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_create_client(
node: *mut RmwZerodsNode,
service_name: *const c_char,
type_name: *const c_char,
) -> *mut RmwZerodsClient {
if node.is_null() || service_name.is_null() || type_name.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*node };
let ctx = unsafe { &*n.ctx };
let service = match unsafe { CStr::from_ptr(service_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(),
};
if zerodds_rpc::topic_naming::validate_service_name(&service).is_err() {
return ptr::null_mut();
}
let req_topic = match zerodds_rpc::topic_naming::request_topic_name(&service) {
Ok(t) => t,
Err(_) => return ptr::null_mut(),
};
let rep_topic = match zerodds_rpc::topic_naming::reply_topic_name(&service) {
Ok(t) => t,
Err(_) => return ptr::null_mut(),
};
let req_topic_c = match std::ffi::CString::new(req_topic) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let rep_topic_c = match std::ffi::CString::new(rep_topic) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let typ_c = match std::ffi::CString::new(typ) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let writer = unsafe {
zerodds::zerodds_writer_create(
ctx.runtime as *mut zerodds::ZeroDdsRuntime,
req_topic_c.as_ptr(),
typ_c.as_ptr(),
1, )
};
let reader = unsafe {
zerodds::zerodds_reader_create(
ctx.runtime as *mut zerodds::ZeroDdsRuntime,
rep_topic_c.as_ptr(),
typ_c.as_ptr(),
1,
)
};
if writer.is_null() || reader.is_null() {
if !writer.is_null() {
unsafe { zerodds::zerodds_writer_destroy(writer) };
}
if !reader.is_null() {
unsafe { zerodds::zerodds_reader_destroy(reader) };
}
return ptr::null_mut();
}
Box::into_raw(Box::new(RmwZerodsClient {
request_writer: Mutex::new(writer),
reply_reader: Mutex::new(reader),
service_name: service,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_destroy_client(client: *mut RmwZerodsClient) -> i32 {
if client.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let c = unsafe { Box::from_raw(client) };
if let Ok(g) = c.request_writer.lock() {
unsafe { zerodds::zerodds_writer_destroy(*g) };
}
if let Ok(g) = c.reply_reader.lock() {
unsafe { zerodds::zerodds_reader_destroy(*g) };
}
RMW_RET_OK
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_send_request(
client: *mut RmwZerodsClient,
payload: *const u8,
len: usize,
) -> i32 {
if client.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let c = unsafe { &*client };
let writer = match c.request_writer.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_writer_write(writer, payload, len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_take_response(
client: *mut RmwZerodsClient,
out_buf: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
if client.is_null() || out_buf.is_null() || out_len.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let c = unsafe { &*client };
let reader = match c.reply_reader.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_reader_take(reader, out_buf, out_len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_create_service(
node: *mut RmwZerodsNode,
service_name: *const c_char,
type_name: *const c_char,
) -> *mut RmwZerodsService {
if node.is_null() || service_name.is_null() || type_name.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*node };
let ctx = unsafe { &*n.ctx };
let service = match unsafe { CStr::from_ptr(service_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(),
};
if zerodds_rpc::topic_naming::validate_service_name(&service).is_err() {
return ptr::null_mut();
}
let req_topic = match zerodds_rpc::topic_naming::request_topic_name(&service) {
Ok(t) => t,
Err(_) => return ptr::null_mut(),
};
let rep_topic = match zerodds_rpc::topic_naming::reply_topic_name(&service) {
Ok(t) => t,
Err(_) => return ptr::null_mut(),
};
let req_topic_c = match std::ffi::CString::new(req_topic) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let rep_topic_c = match std::ffi::CString::new(rep_topic) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let typ_c = match std::ffi::CString::new(typ) {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let reader = unsafe {
zerodds::zerodds_reader_create(
ctx.runtime as *mut zerodds::ZeroDdsRuntime,
req_topic_c.as_ptr(),
typ_c.as_ptr(),
1,
)
};
let writer = unsafe {
zerodds::zerodds_writer_create(
ctx.runtime as *mut zerodds::ZeroDdsRuntime,
rep_topic_c.as_ptr(),
typ_c.as_ptr(),
1,
)
};
if writer.is_null() || reader.is_null() {
if !writer.is_null() {
unsafe { zerodds::zerodds_writer_destroy(writer) };
}
if !reader.is_null() {
unsafe { zerodds::zerodds_reader_destroy(reader) };
}
return ptr::null_mut();
}
Box::into_raw(Box::new(RmwZerodsService {
request_reader: Mutex::new(reader),
reply_writer: Mutex::new(writer),
service_name: service,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_destroy_service(service: *mut RmwZerodsService) -> i32 {
if service.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let s = unsafe { Box::from_raw(service) };
if let Ok(g) = s.request_reader.lock() {
unsafe { zerodds::zerodds_reader_destroy(*g) };
}
if let Ok(g) = s.reply_writer.lock() {
unsafe { zerodds::zerodds_writer_destroy(*g) };
}
RMW_RET_OK
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_take_request(
service: *mut RmwZerodsService,
out_buf: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
if service.is_null() || out_buf.is_null() || out_len.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let s = unsafe { &*service };
let reader = match s.request_reader.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_reader_take(reader, out_buf, out_len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_send_response(
service: *mut RmwZerodsService,
payload: *const u8,
len: usize,
) -> i32 {
if service.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let s = unsafe { &*service };
let writer = match s.reply_writer.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_writer_write(writer, payload, len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
pub struct RmwZerodsWaitSet {
subscriptions: Mutex<Vec<*mut RmwZerodsSubscription>>,
}
#[unsafe(no_mangle)]
pub extern "C" fn rmw_zerodds_create_wait_set() -> *mut RmwZerodsWaitSet {
Box::into_raw(Box::new(RmwZerodsWaitSet {
subscriptions: Mutex::new(Vec::new()),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_destroy_wait_set(ws: *mut RmwZerodsWaitSet) -> i32 {
if ws.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let _ = unsafe { Box::from_raw(ws) };
RMW_RET_OK
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_wait_set_add_subscription(
ws: *mut RmwZerodsWaitSet,
sub: *mut RmwZerodsSubscription,
) -> i32 {
if ws.is_null() || sub.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let w = unsafe { &*ws };
if let Ok(mut g) = w.subscriptions.lock() {
g.push(sub);
RMW_RET_OK
} else {
RMW_RET_ERROR
}
}
fn call_wait_matched(reader: *mut zerodds::ZeroDdsReader) -> i32 {
unsafe { zerodds::zerodds_reader_wait_for_matched(reader, 1, 0) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_wait(ws: *mut RmwZerodsWaitSet, timeout_ms: u64) -> i32 {
if ws.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let w = unsafe { &*ws };
let start = std::time::Instant::now();
let deadline = start + Duration::from_millis(timeout_ms);
loop {
let ready = {
let Ok(g) = w.subscriptions.lock() else {
return RMW_RET_ERROR;
};
g.iter()
.filter(|s| {
if s.is_null() {
return false;
}
let sref: &RmwZerodsSubscription = unsafe { &***s };
if let Ok(reader_g) = sref.inner.lock() {
let rc = call_wait_matched(*reader_g);
rc == 0
} else {
false
}
})
.count()
};
if ready > 0 {
return RMW_RET_OK;
}
let now = std::time::Instant::now();
if now >= deadline {
return RMW_RET_TIMEOUT;
}
let elapsed = now - start;
if elapsed < Duration::from_micros(100) {
for _ in 0..16 {
std::hint::spin_loop();
}
} else if elapsed < Duration::from_millis(10) {
std::thread::sleep(Duration::from_micros(10));
} else if elapsed < Duration::from_millis(100) {
std::thread::sleep(Duration::from_micros(100));
} else {
std::thread::sleep(Duration::from_millis(1));
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_borrow_loaned_message(
pub_: *mut RmwZerodsPublisher,
len: usize,
out_ptr: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
if pub_.is_null() || out_ptr.is_null() || out_len.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let p = unsafe { &*pub_ };
let writer = match p.inner.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_writer_loan_message(writer, len, out_ptr, out_len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_publish_loaned_message(
pub_: *mut RmwZerodsPublisher,
ptr: *mut u8,
len: usize,
) -> i32 {
if pub_.is_null() || ptr.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let p = unsafe { &*pub_ };
let writer = match p.inner.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_writer_commit_loan(writer, ptr, len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_return_loaned_message(
pub_: *mut RmwZerodsPublisher,
ptr: *mut u8,
len: usize,
) -> i32 {
if pub_.is_null() || ptr.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let p = unsafe { &*pub_ };
let writer = match p.inner.lock() {
Ok(g) => *g,
Err(_) => return RMW_RET_ERROR,
};
let rc = unsafe { zerodds::zerodds_writer_discard_loan(writer, ptr, len) };
if rc == 0 { RMW_RET_OK } else { RMW_RET_ERROR }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rmw_zerodds_compute_type_hash(
type_str: *const c_char,
out_hash: *mut u8,
) -> i32 {
if type_str.is_null() || out_hash.is_null() {
return RMW_RET_INVALID_ARGUMENT;
}
let s = match unsafe { CStr::from_ptr(type_str) }.to_bytes_with_nul() {
b if b.len() > 1 => &b[..b.len() - 1],
_ => return RMW_RET_INVALID_ARGUMENT,
};
use sha2::{Digest, Sha256};
let mut h = Sha256::new();
h.update(s);
let result = h.finalize();
unsafe {
core::ptr::copy_nonoverlapping(result.as_ptr(), out_hash, 32);
}
RMW_RET_OK
}
#[cfg(test)]
#[allow(clippy::unwrap_used)] mod tests {
use super::*;
#[test]
fn rmw_codes_match_rep_2007() {
assert_eq!(RMW_RET_OK, 0);
assert_eq!(RMW_RET_ERROR, 1);
assert_eq!(RMW_RET_TIMEOUT, 2);
assert_eq!(RMW_RET_UNSUPPORTED, 3);
assert_eq!(RMW_RET_BAD_ALLOC, 10);
assert_eq!(RMW_RET_INVALID_ARGUMENT, 11);
assert_eq!(RMW_RET_INCORRECT_RMW_IMPLEMENTATION, 12);
}
#[test]
fn impl_identifier_is_static_cstring() {
let p = rmw_zerodds_get_implementation_identifier();
assert!(!p.is_null());
let s = unsafe { CStr::from_ptr(p) }.to_str().unwrap();
assert_eq!(s, "rmw_zerodds_cpp");
}
#[test]
fn serialization_format_is_cdr() {
let p = rmw_zerodds_get_serialization_format();
let s = unsafe { CStr::from_ptr(p) }.to_str().unwrap();
assert_eq!(s, "cdr");
}
#[test]
fn shutdown_null_returns_invalid_argument() {
let r = unsafe { rmw_zerodds_shutdown(ptr::null_mut()) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn destroy_node_null_returns_invalid_argument() {
let r = unsafe { rmw_zerodds_destroy_node(ptr::null_mut()) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn loaning_null_args_rejected() {
let mut p: *mut u8 = ptr::null_mut();
let mut l: usize = 0;
let r = unsafe { rmw_zerodds_borrow_loaned_message(ptr::null_mut(), 64, &mut p, &mut l) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn return_loan_null_args_rejected() {
let r = unsafe { rmw_zerodds_return_loaned_message(ptr::null_mut(), ptr::null_mut(), 0) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn destroy_client_null_returns_invalid_argument() {
let r = unsafe { rmw_zerodds_destroy_client(ptr::null_mut()) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn destroy_service_null_returns_invalid_argument() {
let r = unsafe { rmw_zerodds_destroy_service(ptr::null_mut()) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn wait_set_create_destroy_smoke() {
let ws = rmw_zerodds_create_wait_set();
assert!(!ws.is_null());
let r = unsafe { rmw_zerodds_destroy_wait_set(ws) };
assert_eq!(r, RMW_RET_OK);
}
#[test]
fn wait_returns_invalid_for_null() {
let r = unsafe { rmw_zerodds_wait(ptr::null_mut(), 100) };
assert_eq!(r, RMW_RET_INVALID_ARGUMENT);
}
#[test]
fn type_hash_sha256_is_deterministic() {
let s = std::ffi::CString::new("std_msgs/msg/String").unwrap();
let mut h1 = [0u8; 32];
let mut h2 = [0u8; 32];
let rc1 = unsafe { rmw_zerodds_compute_type_hash(s.as_ptr(), h1.as_mut_ptr()) };
let rc2 = unsafe { rmw_zerodds_compute_type_hash(s.as_ptr(), h2.as_mut_ptr()) };
assert_eq!(rc1, RMW_RET_OK);
assert_eq!(rc2, RMW_RET_OK);
assert_eq!(h1, h2);
assert_ne!(h1, [0u8; 32]);
}
#[test]
fn type_hash_differs_for_different_types() {
let a = std::ffi::CString::new("std_msgs/msg/String").unwrap();
let b = std::ffi::CString::new("std_msgs/msg/Int32").unwrap();
let mut ha = [0u8; 32];
let mut hb = [0u8; 32];
let _ = unsafe { rmw_zerodds_compute_type_hash(a.as_ptr(), ha.as_mut_ptr()) };
let _ = unsafe { rmw_zerodds_compute_type_hash(b.as_ptr(), hb.as_mut_ptr()) };
assert_ne!(ha, hb);
}
#[test]
fn type_hash_null_args_rejected() {
let mut h = [0u8; 32];
let r1 = unsafe { rmw_zerodds_compute_type_hash(ptr::null(), h.as_mut_ptr()) };
assert_eq!(r1, RMW_RET_INVALID_ARGUMENT);
let s = std::ffi::CString::new("x").unwrap();
let r2 = unsafe { rmw_zerodds_compute_type_hash(s.as_ptr(), ptr::null_mut()) };
assert_eq!(r2, RMW_RET_INVALID_ARGUMENT);
}
}