1extern crate alloc;
40
41use alloc::boxed::Box;
42use core::{
43 ffi::{c_char, c_void},
44 marker::PhantomData,
45};
46
47use nros_rmw::{
48 ClientTrait, Publisher, QoSProfile, Rmw, RmwConfig, ServiceTrait, Session, SessionMode,
49 Subscription, TopicInfo, TransportError,
50};
51
52use crate::{
53 EMPTY_VTABLE, MAX_SESSION_PROPERTIES, NROS_RMW_RET_INVALID_ARGUMENT, NROS_RMW_RET_OK,
54 NROS_RMW_RET_UNSUPPORTED, NrosRmwClient, NrosRmwEventCallback, NrosRmwEventKind, NrosRmwNode,
55 NrosRmwPublisher, NrosRmwQos, NrosRmwRet, NrosRmwService, NrosRmwSession,
56 NrosRmwSessionOptions, NrosRmwSubscription, NrosRmwVtable, event_kind_from_c, ret_from_error,
57 rmw_publisher_options_t, rmw_subscription_options_t,
58};
59use crate::generated::{
64 NROS_RMW_DURABILITY_UNKNOWN, NROS_RMW_HISTORY_UNKNOWN, NROS_RMW_RELIABILITY_UNKNOWN,
65 rmw_endpoint_type_t, rmw_gid_t, rmw_liveliness_kind_t, rmw_qos_profile_t,
66 rmw_topic_endpoint_info_t,
67};
68
69#[cfg(all(target_os = "none", not(feature = "std")))]
70mod static_subscriber_storage {
71 use core::{cell::UnsafeCell, mem, ptr};
72
73 use portable_atomic::{AtomicBool, Ordering};
74
75 const SLOT_COUNT: usize = crate::parse_env_usize(
83 env!("NROS_RMW_SUBSCRIBER_SLOTS"),
84 "NROS_RMW_SUBSCRIBER_SLOTS must be a decimal integer",
85 );
86 const SLOT_SIZE: usize = 1024;
87 const SLOT_ALIGN: usize = 16;
88
89 #[repr(align(16))]
90 struct Slot {
91 bytes: UnsafeCell<[u8; SLOT_SIZE]>,
92 }
93
94 const _: () = assert!(mem::align_of::<Slot>() == SLOT_ALIGN);
97
98 unsafe impl Sync for Slot {}
99
100 impl Slot {
101 const fn new() -> Self {
102 Self {
103 bytes: UnsafeCell::new([0; SLOT_SIZE]),
104 }
105 }
106 }
107
108 static USED: [AtomicBool; SLOT_COUNT] = [const { AtomicBool::new(false) }; SLOT_COUNT];
113 static SLOTS: [Slot; SLOT_COUNT] = [const { Slot::new() }; SLOT_COUNT];
114
115 pub unsafe fn insert<T>(value: T) -> Option<*mut T> {
116 if mem::size_of::<T>() > SLOT_SIZE || mem::align_of::<T>() > SLOT_ALIGN {
117 return None;
118 }
119
120 for index in 0..SLOT_COUNT {
121 if USED[index]
122 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
123 .is_err()
124 {
125 continue;
126 }
127
128 let ptr = SLOTS[index].bytes.get().cast::<T>();
129 unsafe { ptr.write(value) };
130 return Some(ptr);
131 }
132
133 None
134 }
135
136 pub unsafe fn take<T>(ptr: *mut T) -> bool {
137 if ptr.is_null() {
138 return false;
139 }
140
141 for index in 0..SLOT_COUNT {
142 let slot_ptr = SLOTS[index].bytes.get().cast::<T>();
143 if ptr != slot_ptr {
144 continue;
145 }
146
147 unsafe { ptr::drop_in_place(ptr) };
148 USED[index].store(false, Ordering::Release);
149 return true;
150 }
151
152 false
153 }
154}
155
156pub trait RustBackend: Sized {
171 type Session: Session<
172 Error = TransportError,
173 PublisherHandle = Self::Publisher,
174 SubscriptionHandle = Self::Subscription,
175 ServiceHandle = Self::Service,
176 ClientHandle = Self::Client,
177 > + 'static;
178 type Publisher: Publisher<Error = TransportError> + 'static;
179 type Subscription: Subscription<Error = TransportError> + 'static;
180 type Service: ServiceTrait<Error = TransportError> + 'static;
181 type Client: ClientTrait<Error = TransportError> + 'static;
182
183 fn factory() -> Self;
188
189 fn open(self, config: &RmwConfig) -> Result<Self::Session, TransportError>;
191}
192
193impl<R> RustBackend for R
194where
195 R: Rmw<Error = TransportError> + Default + Sized,
196 R::Session: Session<Error = TransportError> + 'static,
197 <R::Session as Session>::PublisherHandle: Publisher<Error = TransportError> + 'static,
198 <R::Session as Session>::SubscriptionHandle: Subscription<Error = TransportError> + 'static,
199 <R::Session as Session>::ServiceHandle: ServiceTrait<Error = TransportError> + 'static,
200 <R::Session as Session>::ClientHandle: ClientTrait<Error = TransportError> + 'static,
201{
202 type Session = R::Session;
203 type Publisher = <R::Session as Session>::PublisherHandle;
204 type Subscription = <R::Session as Session>::SubscriptionHandle;
205 type Service = <R::Session as Session>::ServiceHandle;
206 type Client = <R::Session as Session>::ClientHandle;
207
208 fn factory() -> Self {
209 R::default()
210 }
211
212 fn open(self, config: &RmwConfig) -> Result<Self::Session, TransportError> {
213 Rmw::open(self, config)
214 }
215}
216
217unsafe fn cstr_to_str<'a>(ptr: *const core::ffi::c_char) -> &'a str {
229 if ptr.is_null() {
230 return "";
231 }
232 let ptr = ptr.cast::<u8>();
233 let mut len = 0usize;
234 while len < 4096 && unsafe { *ptr.add(len) } != 0 {
238 len += 1;
239 }
240 let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
241 core::str::from_utf8(slice).unwrap_or("")
242}
243
244unsafe fn cstr_to_str_strict<'a>(ptr: *const core::ffi::c_char) -> Option<&'a str> {
252 if ptr.is_null() {
253 return None;
254 }
255 let bytes = ptr.cast::<u8>();
256 let mut len = 0usize;
257 while len < 4096 && unsafe { *bytes.add(len) } != 0 {
258 len += 1;
259 }
260 if len == 0 {
261 return None;
262 }
263 let slice = unsafe { core::slice::from_raw_parts(bytes, len) };
264 core::str::from_utf8(slice).ok()
265}
266
267unsafe fn collect_session_properties<'a>(
274 options: *const NrosRmwSessionOptions,
275 out: &mut [(&'a str, &'a str); MAX_SESSION_PROPERTIES],
276) -> Result<usize, NrosRmwRet> {
277 if options.is_null() {
278 return Ok(0);
279 }
280 let opts = unsafe { &*options };
281 let count = opts.property_count;
282 if count == 0 {
283 return Ok(0);
284 }
285 if opts.properties.is_null() || count > MAX_SESSION_PROPERTIES {
286 return Err(NROS_RMW_RET_INVALID_ARGUMENT);
287 }
288 for (i, slot) in out.iter_mut().enumerate().take(count) {
289 let entry = unsafe { &*opts.properties.add(i) };
290 let (Some(key), Some(value)) = (unsafe { cstr_to_str_strict(entry.key) }, unsafe {
291 cstr_to_str_strict(entry.value)
292 }) else {
293 return Err(NROS_RMW_RET_INVALID_ARGUMENT);
294 };
295 *slot = (key, value);
296 }
297 Ok(count)
298}
299
300fn qos_from_cffi(q: &NrosRmwQos) -> QoSProfile {
304 use crate::generated;
305 use nros_rmw::{
306 QoSDurabilityPolicy, QoSHistoryPolicy, QoSLivelinessPolicy, QoSReliabilityPolicy,
307 };
308 QoSProfile {
322 reliability: match q.reliability as i32 {
323 generated::NROS_RMW_RELIABILITY_SYSTEM_DEFAULT => QoSReliabilityPolicy::SystemDefault,
324 generated::NROS_RMW_RELIABILITY_BEST_EFFORT => QoSReliabilityPolicy::BestEffort,
325 generated::NROS_RMW_RELIABILITY_RELIABLE => QoSReliabilityPolicy::Reliable,
326 _ => QoSReliabilityPolicy::Reliable,
327 },
328 durability: match q.durability as i32 {
329 generated::NROS_RMW_DURABILITY_SYSTEM_DEFAULT => QoSDurabilityPolicy::SystemDefault,
330 generated::NROS_RMW_DURABILITY_TRANSIENT_LOCAL => QoSDurabilityPolicy::TransientLocal,
331 _ => QoSDurabilityPolicy::Volatile,
332 },
333 history: match q.history as i32 {
334 generated::NROS_RMW_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
335 generated::NROS_RMW_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
336 _ => QoSHistoryPolicy::KeepLast,
337 },
338 depth: q.depth as u32,
341 tx_express: false,
344 deadline_ms: q.deadline_ms,
345 lifespan_ms: q.lifespan_ms,
346 liveliness_kind: match q.liveliness_kind as u32 {
347 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_AUTOMATIC => {
348 QoSLivelinessPolicy::Automatic
349 }
350 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_MANUAL_BY_NODE => {
351 QoSLivelinessPolicy::ManualByNode
352 }
353 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_MANUAL_BY_TOPIC => {
354 QoSLivelinessPolicy::ManualByTopic
355 }
356 _ => QoSLivelinessPolicy::None,
357 },
358 liveliness_lease_ms: q.liveliness_lease_ms,
359 avoid_ros_namespace_conventions: q.avoid_ros_namespace_conventions != 0,
360 }
361}
362
363unsafe fn node_name_of<'a>(node: *const NrosRmwNode) -> Option<&'a str> {
370 let name = unsafe { cstr_to_str((*node).name) };
371 if name.is_empty() { None } else { Some(name) }
372}
373
374unsafe fn node_namespace_of<'a>(node: *const NrosRmwNode) -> &'a str {
375 let namespace = unsafe { cstr_to_str((*node).namespace_) };
376 if namespace.is_empty() { "/" } else { namespace }
377}
378
379const FORMAT_NAME_CAP: usize = 32;
396
397const fn format_name_cstr(name: &str) -> [u8; FORMAT_NAME_CAP] {
399 let bytes = name.as_bytes();
400 assert!(
401 bytes.len() < FORMAT_NAME_CAP,
402 "serialization format name does not fit the vtable slot's buffer"
403 );
404 let mut out = [0u8; FORMAT_NAME_CAP];
405 let mut i = 0;
406 while i < bytes.len() {
407 out[i] = bytes[i];
408 i += 1;
409 }
410 out
411}
412
413unsafe extern "C" fn get_serialization_format_trampoline<R: RustBackend>() -> *const c_char {
423 RustBackendAdapter::<R>::SERIALIZATION_FORMAT_CSTR
424 .as_ptr()
425 .cast()
426}
427
428pub struct RustBackendAdapter<R>(PhantomData<R>);
431
432impl<R: RustBackend> RustBackendAdapter<R> {
433 const SERIALIZATION_FORMAT_CSTR: &'static [u8; FORMAT_NAME_CAP] =
441 &format_name_cstr(<R::Session as Session>::SERIALIZATION_FORMAT);
442
443 pub const VTABLE: NrosRmwVtable = NrosRmwVtable {
447 create_session: Some(create_session_trampoline::<R>),
448 destroy_session: Some(destroy_session_trampoline::<R>),
449 drive_io: Some(drive_io_trampoline::<R>),
450 create_publisher: Some(create_publisher_trampoline::<R>),
451 destroy_publisher: Some(destroy_publisher_trampoline::<R>),
452 publish: Some(publish_trampoline::<R>),
453 create_subscription: Some(create_subscription_trampoline::<R>),
454 destroy_subscription: Some(destroy_subscription_trampoline::<R>),
455 take: Some(take_trampoline::<R>),
456 has_data: Some(has_data_trampoline::<R>),
457 create_service: Some(create_service_trampoline::<R>),
458 destroy_service: Some(destroy_service_trampoline::<R>),
459 take_request: Some(take_request_trampoline::<R>),
460 has_request: Some(has_request_trampoline::<R>),
461 send_response: Some(send_response_trampoline::<R>),
462 create_client: Some(create_client_trampoline::<R>),
463 destroy_client: Some(destroy_client_trampoline::<R>),
464 send_request: Some(send_request_trampoline::<R>),
467 take_response: Some(take_response_trampoline::<R>),
468 subscription_event_init: Some(subscription_event_init_trampoline::<R>),
469 publisher_event_init: Some(publisher_event_init_trampoline::<R>),
470 publisher_assert_liveliness: Some(publisher_assert_liveliness_trampoline::<R>),
471 next_deadline_ms: Some(next_deadline_ms_trampoline::<R>),
472 set_wake_callback: Some(set_wake_callback_trampoline::<R>),
473 service_server_is_available: Some(service_server_is_available_trampoline::<R>),
478 take_sequence: Some(take_sequence_trampoline::<R>),
479 publish_streamed: Some(publish_streamed_trampoline::<R>),
480 ping_session: Some(ping_session_trampoline::<R>),
481 subscription_supports_in_place: Some(subscription_supports_in_place_trampoline::<R>),
482 process_raw_in_place: Some(process_raw_in_place_trampoline::<R>),
483 get_node_names: Some(get_node_names_trampoline::<R>),
488 count_publishers: Some(count_publishers_trampoline::<R>),
489 count_subscribers: Some(count_subscribers_trampoline::<R>),
490 get_topic_names_and_types: Some(get_topic_names_and_types_trampoline::<R>),
491 get_service_names_and_types: Some(get_service_names_and_types_trampoline::<R>),
492 get_publisher_names_and_types_by_node: Some(
496 get_publisher_names_and_types_by_node_trampoline::<R>,
497 ),
498 get_subscriber_names_and_types_by_node: Some(
499 get_subscriber_names_and_types_by_node_trampoline::<R>,
500 ),
501 get_service_names_and_types_by_node: Some(
502 get_service_names_and_types_by_node_trampoline::<R>,
503 ),
504 get_client_names_and_types_by_node: Some(
505 get_client_names_and_types_by_node_trampoline::<R>,
506 ),
507 get_publishers_info_by_topic: Some(get_publishers_info_by_topic_trampoline::<R>),
508 get_subscriptions_info_by_topic: Some(get_subscriptions_info_by_topic_trampoline::<R>),
509 get_serialization_format: Some(get_serialization_format_trampoline::<R>),
515 ..EMPTY_VTABLE
516 };
517
518 pub fn register() -> NrosRmwRet {
528 unsafe { crate::nros_rmw_cffi_register_named(c"default".as_ptr(), &Self::VTABLE) }
531 }
532
533 pub unsafe fn register_named(name: *const core::ffi::c_char) -> NrosRmwRet {
539 unsafe { crate::nros_rmw_cffi_register_named(name, &Self::VTABLE) }
542 }
543}
544
545unsafe extern "C" fn create_session_trampoline<R: RustBackend>(
550 locator: *const core::ffi::c_char,
551 mode: u8,
552 domain_id: u32,
553 node_name: *const core::ffi::c_char,
554 options: *const NrosRmwSessionOptions,
555 out: *mut NrosRmwSession,
556) -> NrosRmwRet {
557 if out.is_null() {
558 return NROS_RMW_RET_INVALID_ARGUMENT;
559 }
560 let mut prop_buf: [(&str, &str); MAX_SESSION_PROPERTIES] = [("", ""); MAX_SESSION_PROPERTIES];
575 let prop_count = match unsafe { collect_session_properties(options, &mut prop_buf) } {
576 Ok(n) => n,
577 Err(ret) => return ret,
578 };
579 let cfg = RmwConfig {
580 locator: unsafe { cstr_to_str(locator) },
581 mode: if mode == 0 {
582 SessionMode::Client
583 } else {
584 SessionMode::Peer
585 },
586 domain_id,
587 node_name: unsafe { cstr_to_str(node_name) },
588 namespace: "",
589 properties: &prop_buf[..prop_count],
590 };
591 let factory = R::factory();
592 match factory.open(&cfg) {
593 Ok(session) => {
594 let boxed = Box::into_raw(Box::new(session));
595 unsafe {
596 (*out).backend_data = boxed as *mut c_void;
597 }
598 NROS_RMW_RET_OK
599 }
600 Err(e) => ret_from_error(&e),
601 }
602}
603
604unsafe extern "C" fn destroy_session_trampoline<R: RustBackend>(
605 session: *mut NrosRmwSession,
606) -> NrosRmwRet {
607 let Some(boxed) = (unsafe { take_box::<R::Session>(session_data_mut(session)) }) else {
608 return NROS_RMW_RET_INVALID_ARGUMENT;
609 };
610 let mut s = boxed;
611 let ret = match Session::close(&mut *s) {
612 Ok(()) => NROS_RMW_RET_OK,
613 Err(e) => ret_from_error(&e),
614 };
615 drop(s);
616 ret
617}
618
619unsafe extern "C" fn drive_io_trampoline<R: RustBackend>(
620 session: *mut NrosRmwSession,
621 timeout_ms: i32,
622) -> NrosRmwRet {
623 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
624 return NROS_RMW_RET_INVALID_ARGUMENT;
625 };
626 match Session::drive_io(s, timeout_ms) {
627 Ok(()) => NROS_RMW_RET_OK,
628 Err(e) => ret_from_error(&e),
629 }
630}
631
632unsafe extern "C" fn create_publisher_trampoline<R: RustBackend>(
637 node: *const NrosRmwNode,
638 type_support: *const crate::generated::rmw_message_type_support_t,
641 topic_name: *const core::ffi::c_char,
642 domain_id: u32,
643 qos: *const NrosRmwQos,
644 options: *const rmw_publisher_options_t,
645 out: *mut NrosRmwPublisher,
646) -> NrosRmwRet {
647 let Some(ts) = (unsafe { type_support.as_ref() }) else {
652 return NROS_RMW_RET_INVALID_ARGUMENT;
653 };
654 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
655 if out.is_null() || qos.is_null() {
656 return NROS_RMW_RET_INVALID_ARGUMENT;
657 }
658 if node.is_null() {
662 return NROS_RMW_RET_INVALID_ARGUMENT;
663 }
664 let session = unsafe { (*node).session };
665 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
666 return NROS_RMW_RET_INVALID_ARGUMENT;
667 };
668 let node_name = unsafe { node_name_of(node) };
669 let namespace = unsafe { node_namespace_of(node) };
670 let topic = TopicInfo {
671 name: unsafe { cstr_to_str(topic_name) },
672 type_name: unsafe { cstr_to_str(type_name) },
673 type_hash: unsafe { cstr_to_str(type_hash) },
674 domain_id,
675 node_name,
676 namespace,
677 rx_buffer_hint: 0,
679 tx_express: unsafe { options.as_ref() }.is_some_and(|o| o.tx_express != 0),
682 };
683 let qos_settings = qos_from_cffi(unsafe { &*qos });
684 match Session::create_publisher(s, &topic, qos_settings) {
685 Ok(pub_handle) => {
686 let boxed = Box::into_raw(Box::new(pub_handle));
687 unsafe {
688 (*out).backend_data = boxed as *mut c_void;
689 }
690 NROS_RMW_RET_OK
691 }
692 Err(e) => ret_from_error(&e),
693 }
694}
695
696unsafe extern "C" fn destroy_publisher_trampoline<R: RustBackend>(
697 publisher: *mut NrosRmwPublisher,
698) -> NrosRmwRet {
699 let _ = unsafe { take_box::<R::Publisher>(publisher_data_mut(publisher)) };
700
701 NROS_RMW_RET_OK
702}
703
704unsafe extern "C" fn publish_trampoline<R: RustBackend>(
705 publisher: *const NrosRmwPublisher,
706 payload: crate::generated::rmw_byte_span_t,
708) -> NrosRmwRet {
709 let (data, len) = (payload.data, payload.len);
710 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
711 return NROS_RMW_RET_INVALID_ARGUMENT;
712 };
713 if data.is_null() && len != 0 {
714 return NROS_RMW_RET_INVALID_ARGUMENT;
715 }
716 let slice = unsafe { core::slice::from_raw_parts(data, len) };
717 match Publisher::publish_raw(p, slice) {
718 Ok(()) => NROS_RMW_RET_OK,
719 Err(e) => ret_from_error(&e),
720 }
721}
722
723unsafe extern "C" fn create_subscription_trampoline<R: RustBackend>(
728 node: *const NrosRmwNode,
729 type_support: *const crate::generated::rmw_message_type_support_t,
732 topic_name: *const core::ffi::c_char,
733 domain_id: u32,
734 qos: *const NrosRmwQos,
735 options: *const rmw_subscription_options_t,
736 out: *mut NrosRmwSubscription,
737) -> NrosRmwRet {
738 let Some(ts) = (unsafe { type_support.as_ref() }) else {
743 return NROS_RMW_RET_INVALID_ARGUMENT;
744 };
745 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
746 if out.is_null() || qos.is_null() {
747 return NROS_RMW_RET_INVALID_ARGUMENT;
748 }
749 if node.is_null() {
753 return NROS_RMW_RET_INVALID_ARGUMENT;
754 }
755 let session = unsafe { (*node).session };
756 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
757 return NROS_RMW_RET_INVALID_ARGUMENT;
758 };
759 let node_name = unsafe { node_name_of(node) };
760 let namespace = unsafe { node_namespace_of(node) };
761 let topic = TopicInfo {
762 name: unsafe { cstr_to_str(topic_name) },
763 type_name: unsafe { cstr_to_str(type_name) },
764 type_hash: unsafe { cstr_to_str(type_hash) },
765 domain_id,
766 node_name,
767 namespace,
768 rx_buffer_hint: unsafe { options.as_ref() }.map_or(0, |o| o.rx_buffer_hint as usize),
772 tx_express: false,
774 };
775 let qos_settings = qos_from_cffi(unsafe { &*qos });
776 match Session::create_subscription(s, &topic, qos_settings) {
777 Ok(sub_handle) => {
778 #[cfg(all(target_os = "none", not(feature = "std")))]
779 {
780 let Some(ptr) = (unsafe { static_subscriber_storage::insert(sub_handle) }) else {
781 return crate::NROS_RMW_RET_BAD_ALLOC;
782 };
783 unsafe {
784 (*out).backend_data = ptr as *mut c_void;
785 }
786 NROS_RMW_RET_OK
787 }
788 #[cfg(not(all(target_os = "none", not(feature = "std"))))]
789 {
790 let boxed = Box::into_raw(Box::new(sub_handle));
791 unsafe {
792 (*out).backend_data = boxed as *mut c_void;
793 }
794 NROS_RMW_RET_OK
795 }
796 }
797 Err(e) => ret_from_error(&e),
798 }
799}
800
801unsafe extern "C" fn destroy_subscription_trampoline<R: RustBackend>(
802 subscriber: *mut NrosRmwSubscription,
803) -> NrosRmwRet {
804 let slot = unsafe { subscription_data_mut(subscriber) };
805 #[cfg(all(target_os = "none", not(feature = "std")))]
806 {
807 if unsafe {
808 static_subscriber_storage::take::<R::Subscription>(*slot as *mut R::Subscription)
809 } {
810 *slot = core::ptr::null_mut();
811 return NROS_RMW_RET_OK;
812 }
813 }
814 let _ = unsafe { take_box::<R::Subscription>(slot) };
815
816 NROS_RMW_RET_OK
817}
818
819unsafe extern "C" fn take_trampoline<R: RustBackend>(
820 subscriber: *const NrosRmwSubscription,
821 out: *mut crate::generated::rmw_mut_byte_span_t,
823 taken: *mut bool,
824) -> NrosRmwRet {
825 let Some(span) = (unsafe { out.as_mut() }) else {
826 return NROS_RMW_RET_INVALID_ARGUMENT;
827 };
828 let (buf, buf_len) = (span.data, span.capacity);
829 let out_len: *mut usize = &mut span.len;
830 if out_len.is_null() || taken.is_null() {
834 return NROS_RMW_RET_INVALID_ARGUMENT;
835 }
836 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
837 return NROS_RMW_RET_INVALID_ARGUMENT;
838 };
839 if buf.is_null() && buf_len != 0 {
840 return NROS_RMW_RET_INVALID_ARGUMENT;
841 }
842 let slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
843 let key = unsafe { (*subscriber).backend_data as usize };
844
845 #[cfg(feature = "safety-e2e")]
847 if crate::take_cffi_integrity_request(key) {
848 return match Subscription::take_validated(s, slice) {
849 Ok(Some((n, status))) => {
850 crate::store_cffi_integrity_status(key, status);
851 unsafe {
852 *out_len = n;
853 *taken = true;
854 }
855 NROS_RMW_RET_OK
856 }
857 Ok(None) => {
858 unsafe { *taken = false };
859 NROS_RMW_RET_OK
860 }
861 Err(e) => ret_from_error(&e),
862 };
863 }
864
865 match Subscription::take_serialized_with_info(s, slice) {
866 Ok(Some((n, info))) => {
867 crate::store_cffi_message_info(key, info);
868 unsafe {
869 *out_len = n;
870 *taken = true;
871 }
872 NROS_RMW_RET_OK
873 }
874 Ok(None) => {
875 unsafe { *taken = false };
876 NROS_RMW_RET_OK
877 }
878 Err(e) => ret_from_error(&e),
879 }
880}
881
882unsafe extern "C" fn has_data_trampoline<R: RustBackend>(
883 subscription: *mut NrosRmwSubscription,
884 out_has_data: *mut bool,
885) -> NrosRmwRet {
886 if out_has_data.is_null() {
889 return NROS_RMW_RET_INVALID_ARGUMENT;
890 }
891 let Some(s) = (unsafe { subscription_ref::<R::Subscription>(subscription) }) else {
894 return NROS_RMW_RET_INVALID_ARGUMENT;
895 };
896 unsafe { *out_has_data = Subscription::has_data(s) };
898 NROS_RMW_RET_OK
899}
900
901unsafe extern "C" fn subscription_supports_in_place_trampoline<R: RustBackend>(
904 subscriber: *mut NrosRmwSubscription,
905 out_supports: *mut bool,
906) -> NrosRmwRet {
907 if out_supports.is_null() {
909 return NROS_RMW_RET_INVALID_ARGUMENT;
910 }
911 let Some(s) = (unsafe { subscription_ref::<R::Subscription>(subscriber) }) else {
912 return NROS_RMW_RET_INVALID_ARGUMENT;
913 };
914 unsafe { *out_supports = Subscription::supports_process_in_place(s) };
916 NROS_RMW_RET_OK
917}
918
919unsafe extern "C" fn process_raw_in_place_trampoline<R: RustBackend>(
920 subscriber: *mut NrosRmwSubscription,
921 ctx: *mut core::ffi::c_void,
922 cb: Option<
923 unsafe extern "C" fn(
924 ctx: *mut core::ffi::c_void,
925 message: crate::generated::rmw_byte_span_t,
926 ),
927 >,
928 out_processed: *mut bool,
929) -> NrosRmwRet {
930 if out_processed.is_null() {
933 return NROS_RMW_RET_INVALID_ARGUMENT;
934 }
935 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
936 return NROS_RMW_RET_INVALID_ARGUMENT;
937 };
938 let Some(cb) = cb else {
939 return NROS_RMW_RET_INVALID_ARGUMENT;
940 };
941 match Subscription::process_raw_in_place(s, |raw| unsafe {
942 cb(
943 ctx,
944 crate::generated::rmw_byte_span_t {
945 data: raw.as_ptr(),
946 len: raw.len(),
947 },
948 )
949 }) {
950 Ok(processed) => {
951 unsafe { *out_processed = processed };
953 NROS_RMW_RET_OK
954 }
955 Err(e) => ret_from_error(&e),
956 }
957}
958
959unsafe extern "C" fn create_service_trampoline<R: RustBackend>(
964 node: *const NrosRmwNode,
965 type_support: *const crate::generated::rmw_service_type_support_t,
968 service_name: *const core::ffi::c_char,
969 domain_id: u32,
970 qos: *const NrosRmwQos,
971 out: *mut NrosRmwService,
972) -> NrosRmwRet {
973 let Some(ts) = (unsafe { type_support.as_ref() }) else {
978 return NROS_RMW_RET_INVALID_ARGUMENT;
979 };
980 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
981 if out.is_null() || qos.is_null() {
982 return NROS_RMW_RET_INVALID_ARGUMENT;
983 }
984 if node.is_null() {
988 return NROS_RMW_RET_INVALID_ARGUMENT;
989 }
990 let session = unsafe { (*node).session };
991 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
992 return NROS_RMW_RET_INVALID_ARGUMENT;
993 };
994 let node_name = unsafe { node_name_of(node) };
995 let namespace = unsafe { node_namespace_of(node) };
996 let info = nros_rmw::ServiceInfo {
997 name: unsafe { cstr_to_str(service_name) },
998 type_name: unsafe { cstr_to_str(type_name) },
999 type_hash: unsafe { cstr_to_str(type_hash) },
1000 domain_id,
1001 node_name,
1002 namespace,
1003 };
1004 let qos_settings = qos_from_cffi(unsafe { &*qos });
1005 match Session::create_service(s, &info, qos_settings) {
1006 Ok(server) => {
1007 let boxed = Box::into_raw(Box::new(server));
1008 unsafe {
1009 (*out).backend_data = boxed as *mut c_void;
1010 }
1011 NROS_RMW_RET_OK
1012 }
1013 Err(e) => ret_from_error(&e),
1014 }
1015}
1016
1017unsafe extern "C" fn destroy_service_trampoline<R: RustBackend>(
1018 server: *mut NrosRmwService,
1019) -> NrosRmwRet {
1020 let _ = unsafe { take_box::<R::Service>(service_data_mut(server)) };
1021
1022 NROS_RMW_RET_OK
1023}
1024
1025unsafe extern "C" fn take_request_trampoline<R: RustBackend>(
1026 server: *const NrosRmwService,
1027 request: *mut crate::generated::rmw_mut_byte_span_t,
1028 seq_out: *mut i64,
1029 taken: *mut bool,
1030) -> NrosRmwRet {
1031 if request.is_null() || taken.is_null() {
1034 return NROS_RMW_RET_INVALID_ARGUMENT;
1035 }
1036 let Some(s) = (unsafe { service_mut::<R::Service>(server) }) else {
1037 return NROS_RMW_RET_INVALID_ARGUMENT;
1038 };
1039 let (buf, buf_len) = unsafe { ((*request).data, (*request).capacity) };
1040 let out_len = unsafe { &raw mut (*request).len };
1041 if (buf.is_null() && buf_len != 0) || seq_out.is_null() {
1042 return NROS_RMW_RET_INVALID_ARGUMENT;
1043 }
1044 let slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
1045 let buf_start = slice.as_ptr() as usize;
1046 match ServiceTrait::take_request(s, slice) {
1047 Ok(Some(req)) => {
1048 let offset = (req.data.as_ptr() as usize).saturating_sub(buf_start);
1053 let len = req.data.len();
1054 unsafe {
1055 *seq_out = req.sequence_number;
1056 }
1057 if offset != 0 {
1061 let total = offset + len;
1062 slice.copy_within(offset..total, 0);
1065 }
1066 unsafe {
1068 *out_len = len;
1069 *taken = true;
1070 }
1071 NROS_RMW_RET_OK
1072 }
1073 Ok(None) => {
1074 unsafe { *taken = false };
1076 NROS_RMW_RET_OK
1077 }
1078 Err(e) => ret_from_error(&e),
1079 }
1080}
1081
1082unsafe extern "C" fn has_request_trampoline<R: RustBackend>(
1083 server: *mut NrosRmwService,
1084 out_has_request: *mut bool,
1085) -> NrosRmwRet {
1086 if out_has_request.is_null() {
1088 return NROS_RMW_RET_INVALID_ARGUMENT;
1089 }
1090 let Some(s) = (unsafe { service_ref::<R::Service>(server) }) else {
1091 return NROS_RMW_RET_INVALID_ARGUMENT;
1092 };
1093 unsafe { *out_has_request = ServiceTrait::has_request(s) };
1095 NROS_RMW_RET_OK
1096}
1097
1098unsafe extern "C" fn send_response_trampoline<R: RustBackend>(
1099 server: *const NrosRmwService,
1100 seq: i64,
1101 response: crate::generated::rmw_byte_span_t,
1102) -> NrosRmwRet {
1103 let Some(s) = (unsafe { service_mut::<R::Service>(server) }) else {
1104 return NROS_RMW_RET_INVALID_ARGUMENT;
1105 };
1106 let (data, len) = (response.data, response.len);
1107 if data.is_null() && len != 0 {
1108 return NROS_RMW_RET_INVALID_ARGUMENT;
1109 }
1110 let slice = unsafe { core::slice::from_raw_parts(data, len) };
1111 match ServiceTrait::send_response(s, seq, slice) {
1112 Ok(()) => NROS_RMW_RET_OK,
1113 Err(e) => ret_from_error(&e),
1114 }
1115}
1116
1117unsafe extern "C" fn create_client_trampoline<R: RustBackend>(
1122 node: *const NrosRmwNode,
1123 type_support: *const crate::generated::rmw_service_type_support_t,
1126 service_name: *const core::ffi::c_char,
1127 domain_id: u32,
1128 qos: *const NrosRmwQos,
1129 out: *mut NrosRmwClient,
1130) -> NrosRmwRet {
1131 let Some(ts) = (unsafe { type_support.as_ref() }) else {
1136 return NROS_RMW_RET_INVALID_ARGUMENT;
1137 };
1138 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
1139 if out.is_null() || qos.is_null() {
1140 return NROS_RMW_RET_INVALID_ARGUMENT;
1141 }
1142 if node.is_null() {
1146 return NROS_RMW_RET_INVALID_ARGUMENT;
1147 }
1148 let session = unsafe { (*node).session };
1149 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1150 return NROS_RMW_RET_INVALID_ARGUMENT;
1151 };
1152 let node_name = unsafe { node_name_of(node) };
1153 let namespace = unsafe { node_namespace_of(node) };
1154 let info = nros_rmw::ServiceInfo {
1155 name: unsafe { cstr_to_str(service_name) },
1156 type_name: unsafe { cstr_to_str(type_name) },
1157 type_hash: unsafe { cstr_to_str(type_hash) },
1158 domain_id,
1159 node_name,
1160 namespace,
1161 };
1162 let qos_settings = qos_from_cffi(unsafe { &*qos });
1163 match Session::create_client(s, &info, qos_settings) {
1164 Ok(client) => {
1165 let boxed = Box::into_raw(Box::new(client));
1166 unsafe {
1167 (*out).backend_data = boxed as *mut c_void;
1168 }
1169 NROS_RMW_RET_OK
1170 }
1171 Err(e) => ret_from_error(&e),
1172 }
1173}
1174
1175unsafe extern "C" fn destroy_client_trampoline<R: RustBackend>(
1176 client: *mut NrosRmwClient,
1177) -> NrosRmwRet {
1178 let _ = unsafe { take_box::<R::Client>(client_data_mut(client)) };
1179
1180 NROS_RMW_RET_OK
1181}
1182
1183unsafe extern "C" fn send_request_trampoline<R: RustBackend>(
1186 client: *const NrosRmwClient,
1187 request: crate::generated::rmw_byte_span_t,
1188 sequence_id: *mut i64,
1189) -> NrosRmwRet {
1190 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1191 return NROS_RMW_RET_INVALID_ARGUMENT;
1192 };
1193 let (request, req_len) = (request.data, request.len);
1194 if request.is_null() && req_len != 0 {
1195 return NROS_RMW_RET_INVALID_ARGUMENT;
1196 }
1197 let req = unsafe { core::slice::from_raw_parts(request, req_len) };
1198 match ClientTrait::send_request_raw(c, req) {
1199 Ok(seq) => {
1200 if !sequence_id.is_null() {
1203 unsafe { *sequence_id = seq };
1205 }
1206 NROS_RMW_RET_OK
1207 }
1208 Err(e) => ret_from_error(&e),
1209 }
1210}
1211
1212unsafe extern "C" fn take_response_trampoline<R: RustBackend>(
1213 client: *const NrosRmwClient,
1214 reply: *mut crate::generated::rmw_mut_byte_span_t,
1215 seq_out: *mut i64,
1216 taken: *mut bool,
1217) -> NrosRmwRet {
1218 if reply.is_null() || taken.is_null() {
1221 return NROS_RMW_RET_INVALID_ARGUMENT;
1222 }
1223 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1224 return NROS_RMW_RET_INVALID_ARGUMENT;
1225 };
1226 let (reply_buf, reply_buf_len) = unsafe { ((*reply).data, (*reply).capacity) };
1227 let out_len = unsafe { &raw mut (*reply).len };
1228 if reply_buf.is_null() && reply_buf_len != 0 {
1229 return NROS_RMW_RET_INVALID_ARGUMENT;
1230 }
1231 let reply = unsafe { core::slice::from_raw_parts_mut(reply_buf, reply_buf_len) };
1232 match ClientTrait::take_response_raw(c, reply) {
1233 Ok(Some((n, seq))) => {
1234 unsafe {
1236 *out_len = n;
1237 *taken = true;
1238 if !seq_out.is_null() {
1239 *seq_out = seq;
1240 }
1241 }
1242 NROS_RMW_RET_OK
1243 }
1244 Ok(None) => {
1245 unsafe { *taken = false };
1247 NROS_RMW_RET_OK
1248 }
1249 Err(e) => ret_from_error(&e),
1250 }
1251}
1252
1253const _: () = {
1278 use core::mem::{align_of, size_of};
1279 assert!(
1280 size_of::<crate::NrosRmwLivelinessChangedStatus>()
1281 == size_of::<nros_rmw::LivelinessChangedStatus>()
1282 );
1283 assert!(
1284 align_of::<crate::NrosRmwLivelinessChangedStatus>()
1285 == align_of::<nros_rmw::LivelinessChangedStatus>()
1286 );
1287 assert!(size_of::<crate::NrosRmwCountStatus>() == size_of::<nros_rmw::CountStatus>());
1288 assert!(align_of::<crate::NrosRmwCountStatus>() == align_of::<nros_rmw::CountStatus>());
1289 use crate::rmw_event_type_t as ck;
1292 assert!(
1293 ck::NROS_RMW_EVENT_LIVELINESS_CHANGED == nros_rmw::EventKind::LivelinessChanged as ck::Type
1294 );
1295 assert!(
1296 ck::NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED
1297 == nros_rmw::EventKind::RequestedDeadlineMissed as ck::Type
1298 );
1299 assert!(ck::NROS_RMW_EVENT_MESSAGE_LOST == nros_rmw::EventKind::MessageLost as ck::Type);
1300 assert!(ck::NROS_RMW_EVENT_LIVELINESS_LOST == nros_rmw::EventKind::LivelinessLost as ck::Type);
1301 assert!(
1302 ck::NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED
1303 == nros_rmw::EventKind::OfferedDeadlineMissed as ck::Type
1304 );
1305};
1306
1307unsafe extern "C" fn subscription_event_init_trampoline<R: RustBackend>(
1308 subscriber: *const NrosRmwSubscription,
1309 kind: NrosRmwEventKind,
1310 deadline_ms: u32,
1311 cb: NrosRmwEventCallback,
1312 user_context: *mut c_void,
1313) -> NrosRmwRet {
1314 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1315 return NROS_RMW_RET_INVALID_ARGUMENT;
1316 };
1317 let trait_kind: nros_rmw::EventKind = event_kind_from_c(kind);
1321 let Some(cb) = cb else {
1324 return NROS_RMW_RET_INVALID_ARGUMENT;
1325 };
1326 let trait_cb: nros_rmw::EventCallback = unsafe { core::mem::transmute(cb) };
1330 let res = unsafe {
1331 Subscription::register_event_callback(s, trait_kind, deadline_ms, trait_cb, user_context)
1332 };
1333 match res {
1334 Ok(()) => NROS_RMW_RET_OK,
1335 Err(e) => {
1336 let _ = e;
1344 NROS_RMW_RET_UNSUPPORTED
1345 }
1346 }
1347}
1348
1349unsafe extern "C" fn publisher_event_init_trampoline<R: RustBackend>(
1350 publisher: *const NrosRmwPublisher,
1351 kind: NrosRmwEventKind,
1352 deadline_ms: u32,
1353 cb: NrosRmwEventCallback,
1354 user_context: *mut c_void,
1355) -> NrosRmwRet {
1356 if publisher.is_null() {
1359 return NROS_RMW_RET_INVALID_ARGUMENT;
1360 }
1361 let p_ptr = unsafe { (*publisher).backend_data } as *mut R::Publisher;
1362 if p_ptr.is_null() {
1363 return NROS_RMW_RET_INVALID_ARGUMENT;
1364 }
1365 let p = unsafe { &mut *p_ptr };
1366 let trait_kind: nros_rmw::EventKind = event_kind_from_c(kind);
1368 let Some(cb) = cb else {
1369 return NROS_RMW_RET_INVALID_ARGUMENT;
1370 };
1371 let trait_cb: nros_rmw::EventCallback = unsafe { core::mem::transmute(cb) };
1372 let res = unsafe {
1373 Publisher::register_event_callback(p, trait_kind, deadline_ms, trait_cb, user_context)
1374 };
1375 match res {
1376 Ok(()) => NROS_RMW_RET_OK,
1377 Err(_) => NROS_RMW_RET_UNSUPPORTED,
1378 }
1379}
1380
1381unsafe extern "C" fn publisher_assert_liveliness_trampoline<R: RustBackend>(
1382 publisher: *const NrosRmwPublisher,
1383) -> NrosRmwRet {
1384 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1385 return NROS_RMW_RET_INVALID_ARGUMENT;
1386 };
1387 match Publisher::assert_liveliness(p) {
1388 Ok(()) => NROS_RMW_RET_OK,
1389 Err(e) => ret_from_error(&e),
1390 }
1391}
1392
1393unsafe extern "C" fn next_deadline_ms_trampoline<R: RustBackend>(
1394 session: *const NrosRmwSession,
1395 out_ms: *mut u32,
1396 has_deadline: *mut bool,
1397) -> NrosRmwRet {
1398 if out_ms.is_null() || has_deadline.is_null() {
1400 return NROS_RMW_RET_INVALID_ARGUMENT;
1401 }
1402 let Some(s) = (unsafe { session_ref::<R::Session>(session) }) else {
1405 return NROS_RMW_RET_INVALID_ARGUMENT;
1406 };
1407 match Session::next_deadline_ms(s) {
1408 Some(ms) => {
1409 unsafe {
1411 *out_ms = ms;
1412 *has_deadline = true;
1413 }
1414 }
1415 None => {
1416 unsafe { *has_deadline = false };
1418 }
1419 }
1420 NROS_RMW_RET_OK
1421}
1422
1423unsafe extern "C" fn set_wake_callback_trampoline<R: RustBackend>(
1424 session: *mut NrosRmwSession,
1425 cb: Option<unsafe extern "C" fn(ctx: *mut core::ffi::c_void)>,
1426 ctx: *mut core::ffi::c_void,
1427) -> NrosRmwRet {
1428 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1429 return NROS_RMW_RET_INVALID_ARGUMENT;
1430 };
1431 unsafe { Session::set_wake_callback(s, cb, ctx) };
1436 NROS_RMW_RET_OK
1437}
1438
1439unsafe extern "C" fn service_server_is_available_trampoline<R: RustBackend>(
1440 client: *const NrosRmwClient,
1441 out_available: *mut bool,
1442) -> NrosRmwRet {
1443 if out_available.is_null() {
1454 return NROS_RMW_RET_INVALID_ARGUMENT;
1455 }
1456 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1457 return NROS_RMW_RET_INVALID_ARGUMENT;
1458 };
1459 match ClientTrait::service_is_ready(c) {
1460 Ok(available) => {
1461 unsafe { *out_available = available };
1463 NROS_RMW_RET_OK
1464 }
1465 Err(e) => ret_from_error(&e),
1466 }
1467}
1468
1469unsafe extern "C" fn ping_session_trampoline<R: RustBackend>(
1470 session: *mut NrosRmwSession,
1471 timeout_ms: i32,
1472) -> NrosRmwRet {
1473 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1478 return NROS_RMW_RET_INVALID_ARGUMENT;
1479 };
1480 match Session::ping_session(s, timeout_ms) {
1481 Ok(()) => NROS_RMW_RET_OK,
1482 Err(e) => ret_from_error(&e),
1483 }
1484}
1485
1486unsafe extern "C" fn get_node_names_trampoline<R: RustBackend>(
1496 session: *const NrosRmwSession,
1497 visitor: crate::generated::rmw_node_visitor_t,
1500) -> NrosRmwRet {
1501 let (visit, ctx) = (visitor.visit, visitor.ctx);
1502 let Some(visit) = visit else {
1503 return NROS_RMW_RET_INVALID_ARGUMENT;
1504 };
1505 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
1506 return NROS_RMW_RET_INVALID_ARGUMENT;
1507 };
1508
1509 const NAME_MAX: usize = 256;
1511
1512 let mut cb = |name: &str, namespace: &str, enclave: Option<&str>| -> bool {
1513 let mut name_buf = [0u8; NAME_MAX];
1514 let mut ns_buf = [0u8; NAME_MAX];
1515 let mut enc_buf = [0u8; NAME_MAX];
1516 if name.len() >= NAME_MAX || namespace.len() >= NAME_MAX {
1520 return true; }
1522 name_buf[..name.len()].copy_from_slice(name.as_bytes());
1523 ns_buf[..namespace.len()].copy_from_slice(namespace.as_bytes());
1524 let enc_ptr = match enclave {
1525 Some(e) if e.len() < NAME_MAX => {
1526 enc_buf[..e.len()].copy_from_slice(e.as_bytes());
1527 enc_buf.as_ptr() as *const c_char
1528 }
1529 _ => core::ptr::null(),
1533 };
1534 unsafe {
1535 visit(
1536 ctx,
1537 name_buf.as_ptr() as *const c_char,
1538 ns_buf.as_ptr() as *const c_char,
1539 enc_ptr,
1540 )
1541 }
1542 };
1543
1544 match Session::get_node_names(s, &mut cb) {
1545 Ok(()) => NROS_RMW_RET_OK,
1546 Err(e) => ret_from_error(&e),
1547 }
1548}
1549
1550macro_rules! count_trampoline {
1552 ($name:ident, $method:ident) => {
1553 unsafe extern "C" fn $name<R: RustBackend>(
1554 session: *const NrosRmwSession,
1555 topic_name: *const c_char,
1556 count: *mut usize,
1557 ) -> NrosRmwRet {
1558 if topic_name.is_null() || count.is_null() {
1559 return NROS_RMW_RET_INVALID_ARGUMENT;
1560 }
1561 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
1562 return NROS_RMW_RET_INVALID_ARGUMENT;
1563 };
1564 let Ok(topic) = (unsafe { core::ffi::CStr::from_ptr(topic_name) }).to_str() else {
1565 return NROS_RMW_RET_INVALID_ARGUMENT;
1566 };
1567 match Session::$method(s, topic) {
1568 Ok(n) => {
1569 unsafe { *count = n };
1570 NROS_RMW_RET_OK
1571 }
1572 Err(e) => ret_from_error(&e),
1573 }
1574 }
1575 };
1576}
1577
1578count_trampoline!(count_publishers_trampoline, count_publishers);
1579count_trampoline!(count_subscribers_trampoline, count_subscribers);
1580
1581macro_rules! names_and_types_trampoline {
1591 ($name:ident, $method:ident, demangle_flag) => {
1594 unsafe extern "C" fn $name<R: RustBackend>(
1595 session: *const NrosRmwSession,
1596 no_demangle: bool,
1597 visitor: crate::generated::rmw_names_and_types_visitor_t,
1599 ) -> NrosRmwRet {
1600 if no_demangle {
1605 return NROS_RMW_RET_UNSUPPORTED;
1606 }
1607 $crate::names_and_types_body!(R, $method, session, visitor)
1608 }
1609 };
1610 ($name:ident, $method:ident) => {
1611 unsafe extern "C" fn $name<R: RustBackend>(
1612 session: *const NrosRmwSession,
1613 visitor: crate::generated::rmw_names_and_types_visitor_t,
1615 ) -> NrosRmwRet {
1616 $crate::names_and_types_body!(R, $method, session, visitor)
1617 }
1618 };
1619}
1620
1621#[macro_export]
1623#[doc(hidden)]
1624macro_rules! names_and_types_body {
1625 ($R:ident, $method:ident, $session:ident, $visitor:ident) => {{
1626 let (visit_fn, ctx) = ($visitor.visit, $visitor.ctx);
1629 let Some(visit) = visit_fn else {
1630 return NROS_RMW_RET_INVALID_ARGUMENT;
1631 };
1632 let Some(s) = (unsafe { session_mut::<$R::Session>($session.cast_mut()) }) else {
1633 return NROS_RMW_RET_INVALID_ARGUMENT;
1634 };
1635
1636 const NAME_MAX: usize = 256;
1637 const TYPES_MAX: usize = 8;
1638
1639 let mut cb = |name: &str, types: &[&str]| -> bool {
1640 let mut name_buf = [0u8; NAME_MAX];
1641 if name.len() >= NAME_MAX {
1642 return true; }
1644 name_buf[..name.len()].copy_from_slice(name.as_bytes());
1645
1646 let mut type_bufs = [[0u8; NAME_MAX]; TYPES_MAX];
1647 let mut ptrs: [*const c_char; TYPES_MAX] = [core::ptr::null(); TYPES_MAX];
1648 let mut n = 0usize;
1649 for t in types.iter().take(TYPES_MAX) {
1650 if t.len() >= NAME_MAX {
1651 continue;
1652 }
1653 type_bufs[n][..t.len()].copy_from_slice(t.as_bytes());
1654 ptrs[n] = type_bufs[n].as_ptr() as *const c_char;
1655 n += 1;
1656 }
1657 unsafe { visit(ctx, name_buf.as_ptr() as *const c_char, ptrs.as_ptr(), n) }
1658 };
1659
1660 match Session::$method(s, &mut cb) {
1661 Ok(()) => NROS_RMW_RET_OK,
1662 Err(e) => ret_from_error(&e),
1663 }
1664 }};
1665}
1666
1667names_and_types_trampoline!(
1668 get_topic_names_and_types_trampoline,
1669 get_topic_names_and_types,
1670 demangle_flag
1671);
1672names_and_types_trampoline!(
1673 get_service_names_and_types_trampoline,
1674 get_service_names_and_types
1675);
1676
1677unsafe fn by_node_impl<R: RustBackend>(
1686 session: *const NrosRmwSession,
1687 node_name: *const c_char,
1688 node_namespace: *const c_char,
1689 no_demangle: bool,
1690 visitor: crate::generated::rmw_names_and_types_visitor_t,
1692 kind: nros_rmw::GraphEntityKind,
1693) -> NrosRmwRet {
1694 let (visit, ctx) = (visitor.visit, visitor.ctx);
1695 if no_demangle {
1699 return NROS_RMW_RET_UNSUPPORTED;
1700 }
1701 if node_name.is_null() || node_namespace.is_null() {
1702 return NROS_RMW_RET_INVALID_ARGUMENT;
1703 }
1704 let Some(visit) = visit else {
1705 return NROS_RMW_RET_INVALID_ARGUMENT;
1706 };
1707 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
1708 return NROS_RMW_RET_INVALID_ARGUMENT;
1709 };
1710 let (Ok(name), Ok(ns)) = (
1711 unsafe { core::ffi::CStr::from_ptr(node_name) }.to_str(),
1712 unsafe { core::ffi::CStr::from_ptr(node_namespace) }.to_str(),
1713 ) else {
1714 return NROS_RMW_RET_INVALID_ARGUMENT;
1715 };
1716
1717 const NAME_MAX: usize = 256;
1718 const TYPES_MAX: usize = 8;
1719 let mut cb = |n: &str, types: &[&str]| -> bool {
1720 let mut name_buf = [0u8; NAME_MAX];
1721 if n.len() >= NAME_MAX {
1722 return true;
1723 }
1724 name_buf[..n.len()].copy_from_slice(n.as_bytes());
1725 let mut type_bufs = [[0u8; NAME_MAX]; TYPES_MAX];
1726 let mut ptrs: [*const c_char; TYPES_MAX] = [core::ptr::null(); TYPES_MAX];
1727 let mut count = 0usize;
1728 for t in types.iter().take(TYPES_MAX) {
1729 if t.len() >= NAME_MAX {
1730 continue;
1731 }
1732 type_bufs[count][..t.len()].copy_from_slice(t.as_bytes());
1733 ptrs[count] = type_bufs[count].as_ptr() as *const c_char;
1734 count += 1;
1735 }
1736 unsafe {
1737 visit(
1738 ctx,
1739 name_buf.as_ptr() as *const c_char,
1740 ptrs.as_ptr(),
1741 count,
1742 )
1743 }
1744 };
1745 match Session::get_names_and_types_by_node(s, kind, name, ns, &mut cb) {
1746 Ok(()) => NROS_RMW_RET_OK,
1747 Err(e) => ret_from_error(&e),
1748 }
1749}
1750
1751macro_rules! by_node_trampoline {
1752 ($name:ident, $kind:ident, no_demangle) => {
1757 unsafe extern "C" fn $name<R: RustBackend>(
1758 session: *const NrosRmwSession,
1759 node_name: *const c_char,
1760 node_namespace: *const c_char,
1761 no_demangle: bool,
1762 visitor: crate::generated::rmw_names_and_types_visitor_t,
1764 ) -> NrosRmwRet {
1765 unsafe {
1766 by_node_impl::<R>(
1767 session,
1768 node_name,
1769 node_namespace,
1770 no_demangle,
1771 visitor,
1772 nros_rmw::GraphEntityKind::$kind,
1773 )
1774 }
1775 }
1776 };
1777 ($name:ident, $kind:ident) => {
1779 unsafe extern "C" fn $name<R: RustBackend>(
1780 session: *const NrosRmwSession,
1781 node_name: *const c_char,
1782 node_namespace: *const c_char,
1783 visitor: crate::generated::rmw_names_and_types_visitor_t,
1785 ) -> NrosRmwRet {
1786 unsafe {
1787 by_node_impl::<R>(
1788 session,
1789 node_name,
1790 node_namespace,
1791 false,
1792 visitor,
1793 nros_rmw::GraphEntityKind::$kind,
1794 )
1795 }
1796 }
1797 };
1798}
1799
1800by_node_trampoline!(
1801 get_publisher_names_and_types_by_node_trampoline,
1802 Publisher,
1803 no_demangle
1804);
1805by_node_trampoline!(
1806 get_subscriber_names_and_types_by_node_trampoline,
1807 Subscriber,
1808 no_demangle
1809);
1810by_node_trampoline!(get_service_names_and_types_by_node_trampoline, Service);
1811by_node_trampoline!(get_client_names_and_types_by_node_trampoline, Client);
1812
1813unsafe fn endpoint_info_impl<R: RustBackend>(
1818 session: *const NrosRmwSession,
1819 topic_name: *const c_char,
1820 no_mangle: bool,
1821 visitor: crate::generated::rmw_topic_endpoint_info_visitor_t,
1823 publishers: bool,
1824) -> NrosRmwRet {
1825 let (visit, ctx) = (visitor.visit, visitor.ctx);
1826 if no_mangle {
1827 return NROS_RMW_RET_UNSUPPORTED;
1828 }
1829 if topic_name.is_null() {
1830 return NROS_RMW_RET_INVALID_ARGUMENT;
1831 }
1832 let Some(visit) = visit else {
1833 return NROS_RMW_RET_INVALID_ARGUMENT;
1834 };
1835 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
1836 return NROS_RMW_RET_INVALID_ARGUMENT;
1837 };
1838 let Ok(topic) = (unsafe { core::ffi::CStr::from_ptr(topic_name) }).to_str() else {
1839 return NROS_RMW_RET_INVALID_ARGUMENT;
1840 };
1841
1842 const NAME_MAX: usize = 256;
1843 let mut cb = |info: &nros_rmw::GraphEndpointInfo<'_>| -> bool {
1844 let mut name_buf = [0u8; NAME_MAX];
1845 let mut ns_buf = [0u8; NAME_MAX];
1846 let mut ty_buf = [0u8; NAME_MAX];
1847 if info.node_name.len() >= NAME_MAX
1848 || info.node_namespace.len() >= NAME_MAX
1849 || info.topic_type.len() >= NAME_MAX
1850 {
1851 return true; }
1853 name_buf[..info.node_name.len()].copy_from_slice(info.node_name.as_bytes());
1854 ns_buf[..info.node_namespace.len()].copy_from_slice(info.node_namespace.as_bytes());
1855 ty_buf[..info.topic_type.len()].copy_from_slice(info.topic_type.as_bytes());
1856 let mut qos: rmw_qos_profile_t = unsafe { core::mem::zeroed() };
1868 qos.reliability = NROS_RMW_RELIABILITY_UNKNOWN as u8;
1869 qos.durability = NROS_RMW_DURABILITY_UNKNOWN as u8;
1870 qos.history = NROS_RMW_HISTORY_UNKNOWN as u8;
1871 qos.liveliness_kind = rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_UNKNOWN as u8;
1872 let c_info = rmw_topic_endpoint_info_t {
1873 node_name: name_buf.as_ptr() as *const c_char,
1874 node_namespace: ns_buf.as_ptr() as *const c_char,
1875 topic_type: ty_buf.as_ptr() as *const c_char,
1876 endpoint_type: if info.is_publisher {
1877 rmw_endpoint_type_t::RMW_ENDPOINT_PUBLISHER
1878 } else {
1879 rmw_endpoint_type_t::RMW_ENDPOINT_SUBSCRIPTION
1880 },
1881 endpoint_gid: rmw_gid_t {
1882 implementation_identifier: core::ptr::null(),
1883 data: info.endpoint_gid,
1884 },
1885 qos_profile: qos,
1886 };
1887 unsafe { visit(ctx, &c_info as *const rmw_topic_endpoint_info_t) }
1888 };
1889 match Session::get_endpoint_info_by_topic(s, publishers, topic, &mut cb) {
1890 Ok(()) => NROS_RMW_RET_OK,
1891 Err(e) => ret_from_error(&e),
1892 }
1893}
1894
1895macro_rules! endpoint_info_trampoline {
1896 ($name:ident, $publishers:expr) => {
1897 unsafe extern "C" fn $name<R: RustBackend>(
1898 session: *const NrosRmwSession,
1899 topic_name: *const c_char,
1900 no_mangle: bool,
1901 visitor: crate::generated::rmw_topic_endpoint_info_visitor_t,
1903 ) -> NrosRmwRet {
1904 unsafe { endpoint_info_impl::<R>(session, topic_name, no_mangle, visitor, $publishers) }
1905 }
1906 };
1907}
1908
1909endpoint_info_trampoline!(get_publishers_info_by_topic_trampoline, true);
1910endpoint_info_trampoline!(get_subscriptions_info_by_topic_trampoline, false);
1911
1912unsafe extern "C" fn publish_streamed_trampoline<R: RustBackend>(
1913 publisher: *mut NrosRmwPublisher,
1914 size_cb: Option<unsafe extern "C" fn(out_total_len: *mut usize, user_ctx: *mut c_void)>,
1915 chunk_cb: Option<
1916 unsafe extern "C" fn(
1917 out_buf: *mut u8,
1918 cap: usize,
1919 out_written: *mut usize,
1920 user_ctx: *mut c_void,
1921 ),
1922 >,
1923 user_ctx: *mut c_void,
1924) -> NrosRmwRet {
1925 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1931 return NROS_RMW_RET_INVALID_ARGUMENT;
1932 };
1933 let (Some(size_cb), Some(chunk_cb)) = (size_cb, chunk_cb) else {
1935 return NROS_RMW_RET_INVALID_ARGUMENT;
1936 };
1937 match unsafe { Publisher::publish_streamed(p, size_cb, chunk_cb, user_ctx) } {
1940 Ok(()) => NROS_RMW_RET_OK,
1941 Err(e) => ret_from_error(&e),
1942 }
1943}
1944
1945unsafe extern "C" fn take_sequence_trampoline<R: RustBackend>(
1946 subscriber: *const NrosRmwSubscription,
1947 buf: *mut u8,
1948 per_msg_cap: usize,
1949 max_msgs: usize,
1950 out_lens: *mut usize,
1951 taken: *mut usize,
1952) -> NrosRmwRet {
1953 if taken.is_null() {
1956 return NROS_RMW_RET_INVALID_ARGUMENT;
1957 }
1958 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1963 return NROS_RMW_RET_INVALID_ARGUMENT;
1964 };
1965 if buf.is_null() || out_lens.is_null() || per_msg_cap == 0 {
1966 return NROS_RMW_RET_INVALID_ARGUMENT;
1967 }
1968 let buf_slice =
1973 unsafe { core::slice::from_raw_parts_mut(buf, max_msgs.saturating_mul(per_msg_cap)) };
1974 let lens_slice = unsafe { core::slice::from_raw_parts_mut(out_lens, max_msgs) };
1975 match Subscription::take_sequence(s, buf_slice, per_msg_cap, max_msgs, lens_slice) {
1976 Ok(count) => {
1977 unsafe { *taken = count };
1979 NROS_RMW_RET_OK
1980 }
1981 Err(e) => ret_from_error(&e),
1982 }
1983}
1984
1985#[inline]
2003unsafe fn session_data_mut(session: *mut NrosRmwSession) -> &'static mut *mut c_void {
2004 unsafe { &mut (*session).backend_data }
2005}
2006
2007#[inline]
2008unsafe fn publisher_data_mut(publisher: *mut NrosRmwPublisher) -> &'static mut *mut c_void {
2009 unsafe { &mut (*publisher).backend_data }
2010}
2011
2012#[inline]
2013unsafe fn subscription_data_mut(subscriber: *mut NrosRmwSubscription) -> &'static mut *mut c_void {
2014 unsafe { &mut (*subscriber).backend_data }
2015}
2016
2017#[inline]
2018unsafe fn service_data_mut(server: *mut NrosRmwService) -> &'static mut *mut c_void {
2019 unsafe { &mut (*server).backend_data }
2020}
2021
2022#[inline]
2023unsafe fn client_data_mut(client: *mut NrosRmwClient) -> &'static mut *mut c_void {
2024 unsafe { &mut (*client).backend_data }
2025}
2026
2027#[inline]
2028unsafe fn session_mut<'a, T>(session: *mut NrosRmwSession) -> Option<&'a mut T> {
2029 if session.is_null() {
2030 return None;
2031 }
2032 let p = unsafe { (*session).backend_data } as *mut T;
2033 if p.is_null() {
2034 None
2035 } else {
2036 Some(unsafe { &mut *p })
2037 }
2038}
2039
2040#[inline]
2041unsafe fn session_ref<'a, T>(session: *const NrosRmwSession) -> Option<&'a T> {
2042 if session.is_null() {
2043 return None;
2044 }
2045 let p = unsafe { (*session).backend_data } as *const T;
2046 if p.is_null() {
2047 None
2048 } else {
2049 Some(unsafe { &*p })
2050 }
2051}
2052
2053#[inline]
2054unsafe fn publisher_ref<'a, T>(publisher: *const NrosRmwPublisher) -> Option<&'a T> {
2055 if publisher.is_null() {
2056 return None;
2057 }
2058 let p = unsafe { (*publisher).backend_data } as *const T;
2059 if p.is_null() {
2060 None
2061 } else {
2062 Some(unsafe { &*p })
2063 }
2064}
2065
2066#[inline]
2067unsafe fn subscription_mut<'a, T>(subscriber: *const NrosRmwSubscription) -> Option<&'a mut T> {
2068 if subscriber.is_null() {
2069 return None;
2070 }
2071 let p = unsafe { (*subscriber).backend_data } as *mut T;
2072 if p.is_null() {
2073 None
2074 } else {
2075 Some(unsafe { &mut *p })
2076 }
2077}
2078
2079#[inline]
2080unsafe fn subscription_ref<'a, T>(subscriber: *const NrosRmwSubscription) -> Option<&'a T> {
2081 if subscriber.is_null() {
2082 return None;
2083 }
2084 let p = unsafe { (*subscriber).backend_data } as *const T;
2085 if p.is_null() {
2086 None
2087 } else {
2088 Some(unsafe { &*p })
2089 }
2090}
2091
2092#[inline]
2093unsafe fn service_mut<'a, T>(server: *const NrosRmwService) -> Option<&'a mut T> {
2094 if server.is_null() {
2095 return None;
2096 }
2097 let p = unsafe { (*server).backend_data } as *mut T;
2098 if p.is_null() {
2099 None
2100 } else {
2101 Some(unsafe { &mut *p })
2102 }
2103}
2104
2105#[inline]
2106unsafe fn service_ref<'a, T>(server: *const NrosRmwService) -> Option<&'a T> {
2107 if server.is_null() {
2108 return None;
2109 }
2110 let p = unsafe { (*server).backend_data } as *const T;
2111 if p.is_null() {
2112 None
2113 } else {
2114 Some(unsafe { &*p })
2115 }
2116}
2117
2118#[inline]
2119unsafe fn client_mut<'a, T>(client: *const NrosRmwClient) -> Option<&'a mut T> {
2120 if client.is_null() {
2121 return None;
2122 }
2123 let p = unsafe { (*client).backend_data } as *mut T;
2124 if p.is_null() {
2125 None
2126 } else {
2127 Some(unsafe { &mut *p })
2128 }
2129}
2130
2131#[inline]
2132unsafe fn take_box<T>(slot: &mut *mut c_void) -> Option<Box<T>> {
2133 if slot.is_null() {
2134 return None;
2135 }
2136 let ptr = *slot as *mut T;
2137 *slot = core::ptr::null_mut();
2138 if ptr.is_null() {
2139 None
2140 } else {
2141 Some(unsafe { Box::from_raw(ptr) })
2146 }
2147}
2148
2149#[cfg(test)]
2150mod tests {
2151 use super::*;
2152
2153 #[test]
2161 fn a_zero_filled_c_profile_reads_back_as_the_sentinel() {
2162 let raised = qos_from_cffi(&crate::NROS_RMW_QOS_PROFILE_SYSTEM_DEFAULT);
2163 assert_eq!(raised, nros_rmw::QoSProfile::QOS_PROFILE_SYSTEM_DEFAULT);
2164 assert!(raised.has_unresolved_system_default());
2165 }
2166
2167 #[test]
2171 fn unknown_is_not_raised_to_the_sentinel() {
2172 let mut q = crate::NROS_RMW_QOS_PROFILE_SYSTEM_DEFAULT;
2173 q.reliability = crate::generated::NROS_RMW_RELIABILITY_UNKNOWN as u8;
2174 let raised = qos_from_cffi(&q);
2175 assert_eq!(
2176 raised.reliability,
2177 nros_rmw::QoSReliabilityPolicy::Reliable,
2178 "UNKNOWN must keep folding to the ROS default, not become a sentinel"
2179 );
2180 }
2181}