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_BAD_ALLOC, NROS_RMW_RET_INVALID_ARGUMENT,
54 NROS_RMW_RET_OK, NROS_RMW_RET_UNSUPPORTED, NrosRmwClient, NrosRmwEventCallback,
55 NrosRmwEventKind, NrosRmwGid, NrosRmwNode, NrosRmwPublisher, NrosRmwQos, NrosRmwRet,
56 NrosRmwService, NrosRmwSession, NrosRmwSessionOptions, NrosRmwSubscription, NrosRmwVtable,
57 entity_wake::{EntityWakeCb, Hosted},
58 event_kind_from_c, ret_from_error, rmw_publisher_options_t, rmw_subscription_options_t,
59};
60use crate::generated::{
65 NROS_RMW_DURABILITY_UNKNOWN, NROS_RMW_HISTORY_UNKNOWN, NROS_RMW_RELIABILITY_UNKNOWN,
66 rmw_endpoint_type_t, rmw_gid_t, rmw_liveliness_kind_t, rmw_qos_profile_t,
67 rmw_topic_endpoint_info_t,
68};
69
70#[cfg(all(target_os = "none", not(feature = "std")))]
71mod static_subscriber_storage {
72 use core::{cell::UnsafeCell, mem, ptr};
73
74 use portable_atomic::{AtomicBool, Ordering};
75
76 const SLOT_COUNT: usize = crate::parse_env_usize(
84 env!("NROS_RMW_SUBSCRIBER_SLOTS"),
85 "NROS_RMW_SUBSCRIBER_SLOTS must be a decimal integer",
86 );
87 const SLOT_SIZE: usize = crate::parse_env_usize(
100 env!("NROS_RMW_SUBSCRIBER_SLOT_BYTES"),
101 "NROS_RMW_SUBSCRIBER_SLOT_BYTES must be a decimal integer",
102 );
103 const SLOT_ALIGN: usize = 16;
104
105 #[repr(align(16))]
106 struct Slot {
107 bytes: UnsafeCell<[u8; SLOT_SIZE]>,
108 }
109
110 const _: () = assert!(mem::align_of::<Slot>() == SLOT_ALIGN);
113
114 unsafe impl Sync for Slot {}
115
116 impl Slot {
117 const fn new() -> Self {
118 Self {
119 bytes: UnsafeCell::new([0; SLOT_SIZE]),
120 }
121 }
122 }
123
124 static USED: [AtomicBool; SLOT_COUNT] = [const { AtomicBool::new(false) }; SLOT_COUNT];
130 static SLOTS: [Slot; SLOT_COUNT] = [const { Slot::new() }; SLOT_COUNT];
131
132 pub unsafe fn insert<T>(value: T) -> Option<*mut T> {
154 const {
155 let _raise_nros_rmw_subscriber_slot_bytes = SLOT_SIZE - mem::size_of::<T>();
182 let _over_aligned_for_the_cffi_subscriber_slot = SLOT_ALIGN - mem::align_of::<T>();
188 };
189
190 for index in 0..SLOT_COUNT {
191 if USED[index]
192 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
193 .is_err()
194 {
195 continue;
196 }
197
198 let ptr = SLOTS[index].bytes.get().cast::<T>();
199 unsafe { ptr.write(value) };
200 return Some(ptr);
201 }
202
203 nros_log::log_error!(
209 nros_log::get_logger("nros_rmw_cffi"),
210 "subscription pool exhausted: all {} static slot(s) are in use. \
211 Raise NROS_RMW_SUBSCRIBER_SLOTS (each slot costs \
212 NROS_RMW_SUBSCRIBER_SLOT_BYTES = {} bytes of .bss). A handle that \
213 did not FIT cannot reach this path — that is a build error",
214 SLOT_COUNT,
215 SLOT_SIZE
216 );
217 None
218 }
219
220 pub unsafe fn take<T>(ptr: *mut T) -> bool {
221 if ptr.is_null() {
222 return false;
223 }
224
225 for index in 0..SLOT_COUNT {
226 let slot_ptr = SLOTS[index].bytes.get().cast::<T>();
227 if ptr != slot_ptr {
228 continue;
229 }
230
231 unsafe { ptr::drop_in_place(ptr) };
232 USED[index].store(false, Ordering::Release);
233 return true;
234 }
235
236 false
237 }
238}
239
240pub trait RustBackend: Sized {
255 type Session: Session<
256 Error = TransportError,
257 PublisherHandle = Self::Publisher,
258 SubscriptionHandle = Self::Subscription,
259 ServiceHandle = Self::Service,
260 ClientHandle = Self::Client,
261 > + 'static;
262 type Publisher: Publisher<Error = TransportError> + 'static;
263 type Subscription: Subscription<Error = TransportError> + 'static;
264 type Service: ServiceTrait<Error = TransportError> + 'static;
265 type Client: ClientTrait<Error = TransportError> + 'static;
266
267 fn factory() -> Self;
272
273 fn open(self, config: &RmwConfig) -> Result<Self::Session, TransportError>;
275}
276
277impl<R> RustBackend for R
278where
279 R: Rmw<Error = TransportError> + Default + Sized,
280 R::Session: Session<Error = TransportError> + 'static,
281 <R::Session as Session>::PublisherHandle: Publisher<Error = TransportError> + 'static,
282 <R::Session as Session>::SubscriptionHandle: Subscription<Error = TransportError> + 'static,
283 <R::Session as Session>::ServiceHandle: ServiceTrait<Error = TransportError> + 'static,
284 <R::Session as Session>::ClientHandle: ClientTrait<Error = TransportError> + 'static,
285{
286 type Session = R::Session;
287 type Publisher = <R::Session as Session>::PublisherHandle;
288 type Subscription = <R::Session as Session>::SubscriptionHandle;
289 type Service = <R::Session as Session>::ServiceHandle;
290 type Client = <R::Session as Session>::ClientHandle;
291
292 fn factory() -> Self {
293 R::default()
294 }
295
296 fn open(self, config: &RmwConfig) -> Result<Self::Session, TransportError> {
297 Rmw::open(self, config)
298 }
299}
300
301unsafe fn cstr_to_str<'a>(ptr: *const core::ffi::c_char) -> &'a str {
313 if ptr.is_null() {
314 return "";
315 }
316 let ptr = ptr.cast::<u8>();
317 let mut len = 0usize;
318 while len < 4096 && unsafe { *ptr.add(len) } != 0 {
322 len += 1;
323 }
324 let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
325 core::str::from_utf8(slice).unwrap_or("")
326}
327
328unsafe fn cstr_to_str_strict<'a>(ptr: *const core::ffi::c_char) -> Option<&'a str> {
336 if ptr.is_null() {
337 return None;
338 }
339 let bytes = ptr.cast::<u8>();
340 let mut len = 0usize;
341 while len < 4096 && unsafe { *bytes.add(len) } != 0 {
342 len += 1;
343 }
344 if len == 0 {
345 return None;
346 }
347 let slice = unsafe { core::slice::from_raw_parts(bytes, len) };
348 core::str::from_utf8(slice).ok()
349}
350
351unsafe fn collect_session_properties<'a>(
358 options: *const NrosRmwSessionOptions,
359 out: &mut [(&'a str, &'a str); MAX_SESSION_PROPERTIES],
360) -> Result<usize, NrosRmwRet> {
361 if options.is_null() {
362 return Ok(0);
363 }
364 let opts = unsafe { &*options };
365 let count = opts.property_count;
366 if count == 0 {
367 return Ok(0);
368 }
369 if opts.properties.is_null() || count > MAX_SESSION_PROPERTIES {
370 return Err(NROS_RMW_RET_INVALID_ARGUMENT);
371 }
372 for (i, slot) in out.iter_mut().enumerate().take(count) {
373 let entry = unsafe { &*opts.properties.add(i) };
374 let (Some(key), Some(value)) = (unsafe { cstr_to_str_strict(entry.key) }, unsafe {
375 cstr_to_str_strict(entry.value)
376 }) else {
377 return Err(NROS_RMW_RET_INVALID_ARGUMENT);
378 };
379 *slot = (key, value);
380 }
381 Ok(count)
382}
383
384fn qos_from_cffi(q: &NrosRmwQos) -> QoSProfile {
388 use crate::generated;
389 use nros_rmw::{
390 QoSDurabilityPolicy, QoSHistoryPolicy, QoSLivelinessPolicy, QoSReliabilityPolicy,
391 };
392 QoSProfile {
406 reliability: match q.reliability as i32 {
407 generated::NROS_RMW_RELIABILITY_SYSTEM_DEFAULT => QoSReliabilityPolicy::SystemDefault,
408 generated::NROS_RMW_RELIABILITY_BEST_EFFORT => QoSReliabilityPolicy::BestEffort,
409 generated::NROS_RMW_RELIABILITY_RELIABLE => QoSReliabilityPolicy::Reliable,
410 generated::NROS_RMW_RELIABILITY_BEST_AVAILABLE => QoSReliabilityPolicy::BestAvailable,
414 _ => QoSReliabilityPolicy::Reliable,
415 },
416 durability: match q.durability as i32 {
417 generated::NROS_RMW_DURABILITY_SYSTEM_DEFAULT => QoSDurabilityPolicy::SystemDefault,
418 generated::NROS_RMW_DURABILITY_TRANSIENT_LOCAL => QoSDurabilityPolicy::TransientLocal,
419 generated::NROS_RMW_DURABILITY_BEST_AVAILABLE => QoSDurabilityPolicy::BestAvailable,
420 _ => QoSDurabilityPolicy::Volatile,
421 },
422 history: match q.history as i32 {
423 generated::NROS_RMW_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
424 generated::NROS_RMW_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
425 _ => QoSHistoryPolicy::KeepLast,
426 },
427 depth: q.depth as u32,
430 tx_express: false,
433 deadline_ms: q.deadline_ms,
434 lifespan_ms: q.lifespan_ms,
435 liveliness_kind: match q.liveliness_kind as u32 {
436 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_AUTOMATIC => {
437 QoSLivelinessPolicy::Automatic
438 }
439 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_MANUAL_BY_NODE => {
440 QoSLivelinessPolicy::ManualByNode
441 }
442 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_MANUAL_BY_TOPIC => {
443 QoSLivelinessPolicy::ManualByTopic
444 }
445 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_BEST_AVAILABLE => {
446 QoSLivelinessPolicy::BestAvailable
447 }
448 _ => QoSLivelinessPolicy::None,
449 },
450 liveliness_lease_ms: q.liveliness_lease_ms,
451 avoid_ros_namespace_conventions: q.avoid_ros_namespace_conventions != 0,
452 }
453}
454
455unsafe fn node_name_of<'a>(node: *const NrosRmwNode) -> Option<&'a str> {
462 let name = unsafe { cstr_to_str((*node).name) };
463 if name.is_empty() { None } else { Some(name) }
464}
465
466unsafe fn node_namespace_of<'a>(node: *const NrosRmwNode) -> &'a str {
467 let namespace = unsafe { cstr_to_str((*node).namespace_) };
468 if namespace.is_empty() { "/" } else { namespace }
469}
470
471const FORMAT_NAME_CAP: usize = 32;
488
489const fn format_name_cstr(name: &str) -> [u8; FORMAT_NAME_CAP] {
491 let bytes = name.as_bytes();
492 assert!(
493 bytes.len() < FORMAT_NAME_CAP,
494 "backend name (format or identifier) does not fit the vtable slot's buffer"
495 );
496 let mut out = [0u8; FORMAT_NAME_CAP];
497 let mut i = 0;
498 while i < bytes.len() {
499 out[i] = bytes[i];
500 i += 1;
501 }
502 out
503}
504
505unsafe extern "C" fn get_serialization_format_trampoline<R: RustBackend>() -> *const c_char {
515 RustBackendAdapter::<R>::SERIALIZATION_FORMAT_CSTR
516 .as_ptr()
517 .cast()
518}
519
520unsafe extern "C" fn get_implementation_identifier_trampoline<R: RustBackend>() -> *const c_char {
524 RustBackendAdapter::<R>::IMPLEMENTATION_IDENTIFIER_CSTR
525 .as_ptr()
526 .cast()
527}
528
529unsafe extern "C" fn supported_qos_policies_trampoline<R: RustBackend>(
541 session: *const NrosRmwSession,
542 out_mask: *mut u32,
543) -> NrosRmwRet {
544 if out_mask.is_null() {
545 return NROS_RMW_RET_INVALID_ARGUMENT;
546 }
547 let Some(s) = (unsafe { session_ref::<R::Session>(session) }) else {
548 return NROS_RMW_RET_INVALID_ARGUMENT;
549 };
550 unsafe { *out_mask = Session::supported_qos_policies(s).0 };
552 NROS_RMW_RET_OK
553}
554
555pub struct RustBackendAdapter<R>(PhantomData<R>);
558
559impl<R: RustBackend> RustBackendAdapter<R> {
560 const SERIALIZATION_FORMAT_CSTR: &'static [u8; FORMAT_NAME_CAP] =
568 &format_name_cstr(<R::Session as Session>::SERIALIZATION_FORMAT);
569
570 const IMPLEMENTATION_IDENTIFIER_CSTR: &'static [u8; FORMAT_NAME_CAP] =
574 &format_name_cstr(match <R::Session as Session>::IMPLEMENTATION_IDENTIFIER {
575 Some(name) => name,
576 None => "",
577 });
578
579 pub const VTABLE: NrosRmwVtable = NrosRmwVtable {
583 create_session: Some(create_session_trampoline::<R>),
584 destroy_session: Some(destroy_session_trampoline::<R>),
585 drive_io: Some(drive_io_trampoline::<R>),
586 create_publisher: Some(create_publisher_trampoline::<R>),
587 destroy_publisher: Some(destroy_publisher_trampoline::<R>),
588 publish: Some(publish_trampoline::<R>),
589 create_subscription: Some(create_subscription_trampoline::<R>),
590 destroy_subscription: Some(destroy_subscription_trampoline::<R>),
591 take: Some(take_trampoline::<R>),
592 has_data: Some(has_data_trampoline::<R>),
593 create_service: Some(create_service_trampoline::<R>),
594 destroy_service: Some(destroy_service_trampoline::<R>),
595 take_request: Some(take_request_trampoline::<R>),
596 has_request: Some(has_request_trampoline::<R>),
597 send_response: Some(send_response_trampoline::<R>),
598 create_client: Some(create_client_trampoline::<R>),
599 destroy_client: Some(destroy_client_trampoline::<R>),
600 send_request: Some(send_request_trampoline::<R>),
603 take_response: Some(take_response_trampoline::<R>),
604 take_response_with_info: Some(take_response_with_info_trampoline::<R>),
608 subscription_event_init: Some(subscription_event_init_trampoline::<R>),
609 publisher_event_init: Some(publisher_event_init_trampoline::<R>),
610 publisher_assert_liveliness: Some(publisher_assert_liveliness_trampoline::<R>),
611 get_gid_for_publisher: Some(get_gid_for_publisher_trampoline::<R>),
612 next_deadline_ms: Some(next_deadline_ms_trampoline::<R>),
613 set_wake_callback: Some(set_wake_callback_trampoline::<R>),
614 readiness_source: Some(readiness_source_trampoline::<R>),
619 service_server_is_available: Some(service_server_is_available_trampoline::<R>),
624 take_sequence: Some(take_sequence_trampoline::<R>),
625 publish_streamed: Some(publish_streamed_trampoline::<R>),
626 ping_session: Some(ping_session_trampoline::<R>),
627 subscription_supports_in_place: Some(subscription_supports_in_place_trampoline::<R>),
628 process_raw_in_place: Some(process_raw_in_place_trampoline::<R>),
629 get_node_names: Some(get_node_names_trampoline::<R>),
634 count_publishers: Some(count_publishers_trampoline::<R>),
635 count_subscribers: Some(count_subscribers_trampoline::<R>),
636 get_topic_names_and_types: Some(get_topic_names_and_types_trampoline::<R>),
637 get_service_names_and_types: Some(get_service_names_and_types_trampoline::<R>),
638 get_publisher_names_and_types_by_node: Some(
642 get_publisher_names_and_types_by_node_trampoline::<R>,
643 ),
644 get_subscriber_names_and_types_by_node: Some(
645 get_subscriber_names_and_types_by_node_trampoline::<R>,
646 ),
647 get_service_names_and_types_by_node: Some(
648 get_service_names_and_types_by_node_trampoline::<R>,
649 ),
650 get_client_names_and_types_by_node: Some(
651 get_client_names_and_types_by_node_trampoline::<R>,
652 ),
653 get_publishers_info_by_topic: Some(get_publishers_info_by_topic_trampoline::<R>),
654 get_subscriptions_info_by_topic: Some(get_subscriptions_info_by_topic_trampoline::<R>),
655 node_get_graph_guard_condition: Some(node_get_graph_guard_condition_trampoline::<R>),
664 get_serialization_format: Some(get_serialization_format_trampoline::<R>),
670 get_implementation_identifier: if <R::Session as Session>::IMPLEMENTATION_IDENTIFIER
675 .is_some()
676 {
677 Some(get_implementation_identifier_trampoline::<R>)
678 } else {
679 None
680 },
681 supported_qos_policies: Some(supported_qos_policies_trampoline::<R>),
687 publisher_get_actual_qos: Some(publisher_get_actual_qos_trampoline::<R>),
690 subscription_get_actual_qos: Some(subscription_get_actual_qos_trampoline::<R>),
691 client_request_publisher_get_actual_qos: Some(
692 client_request_publisher_get_actual_qos_trampoline::<R>,
693 ),
694 client_response_subscription_get_actual_qos: Some(
695 client_response_subscription_get_actual_qos_trampoline::<R>,
696 ),
697 service_request_subscription_get_actual_qos: Some(
698 service_request_subscription_get_actual_qos_trampoline::<R>,
699 ),
700 service_response_publisher_get_actual_qos: Some(
701 service_response_publisher_get_actual_qos_trampoline::<R>,
702 ),
703 subscription_set_on_new_message_callback: Some(
708 subscription_set_on_new_message_callback_trampoline::<R>,
709 ),
710 service_set_on_new_request_callback: Some(
711 service_set_on_new_request_callback_trampoline::<R>,
712 ),
713 client_set_on_new_response_callback: Some(
714 client_set_on_new_response_callback_trampoline::<R>,
715 ),
716 count_clients: Some(count_clients_trampoline::<R>),
722 count_services: Some(count_services_trampoline::<R>),
723 get_gid_for_client: Some(get_gid_for_client_trampoline::<R>),
724 event_type_is_supported: Some(event_type_is_supported_trampoline::<R>),
725 ..EMPTY_VTABLE
726 };
727
728 pub fn register() -> NrosRmwRet {
738 unsafe { crate::nros_rmw_cffi_register_named(c"default".as_ptr(), &Self::VTABLE) }
741 }
742
743 pub unsafe fn register_named(name: *const core::ffi::c_char) -> NrosRmwRet {
749 unsafe { crate::nros_rmw_cffi_register_named(name, &Self::VTABLE) }
752 }
753}
754
755unsafe extern "C" fn create_session_trampoline<R: RustBackend>(
760 locator: *const core::ffi::c_char,
761 mode: u8,
762 domain_id: u32,
763 node_name: *const core::ffi::c_char,
764 options: *const NrosRmwSessionOptions,
765 out: *mut NrosRmwSession,
766) -> NrosRmwRet {
767 if out.is_null() {
768 return NROS_RMW_RET_INVALID_ARGUMENT;
769 }
770 let mut prop_buf: [(&str, &str); MAX_SESSION_PROPERTIES] = [("", ""); MAX_SESSION_PROPERTIES];
785 let prop_count = match unsafe { collect_session_properties(options, &mut prop_buf) } {
786 Ok(n) => n,
787 Err(ret) => return ret,
788 };
789 let cfg = RmwConfig {
790 locator: unsafe { cstr_to_str(locator) },
791 mode: if mode == 0 {
792 SessionMode::Client
793 } else {
794 SessionMode::Peer
795 },
796 domain_id,
797 node_name: unsafe { cstr_to_str(node_name) },
798 namespace: unsafe { cstr_to_str((*out).namespace_) },
810 properties: &prop_buf[..prop_count],
811 };
812 let factory = R::factory();
813 match factory.open(&cfg) {
814 Ok(session) => match box_handle("session", session) {
815 Ok(boxed) => {
816 unsafe {
817 (*out).backend_data = boxed;
818 }
819 NROS_RMW_RET_OK
820 }
821 Err(mut session) => {
824 let _ = Session::close(&mut session);
825 drop(session);
826 NROS_RMW_RET_BAD_ALLOC
827 }
828 },
829 Err(e) => ret_from_error(&e),
830 }
831}
832
833unsafe extern "C" fn destroy_session_trampoline<R: RustBackend>(
834 session: *mut NrosRmwSession,
835) -> NrosRmwRet {
836 let Some(boxed) = (unsafe { take_box::<R::Session>(session_data_mut(session)) }) else {
837 return NROS_RMW_RET_INVALID_ARGUMENT;
838 };
839 let mut s = boxed;
840 let ret = match Session::close(&mut *s) {
841 Ok(()) => NROS_RMW_RET_OK,
842 Err(e) => ret_from_error(&e),
843 };
844 drop(s);
845 ret
846}
847
848unsafe extern "C" fn drive_io_trampoline<R: RustBackend>(
849 session: *mut NrosRmwSession,
850 timeout_ms: i32,
851) -> NrosRmwRet {
852 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
853 return NROS_RMW_RET_INVALID_ARGUMENT;
854 };
855 match Session::drive_io(s, timeout_ms) {
856 Ok(()) => NROS_RMW_RET_OK,
857 Err(e) => ret_from_error(&e),
858 }
859}
860
861unsafe extern "C" fn create_publisher_trampoline<R: RustBackend>(
866 node: *const NrosRmwNode,
867 type_support: *const crate::generated::rmw_message_type_support_t,
870 topic_name: *const core::ffi::c_char,
871 domain_id: u32,
872 qos: *const NrosRmwQos,
873 options: *const rmw_publisher_options_t,
874 out: *mut NrosRmwPublisher,
875) -> NrosRmwRet {
876 let Some(ts) = (unsafe { type_support.as_ref() }) else {
881 return NROS_RMW_RET_INVALID_ARGUMENT;
882 };
883 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
884 if out.is_null() || qos.is_null() {
885 return NROS_RMW_RET_INVALID_ARGUMENT;
886 }
887 if node.is_null() {
891 return NROS_RMW_RET_INVALID_ARGUMENT;
892 }
893 let session = unsafe { (*node).session };
894 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
895 return NROS_RMW_RET_INVALID_ARGUMENT;
896 };
897 let node_name = unsafe { node_name_of(node) };
898 let namespace = unsafe { node_namespace_of(node) };
899 let topic = TopicInfo {
900 name: unsafe { cstr_to_str(topic_name) },
901 type_name: unsafe { cstr_to_str(type_name) },
902 type_hash: unsafe { cstr_to_str(type_hash) },
903 domain_id,
904 node_name,
905 namespace,
906 rx_buffer_hint: 0,
908 arrival_rate_mhz: 0,
909 tx_express: unsafe { options.as_ref() }.is_some_and(|o| o.tx_express != 0),
912 };
913 let qos_settings = qos_from_cffi(unsafe { &*qos });
914 match Session::create_publisher(s, &topic, qos_settings) {
915 Ok(pub_handle) => match box_handle("publisher", pub_handle) {
916 Ok(boxed) => {
917 unsafe {
918 (*out).backend_data = boxed;
919 }
920 NROS_RMW_RET_OK
921 }
922 Err(pub_handle) => {
924 drop(pub_handle);
925 NROS_RMW_RET_BAD_ALLOC
926 }
927 },
928 Err(e) => ret_from_error(&e),
929 }
930}
931
932unsafe extern "C" fn destroy_publisher_trampoline<R: RustBackend>(
933 publisher: *mut NrosRmwPublisher,
934) -> NrosRmwRet {
935 let _ = unsafe { take_box::<R::Publisher>(publisher_data_mut(publisher)) };
936
937 NROS_RMW_RET_OK
938}
939
940unsafe extern "C" fn publish_trampoline<R: RustBackend>(
941 publisher: *const NrosRmwPublisher,
942 payload: crate::generated::rmw_byte_span_t,
944) -> NrosRmwRet {
945 let (data, len) = (payload.data, payload.len);
946 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
947 return NROS_RMW_RET_INVALID_ARGUMENT;
948 };
949 if data.is_null() && len != 0 {
950 return NROS_RMW_RET_INVALID_ARGUMENT;
951 }
952 let slice = unsafe { core::slice::from_raw_parts(data, len) };
953 match Publisher::publish_raw(p, slice) {
954 Ok(()) => NROS_RMW_RET_OK,
955 Err(e) => ret_from_error(&e),
956 }
957}
958
959unsafe extern "C" fn create_subscription_trampoline<R: RustBackend>(
964 node: *const NrosRmwNode,
965 type_support: *const crate::generated::rmw_message_type_support_t,
968 topic_name: *const core::ffi::c_char,
969 domain_id: u32,
970 qos: *const NrosRmwQos,
971 options: *const rmw_subscription_options_t,
972 out: *mut NrosRmwSubscription,
973) -> NrosRmwRet {
974 let Some(ts) = (unsafe { type_support.as_ref() }) else {
979 return NROS_RMW_RET_INVALID_ARGUMENT;
980 };
981 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
982 if out.is_null() || qos.is_null() {
983 return NROS_RMW_RET_INVALID_ARGUMENT;
984 }
985 if node.is_null() {
989 return NROS_RMW_RET_INVALID_ARGUMENT;
990 }
991 let session = unsafe { (*node).session };
992 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
993 return NROS_RMW_RET_INVALID_ARGUMENT;
994 };
995 let node_name = unsafe { node_name_of(node) };
996 let namespace = unsafe { node_namespace_of(node) };
997 let opts = unsafe { options.as_ref() };
999 let topic = TopicInfo {
1000 name: unsafe { cstr_to_str(topic_name) },
1001 type_name: unsafe { cstr_to_str(type_name) },
1002 type_hash: unsafe { cstr_to_str(type_hash) },
1003 domain_id,
1004 node_name,
1005 namespace,
1006 rx_buffer_hint: opts.map_or(0, |o| o.rx_buffer_hint as usize),
1010 arrival_rate_mhz: opts.map_or(0, |o| o.arrival_rate_mhz),
1012 tx_express: false,
1014 };
1015 let qos_settings = qos_from_cffi(unsafe { &*qos });
1016 match Session::create_subscription(s, &topic, qos_settings) {
1017 Ok(sub_handle) => {
1018 let sub_handle = Hosted::new(sub_handle, |t: &R::Subscription, w| {
1022 Subscription::register_waker(t, w)
1023 });
1024 #[cfg(all(target_os = "none", not(feature = "std")))]
1025 {
1026 let Some(ptr) = (unsafe { static_subscriber_storage::insert(sub_handle) }) else {
1027 return crate::NROS_RMW_RET_BAD_ALLOC;
1028 };
1029 unsafe {
1030 (*out).backend_data = ptr as *mut c_void;
1031 }
1032 NROS_RMW_RET_OK
1033 }
1034 #[cfg(not(all(target_os = "none", not(feature = "std"))))]
1035 {
1036 match box_handle("subscription", sub_handle) {
1037 Ok(boxed) => {
1038 unsafe {
1039 (*out).backend_data = boxed;
1040 }
1041 NROS_RMW_RET_OK
1042 }
1043 Err(sub_handle) => {
1046 drop(sub_handle);
1047 NROS_RMW_RET_BAD_ALLOC
1048 }
1049 }
1050 }
1051 }
1052 Err(e) => ret_from_error(&e),
1053 }
1054}
1055
1056unsafe extern "C" fn destroy_subscription_trampoline<R: RustBackend>(
1057 subscriber: *mut NrosRmwSubscription,
1058) -> NrosRmwRet {
1059 let slot = unsafe { subscription_data_mut(subscriber) };
1060 #[cfg(all(target_os = "none", not(feature = "std")))]
1061 {
1062 if unsafe {
1063 static_subscriber_storage::take::<Hosted<R::Subscription>>(
1064 *slot as *mut Hosted<R::Subscription>,
1065 )
1066 } {
1067 *slot = core::ptr::null_mut();
1068 return NROS_RMW_RET_OK;
1069 }
1070 }
1071 let _ = unsafe { take_box::<Hosted<R::Subscription>>(slot) };
1072
1073 NROS_RMW_RET_OK
1074}
1075
1076unsafe extern "C" fn take_trampoline<R: RustBackend>(
1077 subscriber: *const NrosRmwSubscription,
1078 out: *mut crate::generated::rmw_mut_byte_span_t,
1080 taken: *mut bool,
1081) -> NrosRmwRet {
1082 let Some(span) = (unsafe { out.as_mut() }) else {
1083 return NROS_RMW_RET_INVALID_ARGUMENT;
1084 };
1085 let (buf, buf_len) = (span.data, span.capacity);
1086 let out_len: *mut usize = &mut span.len;
1087 if out_len.is_null() || taken.is_null() {
1091 return NROS_RMW_RET_INVALID_ARGUMENT;
1092 }
1093 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1094 return NROS_RMW_RET_INVALID_ARGUMENT;
1095 };
1096 if buf.is_null() && buf_len != 0 {
1097 return NROS_RMW_RET_INVALID_ARGUMENT;
1098 }
1099 let slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
1100 let key = unsafe { (*subscriber).backend_data as usize };
1101
1102 #[cfg(feature = "safety-e2e")]
1104 if crate::take_cffi_integrity_request(key) {
1105 return match Subscription::take_validated(s, slice) {
1106 Ok(Some((n, status))) => {
1107 crate::store_cffi_integrity_status(key, status);
1108 unsafe {
1109 *out_len = n;
1110 *taken = true;
1111 }
1112 NROS_RMW_RET_OK
1113 }
1114 Ok(None) => {
1115 unsafe { *taken = false };
1116 NROS_RMW_RET_OK
1117 }
1118 Err(e) => refused_take_ret(s, &e, out_len),
1119 };
1120 }
1121
1122 match Subscription::take_serialized_with_info(s, slice) {
1123 Ok(Some((n, info))) => {
1124 crate::store_cffi_message_info(key, info);
1125 unsafe {
1126 *out_len = n;
1127 *taken = true;
1128 }
1129 NROS_RMW_RET_OK
1130 }
1131 Ok(None) => {
1132 unsafe { *taken = false };
1133 NROS_RMW_RET_OK
1134 }
1135 Err(e) => refused_take_ret(s, &e, out_len),
1136 }
1137}
1138
1139fn refused_take_ret<S: Subscription<Error = TransportError>>(
1147 s: &S,
1148 e: &TransportError,
1149 out_len: *mut usize,
1150) -> NrosRmwRet {
1151 refused_ret(e, s.refused_sample_len(), out_len)
1152}
1153
1154fn refused_ret(e: &TransportError, needed: Option<usize>, out_len: *mut usize) -> NrosRmwRet {
1159 if matches!(e, TransportError::BufferTooSmall)
1160 && let Some(needed) = needed
1161 {
1162 unsafe { *out_len = needed };
1165 }
1166 ret_from_error(e)
1167}
1168
1169unsafe extern "C" fn has_data_trampoline<R: RustBackend>(
1170 subscription: *mut NrosRmwSubscription,
1171 out_has_data: *mut bool,
1172) -> NrosRmwRet {
1173 if out_has_data.is_null() {
1176 return NROS_RMW_RET_INVALID_ARGUMENT;
1177 }
1178 let Some(s) = (unsafe { subscription_ref::<R::Subscription>(subscription) }) else {
1181 return NROS_RMW_RET_INVALID_ARGUMENT;
1182 };
1183 unsafe { *out_has_data = Subscription::has_data(s) };
1185 NROS_RMW_RET_OK
1186}
1187
1188unsafe extern "C" fn subscription_supports_in_place_trampoline<R: RustBackend>(
1191 subscriber: *mut NrosRmwSubscription,
1192 out_supports: *mut bool,
1193) -> NrosRmwRet {
1194 if out_supports.is_null() {
1196 return NROS_RMW_RET_INVALID_ARGUMENT;
1197 }
1198 let Some(s) = (unsafe { subscription_ref::<R::Subscription>(subscriber) }) else {
1199 return NROS_RMW_RET_INVALID_ARGUMENT;
1200 };
1201 unsafe { *out_supports = Subscription::supports_process_in_place(s) };
1203 NROS_RMW_RET_OK
1204}
1205
1206unsafe extern "C" fn process_raw_in_place_trampoline<R: RustBackend>(
1207 subscriber: *mut NrosRmwSubscription,
1208 ctx: *mut core::ffi::c_void,
1209 cb: Option<
1210 unsafe extern "C" fn(
1211 ctx: *mut core::ffi::c_void,
1212 message: crate::generated::rmw_byte_span_t,
1213 ),
1214 >,
1215 out_processed: *mut bool,
1216) -> NrosRmwRet {
1217 if out_processed.is_null() {
1220 return NROS_RMW_RET_INVALID_ARGUMENT;
1221 }
1222 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1223 return NROS_RMW_RET_INVALID_ARGUMENT;
1224 };
1225 let Some(cb) = cb else {
1226 return NROS_RMW_RET_INVALID_ARGUMENT;
1227 };
1228 match Subscription::process_raw_in_place(s, |raw| unsafe {
1229 cb(
1230 ctx,
1231 crate::generated::rmw_byte_span_t {
1232 data: raw.as_ptr(),
1233 len: raw.len(),
1234 },
1235 )
1236 }) {
1237 Ok(processed) => {
1238 unsafe { *out_processed = processed };
1240 NROS_RMW_RET_OK
1241 }
1242 Err(e) => ret_from_error(&e),
1243 }
1244}
1245
1246unsafe extern "C" fn create_service_trampoline<R: RustBackend>(
1251 node: *const NrosRmwNode,
1252 type_support: *const crate::generated::rmw_service_type_support_t,
1255 service_name: *const core::ffi::c_char,
1256 domain_id: u32,
1257 qos: *const NrosRmwQos,
1258 out: *mut NrosRmwService,
1259) -> NrosRmwRet {
1260 let Some(ts) = (unsafe { type_support.as_ref() }) else {
1265 return NROS_RMW_RET_INVALID_ARGUMENT;
1266 };
1267 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
1268 if out.is_null() || qos.is_null() {
1269 return NROS_RMW_RET_INVALID_ARGUMENT;
1270 }
1271 if node.is_null() {
1275 return NROS_RMW_RET_INVALID_ARGUMENT;
1276 }
1277 let session = unsafe { (*node).session };
1278 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1279 return NROS_RMW_RET_INVALID_ARGUMENT;
1280 };
1281 let node_name = unsafe { node_name_of(node) };
1282 let namespace = unsafe { node_namespace_of(node) };
1283 let info = nros_rmw::ServiceInfo {
1284 name: unsafe { cstr_to_str(service_name) },
1285 type_name: unsafe { cstr_to_str(type_name) },
1286 type_hash: unsafe { cstr_to_str(type_hash) },
1287 domain_id,
1288 node_name,
1289 namespace,
1290 };
1291 let qos_settings = qos_from_cffi(unsafe { &*qos });
1292 match Session::create_service(s, &info, qos_settings) {
1293 Ok(server) => match box_handle(
1294 "service server",
1295 Hosted::new(server, |t: &R::Service, w| {
1296 ServiceTrait::register_waker(t, w)
1297 }),
1298 ) {
1299 Ok(boxed) => {
1300 unsafe {
1301 (*out).backend_data = boxed;
1302 }
1303 NROS_RMW_RET_OK
1304 }
1305 Err(server) => {
1307 drop(server);
1308 NROS_RMW_RET_BAD_ALLOC
1309 }
1310 },
1311 Err(e) => ret_from_error(&e),
1312 }
1313}
1314
1315unsafe extern "C" fn destroy_service_trampoline<R: RustBackend>(
1316 server: *mut NrosRmwService,
1317) -> NrosRmwRet {
1318 let _ = unsafe { take_box::<Hosted<R::Service>>(service_data_mut(server)) };
1319
1320 NROS_RMW_RET_OK
1321}
1322
1323unsafe extern "C" fn take_request_trampoline<R: RustBackend>(
1324 server: *const NrosRmwService,
1325 request: *mut crate::generated::rmw_mut_byte_span_t,
1326 seq_out: *mut i64,
1327 taken: *mut bool,
1328) -> NrosRmwRet {
1329 if request.is_null() || taken.is_null() {
1332 return NROS_RMW_RET_INVALID_ARGUMENT;
1333 }
1334 let Some(s) = (unsafe { service_mut::<R::Service>(server) }) else {
1335 return NROS_RMW_RET_INVALID_ARGUMENT;
1336 };
1337 let (buf, buf_len) = unsafe { ((*request).data, (*request).capacity) };
1338 let out_len = unsafe { &raw mut (*request).len };
1339 if (buf.is_null() && buf_len != 0) || seq_out.is_null() {
1340 return NROS_RMW_RET_INVALID_ARGUMENT;
1341 }
1342 let slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
1343 let buf_start = slice.as_ptr() as usize;
1344 match ServiceTrait::take_request(s, slice) {
1345 Ok(Some(req)) => {
1346 let offset = (req.data.as_ptr() as usize).saturating_sub(buf_start);
1351 let len = req.data.len();
1352 unsafe {
1353 *seq_out = req.sequence_number;
1354 }
1355 let key = unsafe { (*server).backend_data as usize };
1359 crate::store_cffi_service_info(key, ServiceTrait::request_info(s));
1360 if offset != 0 {
1364 let total = offset + len;
1365 slice.copy_within(offset..total, 0);
1368 }
1369 unsafe {
1371 *out_len = len;
1372 *taken = true;
1373 }
1374 NROS_RMW_RET_OK
1375 }
1376 Ok(None) => {
1377 unsafe { *taken = false };
1379 NROS_RMW_RET_OK
1380 }
1381 Err(e) => refused_ret(&e, ServiceTrait::refused_request_len(s), out_len),
1382 }
1383}
1384
1385unsafe extern "C" fn has_request_trampoline<R: RustBackend>(
1386 server: *mut NrosRmwService,
1387 out_has_request: *mut bool,
1388) -> NrosRmwRet {
1389 if out_has_request.is_null() {
1391 return NROS_RMW_RET_INVALID_ARGUMENT;
1392 }
1393 let Some(s) = (unsafe { service_ref::<R::Service>(server) }) else {
1394 return NROS_RMW_RET_INVALID_ARGUMENT;
1395 };
1396 unsafe { *out_has_request = ServiceTrait::has_request(s) };
1398 NROS_RMW_RET_OK
1399}
1400
1401unsafe extern "C" fn send_response_trampoline<R: RustBackend>(
1402 server: *const NrosRmwService,
1403 seq: i64,
1404 response: crate::generated::rmw_byte_span_t,
1405) -> NrosRmwRet {
1406 let Some(s) = (unsafe { service_mut::<R::Service>(server) }) else {
1407 return NROS_RMW_RET_INVALID_ARGUMENT;
1408 };
1409 let (data, len) = (response.data, response.len);
1410 if data.is_null() && len != 0 {
1411 return NROS_RMW_RET_INVALID_ARGUMENT;
1412 }
1413 let slice = unsafe { core::slice::from_raw_parts(data, len) };
1414 match ServiceTrait::send_response(s, seq, slice) {
1415 Ok(()) => NROS_RMW_RET_OK,
1416 Err(e) => ret_from_error(&e),
1417 }
1418}
1419
1420unsafe extern "C" fn create_client_trampoline<R: RustBackend>(
1425 node: *const NrosRmwNode,
1426 type_support: *const crate::generated::rmw_service_type_support_t,
1429 service_name: *const core::ffi::c_char,
1430 domain_id: u32,
1431 qos: *const NrosRmwQos,
1432 out: *mut NrosRmwClient,
1433) -> NrosRmwRet {
1434 let Some(ts) = (unsafe { type_support.as_ref() }) else {
1439 return NROS_RMW_RET_INVALID_ARGUMENT;
1440 };
1441 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
1442 if out.is_null() || qos.is_null() {
1443 return NROS_RMW_RET_INVALID_ARGUMENT;
1444 }
1445 if node.is_null() {
1449 return NROS_RMW_RET_INVALID_ARGUMENT;
1450 }
1451 let session = unsafe { (*node).session };
1452 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1453 return NROS_RMW_RET_INVALID_ARGUMENT;
1454 };
1455 let node_name = unsafe { node_name_of(node) };
1456 let namespace = unsafe { node_namespace_of(node) };
1457 let info = nros_rmw::ServiceInfo {
1458 name: unsafe { cstr_to_str(service_name) },
1459 type_name: unsafe { cstr_to_str(type_name) },
1460 type_hash: unsafe { cstr_to_str(type_hash) },
1461 domain_id,
1462 node_name,
1463 namespace,
1464 };
1465 let qos_settings = qos_from_cffi(unsafe { &*qos });
1466 match Session::create_client(s, &info, qos_settings) {
1467 Ok(client) => match box_handle(
1468 "service client",
1469 Hosted::new(client, |t: &R::Client, w| ClientTrait::register_waker(t, w)),
1470 ) {
1471 Ok(boxed) => {
1472 unsafe {
1473 (*out).backend_data = boxed;
1474 }
1475 NROS_RMW_RET_OK
1476 }
1477 Err(client) => {
1479 drop(client);
1480 NROS_RMW_RET_BAD_ALLOC
1481 }
1482 },
1483 Err(e) => ret_from_error(&e),
1484 }
1485}
1486
1487unsafe extern "C" fn destroy_client_trampoline<R: RustBackend>(
1488 client: *mut NrosRmwClient,
1489) -> NrosRmwRet {
1490 let _ = unsafe { take_box::<Hosted<R::Client>>(client_data_mut(client)) };
1491
1492 NROS_RMW_RET_OK
1493}
1494
1495unsafe extern "C" fn send_request_trampoline<R: RustBackend>(
1498 client: *const NrosRmwClient,
1499 request: crate::generated::rmw_byte_span_t,
1500 sequence_id: *mut i64,
1501) -> NrosRmwRet {
1502 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1503 return NROS_RMW_RET_INVALID_ARGUMENT;
1504 };
1505 let (request, req_len) = (request.data, request.len);
1506 if request.is_null() && req_len != 0 {
1507 return NROS_RMW_RET_INVALID_ARGUMENT;
1508 }
1509 let req = unsafe { core::slice::from_raw_parts(request, req_len) };
1510 match ClientTrait::send_request_raw(c, req) {
1511 Ok(seq) => {
1512 if !sequence_id.is_null() {
1515 unsafe { *sequence_id = seq };
1517 }
1518 NROS_RMW_RET_OK
1519 }
1520 Err(e) => ret_from_error(&e),
1521 }
1522}
1523
1524unsafe extern "C" fn take_response_trampoline<R: RustBackend>(
1525 client: *const NrosRmwClient,
1526 reply: *mut crate::generated::rmw_mut_byte_span_t,
1527 seq_out: *mut i64,
1528 taken: *mut bool,
1529) -> NrosRmwRet {
1530 unsafe { take_response_impl::<R>(client, reply, seq_out, taken, core::ptr::null_mut()) }
1531}
1532
1533unsafe extern "C" fn take_response_with_info_trampoline<R: RustBackend>(
1536 client: *const NrosRmwClient,
1537 reply: *mut crate::generated::rmw_mut_byte_span_t,
1538 seq_out: *mut i64,
1539 taken: *mut bool,
1540 service_info: *mut crate::generated::rmw_service_info_t,
1541) -> NrosRmwRet {
1542 if service_info.is_null() {
1543 return NROS_RMW_RET_INVALID_ARGUMENT;
1544 }
1545 unsafe { take_response_impl::<R>(client, reply, seq_out, taken, service_info) }
1546}
1547
1548fn service_info_to_raw(
1551 info: &nros_rmw::ServiceRequestInfo,
1552 out: &mut crate::generated::rmw_service_info_t,
1553) {
1554 out.source_timestamp = info.source_timestamp.map_or(0, |t| t.to_nanos());
1555 out.received_timestamp = info.received_timestamp.map_or(0, |t| t.to_nanos());
1556 match info.request_id {
1557 Some(id) => {
1558 for (dst, src) in out
1559 .request_id
1560 .writer_guid
1561 .iter_mut()
1562 .zip(id.writer_guid.iter())
1563 {
1564 *dst = *src as i8;
1565 }
1566 out.request_id.sequence_number = id.sequence_number;
1567 }
1568 None => {
1569 out.request_id.writer_guid = [0; 16];
1570 out.request_id.sequence_number = 0;
1571 }
1572 }
1573}
1574
1575unsafe fn take_response_impl<R: RustBackend>(
1576 client: *const NrosRmwClient,
1577 reply: *mut crate::generated::rmw_mut_byte_span_t,
1578 seq_out: *mut i64,
1579 taken: *mut bool,
1580 service_info: *mut crate::generated::rmw_service_info_t,
1581) -> NrosRmwRet {
1582 if reply.is_null() || taken.is_null() {
1585 return NROS_RMW_RET_INVALID_ARGUMENT;
1586 }
1587 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1588 return NROS_RMW_RET_INVALID_ARGUMENT;
1589 };
1590 let (reply_buf, reply_buf_len) = unsafe { ((*reply).data, (*reply).capacity) };
1591 let out_len = unsafe { &raw mut (*reply).len };
1592 if reply_buf.is_null() && reply_buf_len != 0 {
1593 return NROS_RMW_RET_INVALID_ARGUMENT;
1594 }
1595 let reply = unsafe { core::slice::from_raw_parts_mut(reply_buf, reply_buf_len) };
1596 match ClientTrait::take_response_raw(c, reply) {
1597 Ok(Some((n, seq))) => {
1598 unsafe {
1600 *out_len = n;
1601 *taken = true;
1602 if !seq_out.is_null() {
1603 *seq_out = seq;
1604 }
1605 }
1606 if !service_info.is_null() {
1607 service_info_to_raw(&ClientTrait::response_info(c), unsafe {
1609 &mut *service_info
1610 });
1611 }
1612 NROS_RMW_RET_OK
1613 }
1614 Ok(None) => {
1615 unsafe { *taken = false };
1617 NROS_RMW_RET_OK
1618 }
1619 Err(e) => refused_ret(&e, ClientTrait::refused_response_len(c), out_len),
1620 }
1621}
1622
1623const _: () = {
1648 use core::mem::{align_of, size_of};
1649 assert!(
1650 size_of::<crate::NrosRmwLivelinessChangedStatus>()
1651 == size_of::<nros_rmw::LivelinessChangedStatus>()
1652 );
1653 assert!(
1654 align_of::<crate::NrosRmwLivelinessChangedStatus>()
1655 == align_of::<nros_rmw::LivelinessChangedStatus>()
1656 );
1657 assert!(size_of::<crate::NrosRmwCountStatus>() == size_of::<nros_rmw::CountStatus>());
1658 assert!(align_of::<crate::NrosRmwCountStatus>() == align_of::<nros_rmw::CountStatus>());
1659 assert!(
1661 size_of::<crate::generated::rmw_matched_status_t>() == size_of::<nros_rmw::MatchedStatus>()
1662 );
1663 assert!(
1664 align_of::<crate::generated::rmw_matched_status_t>()
1665 == align_of::<nros_rmw::MatchedStatus>()
1666 );
1667 assert!(
1668 size_of::<crate::generated::rmw_incompatible_type_status_t>()
1669 == size_of::<nros_rmw::IncompatibleTypeStatus>()
1670 );
1671 assert!(
1672 align_of::<crate::generated::rmw_incompatible_type_status_t>()
1673 == align_of::<nros_rmw::IncompatibleTypeStatus>()
1674 );
1675 use crate::rmw_event_type_t as ck;
1678 assert!(
1679 ck::NROS_RMW_EVENT_LIVELINESS_CHANGED == nros_rmw::EventKind::LivelinessChanged as ck::Type
1680 );
1681 assert!(
1682 ck::NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED
1683 == nros_rmw::EventKind::RequestedDeadlineMissed as ck::Type
1684 );
1685 assert!(ck::NROS_RMW_EVENT_MESSAGE_LOST == nros_rmw::EventKind::MessageLost as ck::Type);
1686 assert!(ck::NROS_RMW_EVENT_LIVELINESS_LOST == nros_rmw::EventKind::LivelinessLost as ck::Type);
1687 assert!(
1688 ck::NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED
1689 == nros_rmw::EventKind::OfferedDeadlineMissed as ck::Type
1690 );
1691 assert!(
1692 ck::NROS_RMW_EVENT_PUBLICATION_MATCHED
1693 == nros_rmw::EventKind::PublicationMatched as ck::Type
1694 );
1695 assert!(
1696 ck::NROS_RMW_EVENT_SUBSCRIPTION_MATCHED
1697 == nros_rmw::EventKind::SubscriptionMatched as ck::Type
1698 );
1699 assert!(
1700 ck::NROS_RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE
1701 == nros_rmw::EventKind::PublisherIncompatibleType as ck::Type
1702 );
1703 assert!(
1704 ck::NROS_RMW_EVENT_SUBSCRIPTION_INCOMPATIBLE_TYPE
1705 == nros_rmw::EventKind::SubscriptionIncompatibleType as ck::Type
1706 );
1707};
1708
1709unsafe extern "C" fn subscription_event_init_trampoline<R: RustBackend>(
1710 subscriber: *const NrosRmwSubscription,
1711 kind: NrosRmwEventKind,
1712 deadline_ms: u32,
1713 cb: NrosRmwEventCallback,
1714 user_context: *mut c_void,
1715) -> NrosRmwRet {
1716 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1717 return NROS_RMW_RET_INVALID_ARGUMENT;
1718 };
1719 let trait_kind: nros_rmw::EventKind = event_kind_from_c(kind);
1723 let Some(cb) = cb else {
1726 return NROS_RMW_RET_INVALID_ARGUMENT;
1727 };
1728 let trait_cb: nros_rmw::EventCallback = unsafe { core::mem::transmute(cb) };
1732 let res = unsafe {
1733 Subscription::register_event_callback(s, trait_kind, deadline_ms, trait_cb, user_context)
1734 };
1735 match res {
1736 Ok(()) => NROS_RMW_RET_OK,
1737 Err(e) => {
1738 let _ = e;
1746 NROS_RMW_RET_UNSUPPORTED
1747 }
1748 }
1749}
1750
1751unsafe extern "C" fn publisher_event_init_trampoline<R: RustBackend>(
1752 publisher: *const NrosRmwPublisher,
1753 kind: NrosRmwEventKind,
1754 deadline_ms: u32,
1755 cb: NrosRmwEventCallback,
1756 user_context: *mut c_void,
1757) -> NrosRmwRet {
1758 if publisher.is_null() {
1761 return NROS_RMW_RET_INVALID_ARGUMENT;
1762 }
1763 let p_ptr = unsafe { (*publisher).backend_data } as *mut R::Publisher;
1764 if p_ptr.is_null() {
1765 return NROS_RMW_RET_INVALID_ARGUMENT;
1766 }
1767 let p = unsafe { &mut *p_ptr };
1768 let trait_kind: nros_rmw::EventKind = event_kind_from_c(kind);
1770 let Some(cb) = cb else {
1771 return NROS_RMW_RET_INVALID_ARGUMENT;
1772 };
1773 let trait_cb: nros_rmw::EventCallback = unsafe { core::mem::transmute(cb) };
1774 let res = unsafe {
1775 Publisher::register_event_callback(p, trait_kind, deadline_ms, trait_cb, user_context)
1776 };
1777 match res {
1778 Ok(()) => NROS_RMW_RET_OK,
1779 Err(_) => NROS_RMW_RET_UNSUPPORTED,
1780 }
1781}
1782
1783unsafe fn write_actual_qos(out: *mut NrosRmwQos, qos: QoSProfile) -> NrosRmwRet {
1804 let Ok(lowered) = NrosRmwQos::try_from(qos) else {
1805 return crate::NROS_RMW_RET_ERROR;
1806 };
1807 unsafe { *out = lowered };
1809 NROS_RMW_RET_OK
1810}
1811
1812unsafe extern "C" fn publisher_get_actual_qos_trampoline<R: RustBackend>(
1813 publisher: *const NrosRmwPublisher,
1814 out: *mut NrosRmwQos,
1815) -> NrosRmwRet {
1816 if out.is_null() {
1817 return NROS_RMW_RET_INVALID_ARGUMENT;
1818 }
1819 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1820 return NROS_RMW_RET_INVALID_ARGUMENT;
1821 };
1822 unsafe { write_actual_qos(out, Publisher::actual_qos(p)) }
1824}
1825
1826unsafe extern "C" fn subscription_get_actual_qos_trampoline<R: RustBackend>(
1827 subscription: *const NrosRmwSubscription,
1828 out: *mut NrosRmwQos,
1829) -> NrosRmwRet {
1830 if out.is_null() {
1831 return NROS_RMW_RET_INVALID_ARGUMENT;
1832 }
1833 let Some(sub) = (unsafe { subscription_ref::<R::Subscription>(subscription) }) else {
1834 return NROS_RMW_RET_INVALID_ARGUMENT;
1835 };
1836 unsafe { write_actual_qos(out, Subscription::actual_qos(sub)) }
1838}
1839
1840unsafe extern "C" fn client_request_publisher_get_actual_qos_trampoline<R: RustBackend>(
1841 client: *const NrosRmwClient,
1842 out: *mut NrosRmwQos,
1843) -> NrosRmwRet {
1844 if out.is_null() {
1845 return NROS_RMW_RET_INVALID_ARGUMENT;
1846 }
1847 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1848 return NROS_RMW_RET_INVALID_ARGUMENT;
1849 };
1850 unsafe { write_actual_qos(out, ClientTrait::request_publisher_actual_qos(c)) }
1852}
1853
1854unsafe extern "C" fn client_response_subscription_get_actual_qos_trampoline<R: RustBackend>(
1855 client: *const NrosRmwClient,
1856 out: *mut NrosRmwQos,
1857) -> NrosRmwRet {
1858 if out.is_null() {
1859 return NROS_RMW_RET_INVALID_ARGUMENT;
1860 }
1861 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1862 return NROS_RMW_RET_INVALID_ARGUMENT;
1863 };
1864 unsafe { write_actual_qos(out, ClientTrait::response_subscription_actual_qos(c)) }
1866}
1867
1868unsafe extern "C" fn service_request_subscription_get_actual_qos_trampoline<R: RustBackend>(
1869 service: *const NrosRmwService,
1870 out: *mut NrosRmwQos,
1871) -> NrosRmwRet {
1872 if out.is_null() {
1873 return NROS_RMW_RET_INVALID_ARGUMENT;
1874 }
1875 let Some(sv) = (unsafe { service_ref::<R::Service>(service) }) else {
1876 return NROS_RMW_RET_INVALID_ARGUMENT;
1877 };
1878 unsafe { write_actual_qos(out, ServiceTrait::request_subscription_actual_qos(sv)) }
1880}
1881
1882unsafe extern "C" fn service_response_publisher_get_actual_qos_trampoline<R: RustBackend>(
1883 service: *const NrosRmwService,
1884 out: *mut NrosRmwQos,
1885) -> NrosRmwRet {
1886 if out.is_null() {
1887 return NROS_RMW_RET_INVALID_ARGUMENT;
1888 }
1889 let Some(sv) = (unsafe { service_ref::<R::Service>(service) }) else {
1890 return NROS_RMW_RET_INVALID_ARGUMENT;
1891 };
1892 unsafe { write_actual_qos(out, ServiceTrait::response_publisher_actual_qos(sv)) }
1894}
1895
1896unsafe extern "C" fn publisher_assert_liveliness_trampoline<R: RustBackend>(
1897 publisher: *const NrosRmwPublisher,
1898) -> NrosRmwRet {
1899 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1900 return NROS_RMW_RET_INVALID_ARGUMENT;
1901 };
1902 match Publisher::assert_liveliness(p) {
1903 Ok(()) => NROS_RMW_RET_OK,
1904 Err(e) => ret_from_error(&e),
1905 }
1906}
1907
1908unsafe extern "C" fn get_gid_for_publisher_trampoline<R: RustBackend>(
1923 publisher: *const NrosRmwPublisher,
1924 gid: *mut NrosRmwGid,
1925) -> NrosRmwRet {
1926 if gid.is_null() {
1927 return NROS_RMW_RET_INVALID_ARGUMENT;
1928 }
1929 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1930 return NROS_RMW_RET_INVALID_ARGUMENT;
1931 };
1932 match Publisher::get_gid(p) {
1933 Ok(bytes) => {
1934 unsafe {
1936 (*gid).data = bytes;
1937 }
1938 NROS_RMW_RET_OK
1939 }
1940 Err(e) => ret_from_error(&e),
1941 }
1942}
1943
1944unsafe extern "C" fn next_deadline_ms_trampoline<R: RustBackend>(
1945 session: *const NrosRmwSession,
1946 out_ms: *mut u32,
1947 has_deadline: *mut bool,
1948) -> NrosRmwRet {
1949 if out_ms.is_null() || has_deadline.is_null() {
1951 return NROS_RMW_RET_INVALID_ARGUMENT;
1952 }
1953 let Some(s) = (unsafe { session_ref::<R::Session>(session) }) else {
1956 return NROS_RMW_RET_INVALID_ARGUMENT;
1957 };
1958 match Session::next_deadline_ms(s) {
1959 Some(ms) => {
1960 unsafe {
1962 *out_ms = ms;
1963 *has_deadline = true;
1964 }
1965 }
1966 None => {
1967 unsafe { *has_deadline = false };
1969 }
1970 }
1971 NROS_RMW_RET_OK
1972}
1973
1974unsafe extern "C" fn set_wake_callback_trampoline<R: RustBackend>(
1975 session: *mut NrosRmwSession,
1976 cb: Option<unsafe extern "C" fn(ctx: *mut core::ffi::c_void)>,
1977 ctx: *mut core::ffi::c_void,
1978) -> NrosRmwRet {
1979 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1980 return NROS_RMW_RET_INVALID_ARGUMENT;
1981 };
1982 unsafe { Session::set_wake_callback(s, cb, ctx) };
1987 NROS_RMW_RET_OK
1988}
1989
1990unsafe extern "C" fn readiness_source_trampoline<R: RustBackend>(
1991 session: *const NrosRmwSession,
1992 out_kind: *mut u8,
1993 out_handle: *mut i32,
1994) -> NrosRmwRet {
1995 if out_kind.is_null() || out_handle.is_null() {
1996 return NROS_RMW_RET_INVALID_ARGUMENT;
1997 }
1998 let Some(s) = (unsafe { session_ref::<R::Session>(session) }) else {
1999 return NROS_RMW_RET_INVALID_ARGUMENT;
2000 };
2001 let (kind, handle) = match Session::readiness_source(s) {
2002 nros_rmw::ReadinessSource::None => (crate::generated::NROS_RMW_READINESS_NONE, -1),
2003 nros_rmw::ReadinessSource::Notify => (crate::generated::NROS_RMW_READINESS_NOTIFY, -1),
2004 nros_rmw::ReadinessSource::Waitable(fd) => {
2005 (crate::generated::NROS_RMW_READINESS_WAITABLE, fd)
2006 }
2007 };
2008 unsafe {
2010 *out_kind = kind as u8;
2011 *out_handle = handle;
2012 }
2013 NROS_RMW_RET_OK
2014}
2015
2016#[doc(hidden)]
2022pub const fn entity_box_size<T>() -> usize {
2023 core::mem::size_of::<Hosted<T>>()
2024}
2025
2026unsafe extern "C" fn subscription_set_on_new_message_callback_trampoline<R: RustBackend>(
2029 subscription: *mut NrosRmwSubscription,
2030 cb: Option<EntityWakeCb>,
2031 user_data: *const c_void,
2032) -> NrosRmwRet {
2033 if subscription.is_null() {
2034 return NROS_RMW_RET_INVALID_ARGUMENT;
2035 }
2036 let data = unsafe { *subscription_data_mut(subscription) };
2037 unsafe { Hosted::<R::Subscription>::set_wake(data, cb, user_data) }
2038}
2039
2040unsafe extern "C" fn service_set_on_new_request_callback_trampoline<R: RustBackend>(
2041 service: *mut NrosRmwService,
2042 cb: Option<EntityWakeCb>,
2043 user_data: *const c_void,
2044) -> NrosRmwRet {
2045 if service.is_null() {
2046 return NROS_RMW_RET_INVALID_ARGUMENT;
2047 }
2048 let data = unsafe { *service_data_mut(service) };
2049 unsafe { Hosted::<R::Service>::set_wake(data, cb, user_data) }
2050}
2051
2052unsafe extern "C" fn client_set_on_new_response_callback_trampoline<R: RustBackend>(
2053 client: *mut NrosRmwClient,
2054 cb: Option<EntityWakeCb>,
2055 user_data: *const c_void,
2056) -> NrosRmwRet {
2057 if client.is_null() {
2058 return NROS_RMW_RET_INVALID_ARGUMENT;
2059 }
2060 let data = unsafe { *client_data_mut(client) };
2061 unsafe { Hosted::<R::Client>::set_wake(data, cb, user_data) }
2062}
2063
2064unsafe extern "C" fn node_get_graph_guard_condition_trampoline<R: RustBackend>(
2073 session: *mut NrosRmwSession,
2074 callback: crate::generated::rmw_event_callback_t,
2075 user_data: *const core::ffi::c_void,
2076) -> NrosRmwRet {
2077 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
2078 return NROS_RMW_RET_INVALID_ARGUMENT;
2079 };
2080 match unsafe { Session::set_graph_change_callback(s, callback, user_data) } {
2083 Ok(()) => NROS_RMW_RET_OK,
2084 Err(e) => ret_from_error(&e),
2085 }
2086}
2087
2088unsafe extern "C" fn service_server_is_available_trampoline<R: RustBackend>(
2089 client: *const NrosRmwClient,
2090 out_available: *mut bool,
2091) -> NrosRmwRet {
2092 if out_available.is_null() {
2103 return NROS_RMW_RET_INVALID_ARGUMENT;
2104 }
2105 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
2106 return NROS_RMW_RET_INVALID_ARGUMENT;
2107 };
2108 match ClientTrait::service_is_ready(c) {
2109 Ok(available) => {
2110 unsafe { *out_available = available };
2112 NROS_RMW_RET_OK
2113 }
2114 Err(e) => ret_from_error(&e),
2115 }
2116}
2117
2118unsafe extern "C" fn ping_session_trampoline<R: RustBackend>(
2119 session: *mut NrosRmwSession,
2120 timeout_ms: i32,
2121) -> NrosRmwRet {
2122 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
2127 return NROS_RMW_RET_INVALID_ARGUMENT;
2128 };
2129 match Session::ping_session(s, timeout_ms) {
2130 Ok(()) => NROS_RMW_RET_OK,
2131 Err(e) => ret_from_error(&e),
2132 }
2133}
2134
2135unsafe extern "C" fn get_node_names_trampoline<R: RustBackend>(
2145 session: *const NrosRmwSession,
2146 visitor: crate::generated::rmw_node_visitor_t,
2149) -> NrosRmwRet {
2150 let (visit, ctx) = (visitor.visit, visitor.ctx);
2151 let Some(visit) = visit else {
2152 return NROS_RMW_RET_INVALID_ARGUMENT;
2153 };
2154 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
2155 return NROS_RMW_RET_INVALID_ARGUMENT;
2156 };
2157
2158 const NAME_MAX: usize = 256;
2160
2161 let mut cb = |name: &str, namespace: &str, enclave: Option<&str>| -> bool {
2162 let mut name_buf = [0u8; NAME_MAX];
2163 let mut ns_buf = [0u8; NAME_MAX];
2164 let mut enc_buf = [0u8; NAME_MAX];
2165 if name.len() >= NAME_MAX || namespace.len() >= NAME_MAX {
2169 return true; }
2171 name_buf[..name.len()].copy_from_slice(name.as_bytes());
2172 ns_buf[..namespace.len()].copy_from_slice(namespace.as_bytes());
2173 let enc_ptr = match enclave {
2174 Some(e) if e.len() < NAME_MAX => {
2175 enc_buf[..e.len()].copy_from_slice(e.as_bytes());
2176 enc_buf.as_ptr() as *const c_char
2177 }
2178 _ => core::ptr::null(),
2182 };
2183 unsafe {
2184 visit(
2185 ctx,
2186 name_buf.as_ptr() as *const c_char,
2187 ns_buf.as_ptr() as *const c_char,
2188 enc_ptr,
2189 )
2190 }
2191 };
2192
2193 match Session::get_node_names(s, &mut cb) {
2194 Ok(()) => NROS_RMW_RET_OK,
2195 Err(e) => ret_from_error(&e),
2196 }
2197}
2198
2199macro_rules! count_trampoline {
2201 ($name:ident, $method:ident) => {
2202 unsafe extern "C" fn $name<R: RustBackend>(
2203 session: *const NrosRmwSession,
2204 topic_name: *const c_char,
2205 count: *mut usize,
2206 ) -> NrosRmwRet {
2207 if topic_name.is_null() || count.is_null() {
2208 return NROS_RMW_RET_INVALID_ARGUMENT;
2209 }
2210 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
2211 return NROS_RMW_RET_INVALID_ARGUMENT;
2212 };
2213 let Ok(topic) = (unsafe { core::ffi::CStr::from_ptr(topic_name) }).to_str() else {
2214 return NROS_RMW_RET_INVALID_ARGUMENT;
2215 };
2216 match Session::$method(s, topic) {
2217 Ok(n) => {
2218 unsafe { *count = n };
2219 NROS_RMW_RET_OK
2220 }
2221 Err(e) => ret_from_error(&e),
2222 }
2223 }
2224 };
2225}
2226
2227count_trampoline!(count_publishers_trampoline, count_publishers);
2228count_trampoline!(count_subscribers_trampoline, count_subscribers);
2229count_trampoline!(count_clients_trampoline, count_clients);
2232count_trampoline!(count_services_trampoline, count_services);
2233
2234unsafe extern "C" fn get_gid_for_client_trampoline<R: RustBackend>(
2237 client: *const NrosRmwClient,
2238 gid: *mut NrosRmwGid,
2239) -> NrosRmwRet {
2240 if gid.is_null() {
2241 return NROS_RMW_RET_INVALID_ARGUMENT;
2242 }
2243 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
2244 return NROS_RMW_RET_INVALID_ARGUMENT;
2245 };
2246 match ClientTrait::get_gid(&*c) {
2247 Ok(bytes) => {
2248 unsafe {
2250 (*gid).data = bytes;
2251 }
2252 NROS_RMW_RET_OK
2253 }
2254 Err(e) => ret_from_error(&e),
2255 }
2256}
2257
2258unsafe extern "C" fn event_type_is_supported_trampoline<R: RustBackend>(
2264 event_type: NrosRmwEventKind,
2265) -> bool {
2266 use crate::rmw_event_type_t as ck;
2267 let known = matches!(
2268 event_type,
2269 ck::NROS_RMW_EVENT_LIVELINESS_CHANGED
2270 | ck::NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED
2271 | ck::NROS_RMW_EVENT_MESSAGE_LOST
2272 | ck::NROS_RMW_EVENT_LIVELINESS_LOST
2273 | ck::NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED
2274 | ck::NROS_RMW_EVENT_PUBLICATION_MATCHED
2275 | ck::NROS_RMW_EVENT_SUBSCRIPTION_MATCHED
2276 | ck::NROS_RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE
2277 | ck::NROS_RMW_EVENT_SUBSCRIPTION_INCOMPATIBLE_TYPE
2278 );
2279 known && <R::Session as Session>::event_type_is_supported(event_kind_from_c(event_type))
2280}
2281
2282macro_rules! names_and_types_trampoline {
2292 ($name:ident, $method:ident, demangle_flag) => {
2295 unsafe extern "C" fn $name<R: RustBackend>(
2296 session: *const NrosRmwSession,
2297 no_demangle: bool,
2298 visitor: crate::generated::rmw_names_and_types_visitor_t,
2300 ) -> NrosRmwRet {
2301 if no_demangle {
2306 return NROS_RMW_RET_UNSUPPORTED;
2307 }
2308 $crate::names_and_types_body!(R, $method, session, visitor)
2309 }
2310 };
2311 ($name:ident, $method:ident) => {
2312 unsafe extern "C" fn $name<R: RustBackend>(
2313 session: *const NrosRmwSession,
2314 visitor: crate::generated::rmw_names_and_types_visitor_t,
2316 ) -> NrosRmwRet {
2317 $crate::names_and_types_body!(R, $method, session, visitor)
2318 }
2319 };
2320}
2321
2322#[macro_export]
2324#[doc(hidden)]
2325macro_rules! names_and_types_body {
2326 ($R:ident, $method:ident, $session:ident, $visitor:ident) => {{
2327 let (visit_fn, ctx) = ($visitor.visit, $visitor.ctx);
2330 let Some(visit) = visit_fn else {
2331 return NROS_RMW_RET_INVALID_ARGUMENT;
2332 };
2333 let Some(s) = (unsafe { session_mut::<$R::Session>($session.cast_mut()) }) else {
2334 return NROS_RMW_RET_INVALID_ARGUMENT;
2335 };
2336
2337 const NAME_MAX: usize = 256;
2338 const TYPES_MAX: usize = 8;
2339
2340 let mut cb = |name: &str, types: &[&str]| -> bool {
2341 let mut name_buf = [0u8; NAME_MAX];
2342 if name.len() >= NAME_MAX {
2343 return true; }
2345 name_buf[..name.len()].copy_from_slice(name.as_bytes());
2346
2347 let mut type_bufs = [[0u8; NAME_MAX]; TYPES_MAX];
2348 let mut ptrs: [*const c_char; TYPES_MAX] = [core::ptr::null(); TYPES_MAX];
2349 let mut n = 0usize;
2350 for t in types.iter().take(TYPES_MAX) {
2351 if t.len() >= NAME_MAX {
2352 continue;
2353 }
2354 type_bufs[n][..t.len()].copy_from_slice(t.as_bytes());
2355 ptrs[n] = type_bufs[n].as_ptr() as *const c_char;
2356 n += 1;
2357 }
2358 unsafe { visit(ctx, name_buf.as_ptr() as *const c_char, ptrs.as_ptr(), n) }
2359 };
2360
2361 match Session::$method(s, &mut cb) {
2362 Ok(()) => NROS_RMW_RET_OK,
2363 Err(e) => ret_from_error(&e),
2364 }
2365 }};
2366}
2367
2368names_and_types_trampoline!(
2369 get_topic_names_and_types_trampoline,
2370 get_topic_names_and_types,
2371 demangle_flag
2372);
2373names_and_types_trampoline!(
2374 get_service_names_and_types_trampoline,
2375 get_service_names_and_types
2376);
2377
2378unsafe fn by_node_impl<R: RustBackend>(
2387 session: *const NrosRmwSession,
2388 node_name: *const c_char,
2389 node_namespace: *const c_char,
2390 no_demangle: bool,
2391 visitor: crate::generated::rmw_names_and_types_visitor_t,
2393 kind: nros_rmw::GraphEntityKind,
2394) -> NrosRmwRet {
2395 let (visit, ctx) = (visitor.visit, visitor.ctx);
2396 if no_demangle {
2400 return NROS_RMW_RET_UNSUPPORTED;
2401 }
2402 if node_name.is_null() || node_namespace.is_null() {
2403 return NROS_RMW_RET_INVALID_ARGUMENT;
2404 }
2405 let Some(visit) = visit else {
2406 return NROS_RMW_RET_INVALID_ARGUMENT;
2407 };
2408 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
2409 return NROS_RMW_RET_INVALID_ARGUMENT;
2410 };
2411 let (Ok(name), Ok(ns)) = (
2412 unsafe { core::ffi::CStr::from_ptr(node_name) }.to_str(),
2413 unsafe { core::ffi::CStr::from_ptr(node_namespace) }.to_str(),
2414 ) else {
2415 return NROS_RMW_RET_INVALID_ARGUMENT;
2416 };
2417
2418 const NAME_MAX: usize = 256;
2419 const TYPES_MAX: usize = 8;
2420 let mut cb = |n: &str, types: &[&str]| -> bool {
2421 let mut name_buf = [0u8; NAME_MAX];
2422 if n.len() >= NAME_MAX {
2423 return true;
2424 }
2425 name_buf[..n.len()].copy_from_slice(n.as_bytes());
2426 let mut type_bufs = [[0u8; NAME_MAX]; TYPES_MAX];
2427 let mut ptrs: [*const c_char; TYPES_MAX] = [core::ptr::null(); TYPES_MAX];
2428 let mut count = 0usize;
2429 for t in types.iter().take(TYPES_MAX) {
2430 if t.len() >= NAME_MAX {
2431 continue;
2432 }
2433 type_bufs[count][..t.len()].copy_from_slice(t.as_bytes());
2434 ptrs[count] = type_bufs[count].as_ptr() as *const c_char;
2435 count += 1;
2436 }
2437 unsafe {
2438 visit(
2439 ctx,
2440 name_buf.as_ptr() as *const c_char,
2441 ptrs.as_ptr(),
2442 count,
2443 )
2444 }
2445 };
2446 match Session::get_names_and_types_by_node(s, kind, name, ns, &mut cb) {
2447 Ok(()) => NROS_RMW_RET_OK,
2448 Err(e) => ret_from_error(&e),
2449 }
2450}
2451
2452macro_rules! by_node_trampoline {
2453 ($name:ident, $kind:ident, no_demangle) => {
2458 unsafe extern "C" fn $name<R: RustBackend>(
2459 session: *const NrosRmwSession,
2460 node_name: *const c_char,
2461 node_namespace: *const c_char,
2462 no_demangle: bool,
2463 visitor: crate::generated::rmw_names_and_types_visitor_t,
2465 ) -> NrosRmwRet {
2466 unsafe {
2467 by_node_impl::<R>(
2468 session,
2469 node_name,
2470 node_namespace,
2471 no_demangle,
2472 visitor,
2473 nros_rmw::GraphEntityKind::$kind,
2474 )
2475 }
2476 }
2477 };
2478 ($name:ident, $kind:ident) => {
2480 unsafe extern "C" fn $name<R: RustBackend>(
2481 session: *const NrosRmwSession,
2482 node_name: *const c_char,
2483 node_namespace: *const c_char,
2484 visitor: crate::generated::rmw_names_and_types_visitor_t,
2486 ) -> NrosRmwRet {
2487 unsafe {
2488 by_node_impl::<R>(
2489 session,
2490 node_name,
2491 node_namespace,
2492 false,
2493 visitor,
2494 nros_rmw::GraphEntityKind::$kind,
2495 )
2496 }
2497 }
2498 };
2499}
2500
2501by_node_trampoline!(
2502 get_publisher_names_and_types_by_node_trampoline,
2503 Publisher,
2504 no_demangle
2505);
2506by_node_trampoline!(
2507 get_subscriber_names_and_types_by_node_trampoline,
2508 Subscriber,
2509 no_demangle
2510);
2511by_node_trampoline!(get_service_names_and_types_by_node_trampoline, Service);
2512by_node_trampoline!(get_client_names_and_types_by_node_trampoline, Client);
2513
2514unsafe fn endpoint_info_impl<R: RustBackend>(
2519 session: *const NrosRmwSession,
2520 topic_name: *const c_char,
2521 no_mangle: bool,
2522 visitor: crate::generated::rmw_topic_endpoint_info_visitor_t,
2524 publishers: bool,
2525) -> NrosRmwRet {
2526 let (visit, ctx) = (visitor.visit, visitor.ctx);
2527 if no_mangle {
2528 return NROS_RMW_RET_UNSUPPORTED;
2529 }
2530 if topic_name.is_null() {
2531 return NROS_RMW_RET_INVALID_ARGUMENT;
2532 }
2533 let Some(visit) = visit else {
2534 return NROS_RMW_RET_INVALID_ARGUMENT;
2535 };
2536 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
2537 return NROS_RMW_RET_INVALID_ARGUMENT;
2538 };
2539 let Ok(topic) = (unsafe { core::ffi::CStr::from_ptr(topic_name) }).to_str() else {
2540 return NROS_RMW_RET_INVALID_ARGUMENT;
2541 };
2542
2543 const NAME_MAX: usize = 256;
2544 let mut cb = |info: &nros_rmw::GraphEndpointInfo<'_>| -> bool {
2545 let mut name_buf = [0u8; NAME_MAX];
2546 let mut ns_buf = [0u8; NAME_MAX];
2547 let mut ty_buf = [0u8; NAME_MAX];
2548 if info.node_name.len() >= NAME_MAX
2549 || info.node_namespace.len() >= NAME_MAX
2550 || info.topic_type.len() >= NAME_MAX
2551 {
2552 return true; }
2554 name_buf[..info.node_name.len()].copy_from_slice(info.node_name.as_bytes());
2555 ns_buf[..info.node_namespace.len()].copy_from_slice(info.node_namespace.as_bytes());
2556 ty_buf[..info.topic_type.len()].copy_from_slice(info.topic_type.as_bytes());
2557 let qos: rmw_qos_profile_t = match NrosRmwQos::try_from(info.qos) {
2569 Ok(q) => q,
2570 Err(_) => {
2571 let mut q: rmw_qos_profile_t = unsafe { core::mem::zeroed() };
2572 q.reliability = NROS_RMW_RELIABILITY_UNKNOWN as u8;
2573 q.durability = NROS_RMW_DURABILITY_UNKNOWN as u8;
2574 q.history = NROS_RMW_HISTORY_UNKNOWN as u8;
2575 q.liveliness_kind = rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_UNKNOWN as u8;
2576 q
2577 }
2578 };
2579 let c_info = rmw_topic_endpoint_info_t {
2580 node_name: name_buf.as_ptr() as *const c_char,
2581 node_namespace: ns_buf.as_ptr() as *const c_char,
2582 topic_type: ty_buf.as_ptr() as *const c_char,
2583 endpoint_type: if info.is_publisher {
2584 rmw_endpoint_type_t::RMW_ENDPOINT_PUBLISHER
2585 } else {
2586 rmw_endpoint_type_t::RMW_ENDPOINT_SUBSCRIPTION
2587 },
2588 endpoint_gid: rmw_gid_t {
2589 implementation_identifier: core::ptr::null(),
2590 data: info.endpoint_gid,
2591 },
2592 qos_profile: qos,
2593 };
2594 unsafe { visit(ctx, &c_info as *const rmw_topic_endpoint_info_t) }
2595 };
2596 match Session::get_endpoint_info_by_topic(s, publishers, topic, &mut cb) {
2597 Ok(()) => NROS_RMW_RET_OK,
2598 Err(e) => ret_from_error(&e),
2599 }
2600}
2601
2602macro_rules! endpoint_info_trampoline {
2603 ($name:ident, $publishers:expr) => {
2604 unsafe extern "C" fn $name<R: RustBackend>(
2605 session: *const NrosRmwSession,
2606 topic_name: *const c_char,
2607 no_mangle: bool,
2608 visitor: crate::generated::rmw_topic_endpoint_info_visitor_t,
2610 ) -> NrosRmwRet {
2611 unsafe { endpoint_info_impl::<R>(session, topic_name, no_mangle, visitor, $publishers) }
2612 }
2613 };
2614}
2615
2616endpoint_info_trampoline!(get_publishers_info_by_topic_trampoline, true);
2617endpoint_info_trampoline!(get_subscriptions_info_by_topic_trampoline, false);
2618
2619unsafe extern "C" fn publish_streamed_trampoline<R: RustBackend>(
2620 publisher: *mut NrosRmwPublisher,
2621 size_cb: Option<unsafe extern "C" fn(out_total_len: *mut usize, user_ctx: *mut c_void)>,
2622 chunk_cb: Option<
2623 unsafe extern "C" fn(
2624 out_buf: *mut u8,
2625 cap: usize,
2626 out_written: *mut usize,
2627 user_ctx: *mut c_void,
2628 ),
2629 >,
2630 user_ctx: *mut c_void,
2631) -> NrosRmwRet {
2632 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
2638 return NROS_RMW_RET_INVALID_ARGUMENT;
2639 };
2640 let (Some(size_cb), Some(chunk_cb)) = (size_cb, chunk_cb) else {
2642 return NROS_RMW_RET_INVALID_ARGUMENT;
2643 };
2644 match unsafe { Publisher::publish_streamed(p, size_cb, chunk_cb, user_ctx) } {
2647 Ok(()) => NROS_RMW_RET_OK,
2648 Err(e) => ret_from_error(&e),
2649 }
2650}
2651
2652unsafe extern "C" fn take_sequence_trampoline<R: RustBackend>(
2653 subscriber: *const NrosRmwSubscription,
2654 buf: *mut u8,
2655 per_msg_cap: usize,
2656 max_msgs: usize,
2657 out_lens: *mut usize,
2658 taken: *mut usize,
2659) -> NrosRmwRet {
2660 if taken.is_null() {
2663 return NROS_RMW_RET_INVALID_ARGUMENT;
2664 }
2665 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
2670 return NROS_RMW_RET_INVALID_ARGUMENT;
2671 };
2672 if buf.is_null() || out_lens.is_null() || per_msg_cap == 0 {
2673 return NROS_RMW_RET_INVALID_ARGUMENT;
2674 }
2675 let buf_slice =
2680 unsafe { core::slice::from_raw_parts_mut(buf, max_msgs.saturating_mul(per_msg_cap)) };
2681 let lens_slice = unsafe { core::slice::from_raw_parts_mut(out_lens, max_msgs) };
2682 match Subscription::take_sequence(s, buf_slice, per_msg_cap, max_msgs, lens_slice) {
2683 Ok(count) => {
2684 unsafe { *taken = count };
2686 NROS_RMW_RET_OK
2687 }
2688 Err(e) => ret_from_error(&e),
2689 }
2690}
2691
2692#[inline]
2710unsafe fn session_data_mut(session: *mut NrosRmwSession) -> &'static mut *mut c_void {
2711 unsafe { &mut (*session).backend_data }
2712}
2713
2714#[inline]
2715unsafe fn publisher_data_mut(publisher: *mut NrosRmwPublisher) -> &'static mut *mut c_void {
2716 unsafe { &mut (*publisher).backend_data }
2717}
2718
2719#[inline]
2720unsafe fn subscription_data_mut(subscriber: *mut NrosRmwSubscription) -> &'static mut *mut c_void {
2721 unsafe { &mut (*subscriber).backend_data }
2722}
2723
2724#[inline]
2725unsafe fn service_data_mut(server: *mut NrosRmwService) -> &'static mut *mut c_void {
2726 unsafe { &mut (*server).backend_data }
2727}
2728
2729#[inline]
2730unsafe fn client_data_mut(client: *mut NrosRmwClient) -> &'static mut *mut c_void {
2731 unsafe { &mut (*client).backend_data }
2732}
2733
2734#[inline]
2735unsafe fn session_mut<'a, T>(session: *mut NrosRmwSession) -> Option<&'a mut T> {
2736 if session.is_null() {
2737 return None;
2738 }
2739 let p = unsafe { (*session).backend_data } as *mut T;
2740 if p.is_null() {
2741 None
2742 } else {
2743 Some(unsafe { &mut *p })
2744 }
2745}
2746
2747#[inline]
2748unsafe fn session_ref<'a, T>(session: *const NrosRmwSession) -> Option<&'a T> {
2749 if session.is_null() {
2750 return None;
2751 }
2752 let p = unsafe { (*session).backend_data } as *const T;
2753 if p.is_null() {
2754 None
2755 } else {
2756 Some(unsafe { &*p })
2757 }
2758}
2759
2760#[inline]
2761unsafe fn publisher_ref<'a, T>(publisher: *const NrosRmwPublisher) -> Option<&'a T> {
2762 if publisher.is_null() {
2763 return None;
2764 }
2765 let p = unsafe { (*publisher).backend_data } as *const T;
2766 if p.is_null() {
2767 None
2768 } else {
2769 Some(unsafe { &*p })
2770 }
2771}
2772
2773#[inline]
2774unsafe fn subscription_mut<'a, T>(subscriber: *const NrosRmwSubscription) -> Option<&'a mut T> {
2775 if subscriber.is_null() {
2776 return None;
2777 }
2778 let p = unsafe { (*subscriber).backend_data } as *mut T;
2779 if p.is_null() {
2780 None
2781 } else {
2782 Some(unsafe { &mut *p })
2783 }
2784}
2785
2786#[inline]
2787unsafe fn subscription_ref<'a, T>(subscriber: *const NrosRmwSubscription) -> Option<&'a T> {
2788 if subscriber.is_null() {
2789 return None;
2790 }
2791 let p = unsafe { (*subscriber).backend_data } as *const T;
2792 if p.is_null() {
2793 None
2794 } else {
2795 Some(unsafe { &*p })
2796 }
2797}
2798
2799#[inline]
2800unsafe fn service_mut<'a, T>(server: *const NrosRmwService) -> Option<&'a mut T> {
2801 if server.is_null() {
2802 return None;
2803 }
2804 let p = unsafe { (*server).backend_data } as *mut T;
2805 if p.is_null() {
2806 None
2807 } else {
2808 Some(unsafe { &mut *p })
2809 }
2810}
2811
2812#[inline]
2813unsafe fn service_ref<'a, T>(server: *const NrosRmwService) -> Option<&'a T> {
2814 if server.is_null() {
2815 return None;
2816 }
2817 let p = unsafe { (*server).backend_data } as *const T;
2818 if p.is_null() {
2819 None
2820 } else {
2821 Some(unsafe { &*p })
2822 }
2823}
2824
2825#[inline]
2826unsafe fn client_mut<'a, T>(client: *const NrosRmwClient) -> Option<&'a mut T> {
2827 if client.is_null() {
2828 return None;
2829 }
2830 let p = unsafe { (*client).backend_data } as *mut T;
2831 if p.is_null() {
2832 None
2833 } else {
2834 Some(unsafe { &mut *p })
2835 }
2836}
2837
2838fn box_handle<T>(kind: &str, value: T) -> Result<*mut c_void, T> {
2855 match nros_rmw::fallible::try_box(value) {
2856 Ok(boxed) => Ok(Box::into_raw(boxed) as *mut c_void),
2857 Err(value) => {
2858 nros_log::log_error!(
2859 nros_log::get_logger("nros_rmw_cffi"),
2860 "heap exhausted: could not allocate {} bytes for a {} handle; \
2861 the {} is NOT created (NROS_RMW_RET_BAD_ALLOC). Raise the \
2862 platform heap (Zephyr: CONFIG_NROS_ZEPHYR_HEAP_SIZE)",
2863 core::mem::size_of::<T>(),
2864 kind,
2865 kind
2866 );
2867 Err(value)
2868 }
2869 }
2870}
2871
2872#[inline]
2873unsafe fn take_box<T>(slot: &mut *mut c_void) -> Option<Box<T>> {
2874 if slot.is_null() {
2875 return None;
2876 }
2877 let ptr = *slot as *mut T;
2878 *slot = core::ptr::null_mut();
2879 if ptr.is_null() {
2880 None
2881 } else {
2882 Some(unsafe { Box::from_raw(ptr) })
2887 }
2888}
2889
2890#[cfg(test)]
2891mod tests {
2892 use super::*;
2893
2894 #[test]
2902 fn a_zero_filled_c_profile_reads_back_as_the_sentinel() {
2903 let raised = qos_from_cffi(&crate::NROS_RMW_QOS_PROFILE_SYSTEM_DEFAULT);
2904 assert_eq!(raised, nros_rmw::QoSProfile::QOS_PROFILE_SYSTEM_DEFAULT);
2905 assert!(raised.has_unresolved_system_default());
2906 }
2907
2908 #[test]
2912 fn unknown_is_not_raised_to_the_sentinel() {
2913 let mut q = crate::NROS_RMW_QOS_PROFILE_SYSTEM_DEFAULT;
2914 q.reliability = crate::generated::NROS_RMW_RELIABILITY_UNKNOWN as u8;
2915 let raised = qos_from_cffi(&q);
2916 assert_eq!(
2917 raised.reliability,
2918 nros_rmw::QoSReliabilityPolicy::Reliable,
2919 "UNKNOWN must keep folding to the ROS default, not become a sentinel"
2920 );
2921 }
2922}