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 event_kind_from_c, ret_from_error, 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 = crate::parse_env_usize(
99 env!("NROS_RMW_SUBSCRIBER_SLOT_BYTES"),
100 "NROS_RMW_SUBSCRIBER_SLOT_BYTES must be a decimal integer",
101 );
102 const SLOT_ALIGN: usize = 16;
103
104 #[repr(align(16))]
105 struct Slot {
106 bytes: UnsafeCell<[u8; SLOT_SIZE]>,
107 }
108
109 const _: () = assert!(mem::align_of::<Slot>() == SLOT_ALIGN);
112
113 unsafe impl Sync for Slot {}
114
115 impl Slot {
116 const fn new() -> Self {
117 Self {
118 bytes: UnsafeCell::new([0; SLOT_SIZE]),
119 }
120 }
121 }
122
123 static USED: [AtomicBool; SLOT_COUNT] = [const { AtomicBool::new(false) }; SLOT_COUNT];
129 static SLOTS: [Slot; SLOT_COUNT] = [const { Slot::new() }; SLOT_COUNT];
130
131 pub unsafe fn insert<T>(value: T) -> Option<*mut T> {
153 const {
154 let _raise_nros_rmw_subscriber_slot_bytes = SLOT_SIZE - mem::size_of::<T>();
181 let _over_aligned_for_the_cffi_subscriber_slot = SLOT_ALIGN - mem::align_of::<T>();
187 };
188
189 for index in 0..SLOT_COUNT {
190 if USED[index]
191 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
192 .is_err()
193 {
194 continue;
195 }
196
197 let ptr = SLOTS[index].bytes.get().cast::<T>();
198 unsafe { ptr.write(value) };
199 return Some(ptr);
200 }
201
202 nros_log::log_error!(
208 nros_log::get_logger("nros_rmw_cffi"),
209 "subscription pool exhausted: all {} static slot(s) are in use. \
210 Raise NROS_RMW_SUBSCRIBER_SLOTS (each slot costs \
211 NROS_RMW_SUBSCRIBER_SLOT_BYTES = {} bytes of .bss). A handle that \
212 did not FIT cannot reach this path — that is a build error",
213 SLOT_COUNT,
214 SLOT_SIZE
215 );
216 None
217 }
218
219 pub unsafe fn take<T>(ptr: *mut T) -> bool {
220 if ptr.is_null() {
221 return false;
222 }
223
224 for index in 0..SLOT_COUNT {
225 let slot_ptr = SLOTS[index].bytes.get().cast::<T>();
226 if ptr != slot_ptr {
227 continue;
228 }
229
230 unsafe { ptr::drop_in_place(ptr) };
231 USED[index].store(false, Ordering::Release);
232 return true;
233 }
234
235 false
236 }
237}
238
239pub trait RustBackend: Sized {
254 type Session: Session<
255 Error = TransportError,
256 PublisherHandle = Self::Publisher,
257 SubscriptionHandle = Self::Subscription,
258 ServiceHandle = Self::Service,
259 ClientHandle = Self::Client,
260 > + 'static;
261 type Publisher: Publisher<Error = TransportError> + 'static;
262 type Subscription: Subscription<Error = TransportError> + 'static;
263 type Service: ServiceTrait<Error = TransportError> + 'static;
264 type Client: ClientTrait<Error = TransportError> + 'static;
265
266 fn factory() -> Self;
271
272 fn open(self, config: &RmwConfig) -> Result<Self::Session, TransportError>;
274}
275
276impl<R> RustBackend for R
277where
278 R: Rmw<Error = TransportError> + Default + Sized,
279 R::Session: Session<Error = TransportError> + 'static,
280 <R::Session as Session>::PublisherHandle: Publisher<Error = TransportError> + 'static,
281 <R::Session as Session>::SubscriptionHandle: Subscription<Error = TransportError> + 'static,
282 <R::Session as Session>::ServiceHandle: ServiceTrait<Error = TransportError> + 'static,
283 <R::Session as Session>::ClientHandle: ClientTrait<Error = TransportError> + 'static,
284{
285 type Session = R::Session;
286 type Publisher = <R::Session as Session>::PublisherHandle;
287 type Subscription = <R::Session as Session>::SubscriptionHandle;
288 type Service = <R::Session as Session>::ServiceHandle;
289 type Client = <R::Session as Session>::ClientHandle;
290
291 fn factory() -> Self {
292 R::default()
293 }
294
295 fn open(self, config: &RmwConfig) -> Result<Self::Session, TransportError> {
296 Rmw::open(self, config)
297 }
298}
299
300unsafe fn cstr_to_str<'a>(ptr: *const core::ffi::c_char) -> &'a str {
312 if ptr.is_null() {
313 return "";
314 }
315 let ptr = ptr.cast::<u8>();
316 let mut len = 0usize;
317 while len < 4096 && unsafe { *ptr.add(len) } != 0 {
321 len += 1;
322 }
323 let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
324 core::str::from_utf8(slice).unwrap_or("")
325}
326
327unsafe fn cstr_to_str_strict<'a>(ptr: *const core::ffi::c_char) -> Option<&'a str> {
335 if ptr.is_null() {
336 return None;
337 }
338 let bytes = ptr.cast::<u8>();
339 let mut len = 0usize;
340 while len < 4096 && unsafe { *bytes.add(len) } != 0 {
341 len += 1;
342 }
343 if len == 0 {
344 return None;
345 }
346 let slice = unsafe { core::slice::from_raw_parts(bytes, len) };
347 core::str::from_utf8(slice).ok()
348}
349
350unsafe fn collect_session_properties<'a>(
357 options: *const NrosRmwSessionOptions,
358 out: &mut [(&'a str, &'a str); MAX_SESSION_PROPERTIES],
359) -> Result<usize, NrosRmwRet> {
360 if options.is_null() {
361 return Ok(0);
362 }
363 let opts = unsafe { &*options };
364 let count = opts.property_count;
365 if count == 0 {
366 return Ok(0);
367 }
368 if opts.properties.is_null() || count > MAX_SESSION_PROPERTIES {
369 return Err(NROS_RMW_RET_INVALID_ARGUMENT);
370 }
371 for (i, slot) in out.iter_mut().enumerate().take(count) {
372 let entry = unsafe { &*opts.properties.add(i) };
373 let (Some(key), Some(value)) = (unsafe { cstr_to_str_strict(entry.key) }, unsafe {
374 cstr_to_str_strict(entry.value)
375 }) else {
376 return Err(NROS_RMW_RET_INVALID_ARGUMENT);
377 };
378 *slot = (key, value);
379 }
380 Ok(count)
381}
382
383fn qos_from_cffi(q: &NrosRmwQos) -> QoSProfile {
387 use crate::generated;
388 use nros_rmw::{
389 QoSDurabilityPolicy, QoSHistoryPolicy, QoSLivelinessPolicy, QoSReliabilityPolicy,
390 };
391 QoSProfile {
405 reliability: match q.reliability as i32 {
406 generated::NROS_RMW_RELIABILITY_SYSTEM_DEFAULT => QoSReliabilityPolicy::SystemDefault,
407 generated::NROS_RMW_RELIABILITY_BEST_EFFORT => QoSReliabilityPolicy::BestEffort,
408 generated::NROS_RMW_RELIABILITY_RELIABLE => QoSReliabilityPolicy::Reliable,
409 _ => QoSReliabilityPolicy::Reliable,
410 },
411 durability: match q.durability as i32 {
412 generated::NROS_RMW_DURABILITY_SYSTEM_DEFAULT => QoSDurabilityPolicy::SystemDefault,
413 generated::NROS_RMW_DURABILITY_TRANSIENT_LOCAL => QoSDurabilityPolicy::TransientLocal,
414 _ => QoSDurabilityPolicy::Volatile,
415 },
416 history: match q.history as i32 {
417 generated::NROS_RMW_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
418 generated::NROS_RMW_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
419 _ => QoSHistoryPolicy::KeepLast,
420 },
421 depth: q.depth as u32,
424 tx_express: false,
427 deadline_ms: q.deadline_ms,
428 lifespan_ms: q.lifespan_ms,
429 liveliness_kind: match q.liveliness_kind as u32 {
430 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_AUTOMATIC => {
431 QoSLivelinessPolicy::Automatic
432 }
433 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_MANUAL_BY_NODE => {
434 QoSLivelinessPolicy::ManualByNode
435 }
436 generated::rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_MANUAL_BY_TOPIC => {
437 QoSLivelinessPolicy::ManualByTopic
438 }
439 _ => QoSLivelinessPolicy::None,
440 },
441 liveliness_lease_ms: q.liveliness_lease_ms,
442 avoid_ros_namespace_conventions: q.avoid_ros_namespace_conventions != 0,
443 }
444}
445
446unsafe fn node_name_of<'a>(node: *const NrosRmwNode) -> Option<&'a str> {
453 let name = unsafe { cstr_to_str((*node).name) };
454 if name.is_empty() { None } else { Some(name) }
455}
456
457unsafe fn node_namespace_of<'a>(node: *const NrosRmwNode) -> &'a str {
458 let namespace = unsafe { cstr_to_str((*node).namespace_) };
459 if namespace.is_empty() { "/" } else { namespace }
460}
461
462const FORMAT_NAME_CAP: usize = 32;
479
480const fn format_name_cstr(name: &str) -> [u8; FORMAT_NAME_CAP] {
482 let bytes = name.as_bytes();
483 assert!(
484 bytes.len() < FORMAT_NAME_CAP,
485 "serialization format name does not fit the vtable slot's buffer"
486 );
487 let mut out = [0u8; FORMAT_NAME_CAP];
488 let mut i = 0;
489 while i < bytes.len() {
490 out[i] = bytes[i];
491 i += 1;
492 }
493 out
494}
495
496unsafe extern "C" fn get_serialization_format_trampoline<R: RustBackend>() -> *const c_char {
506 RustBackendAdapter::<R>::SERIALIZATION_FORMAT_CSTR
507 .as_ptr()
508 .cast()
509}
510
511unsafe extern "C" fn supported_qos_policies_trampoline<R: RustBackend>(
523 session: *const NrosRmwSession,
524 out_mask: *mut u32,
525) -> NrosRmwRet {
526 if out_mask.is_null() {
527 return NROS_RMW_RET_INVALID_ARGUMENT;
528 }
529 let Some(s) = (unsafe { session_ref::<R::Session>(session) }) else {
530 return NROS_RMW_RET_INVALID_ARGUMENT;
531 };
532 unsafe { *out_mask = Session::supported_qos_policies(s).0 };
534 NROS_RMW_RET_OK
535}
536
537pub struct RustBackendAdapter<R>(PhantomData<R>);
540
541impl<R: RustBackend> RustBackendAdapter<R> {
542 const SERIALIZATION_FORMAT_CSTR: &'static [u8; FORMAT_NAME_CAP] =
550 &format_name_cstr(<R::Session as Session>::SERIALIZATION_FORMAT);
551
552 pub const VTABLE: NrosRmwVtable = NrosRmwVtable {
556 create_session: Some(create_session_trampoline::<R>),
557 destroy_session: Some(destroy_session_trampoline::<R>),
558 drive_io: Some(drive_io_trampoline::<R>),
559 create_publisher: Some(create_publisher_trampoline::<R>),
560 destroy_publisher: Some(destroy_publisher_trampoline::<R>),
561 publish: Some(publish_trampoline::<R>),
562 create_subscription: Some(create_subscription_trampoline::<R>),
563 destroy_subscription: Some(destroy_subscription_trampoline::<R>),
564 take: Some(take_trampoline::<R>),
565 has_data: Some(has_data_trampoline::<R>),
566 create_service: Some(create_service_trampoline::<R>),
567 destroy_service: Some(destroy_service_trampoline::<R>),
568 take_request: Some(take_request_trampoline::<R>),
569 has_request: Some(has_request_trampoline::<R>),
570 send_response: Some(send_response_trampoline::<R>),
571 create_client: Some(create_client_trampoline::<R>),
572 destroy_client: Some(destroy_client_trampoline::<R>),
573 send_request: Some(send_request_trampoline::<R>),
576 take_response: Some(take_response_trampoline::<R>),
577 subscription_event_init: Some(subscription_event_init_trampoline::<R>),
578 publisher_event_init: Some(publisher_event_init_trampoline::<R>),
579 publisher_assert_liveliness: Some(publisher_assert_liveliness_trampoline::<R>),
580 get_gid_for_publisher: Some(get_gid_for_publisher_trampoline::<R>),
581 next_deadline_ms: Some(next_deadline_ms_trampoline::<R>),
582 set_wake_callback: Some(set_wake_callback_trampoline::<R>),
583 service_server_is_available: Some(service_server_is_available_trampoline::<R>),
588 take_sequence: Some(take_sequence_trampoline::<R>),
589 publish_streamed: Some(publish_streamed_trampoline::<R>),
590 ping_session: Some(ping_session_trampoline::<R>),
591 subscription_supports_in_place: Some(subscription_supports_in_place_trampoline::<R>),
592 process_raw_in_place: Some(process_raw_in_place_trampoline::<R>),
593 get_node_names: Some(get_node_names_trampoline::<R>),
598 count_publishers: Some(count_publishers_trampoline::<R>),
599 count_subscribers: Some(count_subscribers_trampoline::<R>),
600 get_topic_names_and_types: Some(get_topic_names_and_types_trampoline::<R>),
601 get_service_names_and_types: Some(get_service_names_and_types_trampoline::<R>),
602 get_publisher_names_and_types_by_node: Some(
606 get_publisher_names_and_types_by_node_trampoline::<R>,
607 ),
608 get_subscriber_names_and_types_by_node: Some(
609 get_subscriber_names_and_types_by_node_trampoline::<R>,
610 ),
611 get_service_names_and_types_by_node: Some(
612 get_service_names_and_types_by_node_trampoline::<R>,
613 ),
614 get_client_names_and_types_by_node: Some(
615 get_client_names_and_types_by_node_trampoline::<R>,
616 ),
617 get_publishers_info_by_topic: Some(get_publishers_info_by_topic_trampoline::<R>),
618 get_subscriptions_info_by_topic: Some(get_subscriptions_info_by_topic_trampoline::<R>),
619 node_get_graph_guard_condition: Some(node_get_graph_guard_condition_trampoline::<R>),
628 get_serialization_format: Some(get_serialization_format_trampoline::<R>),
634 supported_qos_policies: Some(supported_qos_policies_trampoline::<R>),
640 publisher_get_actual_qos: Some(publisher_get_actual_qos_trampoline::<R>),
643 subscription_get_actual_qos: Some(subscription_get_actual_qos_trampoline::<R>),
644 client_request_publisher_get_actual_qos: Some(
645 client_request_publisher_get_actual_qos_trampoline::<R>,
646 ),
647 client_response_subscription_get_actual_qos: Some(
648 client_response_subscription_get_actual_qos_trampoline::<R>,
649 ),
650 service_request_subscription_get_actual_qos: Some(
651 service_request_subscription_get_actual_qos_trampoline::<R>,
652 ),
653 service_response_publisher_get_actual_qos: Some(
654 service_response_publisher_get_actual_qos_trampoline::<R>,
655 ),
656 ..EMPTY_VTABLE
657 };
658
659 pub fn register() -> NrosRmwRet {
669 unsafe { crate::nros_rmw_cffi_register_named(c"default".as_ptr(), &Self::VTABLE) }
672 }
673
674 pub unsafe fn register_named(name: *const core::ffi::c_char) -> NrosRmwRet {
680 unsafe { crate::nros_rmw_cffi_register_named(name, &Self::VTABLE) }
683 }
684}
685
686unsafe extern "C" fn create_session_trampoline<R: RustBackend>(
691 locator: *const core::ffi::c_char,
692 mode: u8,
693 domain_id: u32,
694 node_name: *const core::ffi::c_char,
695 options: *const NrosRmwSessionOptions,
696 out: *mut NrosRmwSession,
697) -> NrosRmwRet {
698 if out.is_null() {
699 return NROS_RMW_RET_INVALID_ARGUMENT;
700 }
701 let mut prop_buf: [(&str, &str); MAX_SESSION_PROPERTIES] = [("", ""); MAX_SESSION_PROPERTIES];
716 let prop_count = match unsafe { collect_session_properties(options, &mut prop_buf) } {
717 Ok(n) => n,
718 Err(ret) => return ret,
719 };
720 let cfg = RmwConfig {
721 locator: unsafe { cstr_to_str(locator) },
722 mode: if mode == 0 {
723 SessionMode::Client
724 } else {
725 SessionMode::Peer
726 },
727 domain_id,
728 node_name: unsafe { cstr_to_str(node_name) },
729 namespace: unsafe { cstr_to_str((*out).namespace_) },
741 properties: &prop_buf[..prop_count],
742 };
743 let factory = R::factory();
744 match factory.open(&cfg) {
745 Ok(session) => match box_handle("session", session) {
746 Ok(boxed) => {
747 unsafe {
748 (*out).backend_data = boxed;
749 }
750 NROS_RMW_RET_OK
751 }
752 Err(mut session) => {
755 let _ = Session::close(&mut session);
756 drop(session);
757 NROS_RMW_RET_BAD_ALLOC
758 }
759 },
760 Err(e) => ret_from_error(&e),
761 }
762}
763
764unsafe extern "C" fn destroy_session_trampoline<R: RustBackend>(
765 session: *mut NrosRmwSession,
766) -> NrosRmwRet {
767 let Some(boxed) = (unsafe { take_box::<R::Session>(session_data_mut(session)) }) else {
768 return NROS_RMW_RET_INVALID_ARGUMENT;
769 };
770 let mut s = boxed;
771 let ret = match Session::close(&mut *s) {
772 Ok(()) => NROS_RMW_RET_OK,
773 Err(e) => ret_from_error(&e),
774 };
775 drop(s);
776 ret
777}
778
779unsafe extern "C" fn drive_io_trampoline<R: RustBackend>(
780 session: *mut NrosRmwSession,
781 timeout_ms: i32,
782) -> NrosRmwRet {
783 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
784 return NROS_RMW_RET_INVALID_ARGUMENT;
785 };
786 match Session::drive_io(s, timeout_ms) {
787 Ok(()) => NROS_RMW_RET_OK,
788 Err(e) => ret_from_error(&e),
789 }
790}
791
792unsafe extern "C" fn create_publisher_trampoline<R: RustBackend>(
797 node: *const NrosRmwNode,
798 type_support: *const crate::generated::rmw_message_type_support_t,
801 topic_name: *const core::ffi::c_char,
802 domain_id: u32,
803 qos: *const NrosRmwQos,
804 options: *const rmw_publisher_options_t,
805 out: *mut NrosRmwPublisher,
806) -> NrosRmwRet {
807 let Some(ts) = (unsafe { type_support.as_ref() }) else {
812 return NROS_RMW_RET_INVALID_ARGUMENT;
813 };
814 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
815 if out.is_null() || qos.is_null() {
816 return NROS_RMW_RET_INVALID_ARGUMENT;
817 }
818 if node.is_null() {
822 return NROS_RMW_RET_INVALID_ARGUMENT;
823 }
824 let session = unsafe { (*node).session };
825 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
826 return NROS_RMW_RET_INVALID_ARGUMENT;
827 };
828 let node_name = unsafe { node_name_of(node) };
829 let namespace = unsafe { node_namespace_of(node) };
830 let topic = TopicInfo {
831 name: unsafe { cstr_to_str(topic_name) },
832 type_name: unsafe { cstr_to_str(type_name) },
833 type_hash: unsafe { cstr_to_str(type_hash) },
834 domain_id,
835 node_name,
836 namespace,
837 rx_buffer_hint: 0,
839 tx_express: unsafe { options.as_ref() }.is_some_and(|o| o.tx_express != 0),
842 };
843 let qos_settings = qos_from_cffi(unsafe { &*qos });
844 match Session::create_publisher(s, &topic, qos_settings) {
845 Ok(pub_handle) => match box_handle("publisher", pub_handle) {
846 Ok(boxed) => {
847 unsafe {
848 (*out).backend_data = boxed;
849 }
850 NROS_RMW_RET_OK
851 }
852 Err(pub_handle) => {
854 drop(pub_handle);
855 NROS_RMW_RET_BAD_ALLOC
856 }
857 },
858 Err(e) => ret_from_error(&e),
859 }
860}
861
862unsafe extern "C" fn destroy_publisher_trampoline<R: RustBackend>(
863 publisher: *mut NrosRmwPublisher,
864) -> NrosRmwRet {
865 let _ = unsafe { take_box::<R::Publisher>(publisher_data_mut(publisher)) };
866
867 NROS_RMW_RET_OK
868}
869
870unsafe extern "C" fn publish_trampoline<R: RustBackend>(
871 publisher: *const NrosRmwPublisher,
872 payload: crate::generated::rmw_byte_span_t,
874) -> NrosRmwRet {
875 let (data, len) = (payload.data, payload.len);
876 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
877 return NROS_RMW_RET_INVALID_ARGUMENT;
878 };
879 if data.is_null() && len != 0 {
880 return NROS_RMW_RET_INVALID_ARGUMENT;
881 }
882 let slice = unsafe { core::slice::from_raw_parts(data, len) };
883 match Publisher::publish_raw(p, slice) {
884 Ok(()) => NROS_RMW_RET_OK,
885 Err(e) => ret_from_error(&e),
886 }
887}
888
889unsafe extern "C" fn create_subscription_trampoline<R: RustBackend>(
894 node: *const NrosRmwNode,
895 type_support: *const crate::generated::rmw_message_type_support_t,
898 topic_name: *const core::ffi::c_char,
899 domain_id: u32,
900 qos: *const NrosRmwQos,
901 options: *const rmw_subscription_options_t,
902 out: *mut NrosRmwSubscription,
903) -> NrosRmwRet {
904 let Some(ts) = (unsafe { type_support.as_ref() }) else {
909 return NROS_RMW_RET_INVALID_ARGUMENT;
910 };
911 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
912 if out.is_null() || qos.is_null() {
913 return NROS_RMW_RET_INVALID_ARGUMENT;
914 }
915 if node.is_null() {
919 return NROS_RMW_RET_INVALID_ARGUMENT;
920 }
921 let session = unsafe { (*node).session };
922 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
923 return NROS_RMW_RET_INVALID_ARGUMENT;
924 };
925 let node_name = unsafe { node_name_of(node) };
926 let namespace = unsafe { node_namespace_of(node) };
927 let topic = TopicInfo {
928 name: unsafe { cstr_to_str(topic_name) },
929 type_name: unsafe { cstr_to_str(type_name) },
930 type_hash: unsafe { cstr_to_str(type_hash) },
931 domain_id,
932 node_name,
933 namespace,
934 rx_buffer_hint: unsafe { options.as_ref() }.map_or(0, |o| o.rx_buffer_hint as usize),
938 tx_express: false,
940 };
941 let qos_settings = qos_from_cffi(unsafe { &*qos });
942 match Session::create_subscription(s, &topic, qos_settings) {
943 Ok(sub_handle) => {
944 #[cfg(all(target_os = "none", not(feature = "std")))]
945 {
946 let Some(ptr) = (unsafe { static_subscriber_storage::insert(sub_handle) }) else {
947 return crate::NROS_RMW_RET_BAD_ALLOC;
948 };
949 unsafe {
950 (*out).backend_data = ptr as *mut c_void;
951 }
952 NROS_RMW_RET_OK
953 }
954 #[cfg(not(all(target_os = "none", not(feature = "std"))))]
955 {
956 match box_handle("subscription", sub_handle) {
957 Ok(boxed) => {
958 unsafe {
959 (*out).backend_data = boxed;
960 }
961 NROS_RMW_RET_OK
962 }
963 Err(sub_handle) => {
966 drop(sub_handle);
967 NROS_RMW_RET_BAD_ALLOC
968 }
969 }
970 }
971 }
972 Err(e) => ret_from_error(&e),
973 }
974}
975
976unsafe extern "C" fn destroy_subscription_trampoline<R: RustBackend>(
977 subscriber: *mut NrosRmwSubscription,
978) -> NrosRmwRet {
979 let slot = unsafe { subscription_data_mut(subscriber) };
980 #[cfg(all(target_os = "none", not(feature = "std")))]
981 {
982 if unsafe {
983 static_subscriber_storage::take::<R::Subscription>(*slot as *mut R::Subscription)
984 } {
985 *slot = core::ptr::null_mut();
986 return NROS_RMW_RET_OK;
987 }
988 }
989 let _ = unsafe { take_box::<R::Subscription>(slot) };
990
991 NROS_RMW_RET_OK
992}
993
994unsafe extern "C" fn take_trampoline<R: RustBackend>(
995 subscriber: *const NrosRmwSubscription,
996 out: *mut crate::generated::rmw_mut_byte_span_t,
998 taken: *mut bool,
999) -> NrosRmwRet {
1000 let Some(span) = (unsafe { out.as_mut() }) else {
1001 return NROS_RMW_RET_INVALID_ARGUMENT;
1002 };
1003 let (buf, buf_len) = (span.data, span.capacity);
1004 let out_len: *mut usize = &mut span.len;
1005 if out_len.is_null() || taken.is_null() {
1009 return NROS_RMW_RET_INVALID_ARGUMENT;
1010 }
1011 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1012 return NROS_RMW_RET_INVALID_ARGUMENT;
1013 };
1014 if buf.is_null() && buf_len != 0 {
1015 return NROS_RMW_RET_INVALID_ARGUMENT;
1016 }
1017 let slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
1018 let key = unsafe { (*subscriber).backend_data as usize };
1019
1020 #[cfg(feature = "safety-e2e")]
1022 if crate::take_cffi_integrity_request(key) {
1023 return match Subscription::take_validated(s, slice) {
1024 Ok(Some((n, status))) => {
1025 crate::store_cffi_integrity_status(key, status);
1026 unsafe {
1027 *out_len = n;
1028 *taken = true;
1029 }
1030 NROS_RMW_RET_OK
1031 }
1032 Ok(None) => {
1033 unsafe { *taken = false };
1034 NROS_RMW_RET_OK
1035 }
1036 Err(e) => refused_take_ret(s, &e, out_len),
1037 };
1038 }
1039
1040 match Subscription::take_serialized_with_info(s, slice) {
1041 Ok(Some((n, info))) => {
1042 crate::store_cffi_message_info(key, info);
1043 unsafe {
1044 *out_len = n;
1045 *taken = true;
1046 }
1047 NROS_RMW_RET_OK
1048 }
1049 Ok(None) => {
1050 unsafe { *taken = false };
1051 NROS_RMW_RET_OK
1052 }
1053 Err(e) => refused_take_ret(s, &e, out_len),
1054 }
1055}
1056
1057fn refused_take_ret<S: Subscription<Error = TransportError>>(
1065 s: &S,
1066 e: &TransportError,
1067 out_len: *mut usize,
1068) -> NrosRmwRet {
1069 refused_ret(e, s.refused_sample_len(), out_len)
1070}
1071
1072fn refused_ret(e: &TransportError, needed: Option<usize>, out_len: *mut usize) -> NrosRmwRet {
1077 if matches!(e, TransportError::BufferTooSmall)
1078 && let Some(needed) = needed
1079 {
1080 unsafe { *out_len = needed };
1083 }
1084 ret_from_error(e)
1085}
1086
1087unsafe extern "C" fn has_data_trampoline<R: RustBackend>(
1088 subscription: *mut NrosRmwSubscription,
1089 out_has_data: *mut bool,
1090) -> NrosRmwRet {
1091 if out_has_data.is_null() {
1094 return NROS_RMW_RET_INVALID_ARGUMENT;
1095 }
1096 let Some(s) = (unsafe { subscription_ref::<R::Subscription>(subscription) }) else {
1099 return NROS_RMW_RET_INVALID_ARGUMENT;
1100 };
1101 unsafe { *out_has_data = Subscription::has_data(s) };
1103 NROS_RMW_RET_OK
1104}
1105
1106unsafe extern "C" fn subscription_supports_in_place_trampoline<R: RustBackend>(
1109 subscriber: *mut NrosRmwSubscription,
1110 out_supports: *mut bool,
1111) -> NrosRmwRet {
1112 if out_supports.is_null() {
1114 return NROS_RMW_RET_INVALID_ARGUMENT;
1115 }
1116 let Some(s) = (unsafe { subscription_ref::<R::Subscription>(subscriber) }) else {
1117 return NROS_RMW_RET_INVALID_ARGUMENT;
1118 };
1119 unsafe { *out_supports = Subscription::supports_process_in_place(s) };
1121 NROS_RMW_RET_OK
1122}
1123
1124unsafe extern "C" fn process_raw_in_place_trampoline<R: RustBackend>(
1125 subscriber: *mut NrosRmwSubscription,
1126 ctx: *mut core::ffi::c_void,
1127 cb: Option<
1128 unsafe extern "C" fn(
1129 ctx: *mut core::ffi::c_void,
1130 message: crate::generated::rmw_byte_span_t,
1131 ),
1132 >,
1133 out_processed: *mut bool,
1134) -> NrosRmwRet {
1135 if out_processed.is_null() {
1138 return NROS_RMW_RET_INVALID_ARGUMENT;
1139 }
1140 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1141 return NROS_RMW_RET_INVALID_ARGUMENT;
1142 };
1143 let Some(cb) = cb else {
1144 return NROS_RMW_RET_INVALID_ARGUMENT;
1145 };
1146 match Subscription::process_raw_in_place(s, |raw| unsafe {
1147 cb(
1148 ctx,
1149 crate::generated::rmw_byte_span_t {
1150 data: raw.as_ptr(),
1151 len: raw.len(),
1152 },
1153 )
1154 }) {
1155 Ok(processed) => {
1156 unsafe { *out_processed = processed };
1158 NROS_RMW_RET_OK
1159 }
1160 Err(e) => ret_from_error(&e),
1161 }
1162}
1163
1164unsafe extern "C" fn create_service_trampoline<R: RustBackend>(
1169 node: *const NrosRmwNode,
1170 type_support: *const crate::generated::rmw_service_type_support_t,
1173 service_name: *const core::ffi::c_char,
1174 domain_id: u32,
1175 qos: *const NrosRmwQos,
1176 out: *mut NrosRmwService,
1177) -> NrosRmwRet {
1178 let Some(ts) = (unsafe { type_support.as_ref() }) else {
1183 return NROS_RMW_RET_INVALID_ARGUMENT;
1184 };
1185 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
1186 if out.is_null() || qos.is_null() {
1187 return NROS_RMW_RET_INVALID_ARGUMENT;
1188 }
1189 if node.is_null() {
1193 return NROS_RMW_RET_INVALID_ARGUMENT;
1194 }
1195 let session = unsafe { (*node).session };
1196 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1197 return NROS_RMW_RET_INVALID_ARGUMENT;
1198 };
1199 let node_name = unsafe { node_name_of(node) };
1200 let namespace = unsafe { node_namespace_of(node) };
1201 let info = nros_rmw::ServiceInfo {
1202 name: unsafe { cstr_to_str(service_name) },
1203 type_name: unsafe { cstr_to_str(type_name) },
1204 type_hash: unsafe { cstr_to_str(type_hash) },
1205 domain_id,
1206 node_name,
1207 namespace,
1208 };
1209 let qos_settings = qos_from_cffi(unsafe { &*qos });
1210 match Session::create_service(s, &info, qos_settings) {
1211 Ok(server) => match box_handle("service server", server) {
1212 Ok(boxed) => {
1213 unsafe {
1214 (*out).backend_data = boxed;
1215 }
1216 NROS_RMW_RET_OK
1217 }
1218 Err(server) => {
1220 drop(server);
1221 NROS_RMW_RET_BAD_ALLOC
1222 }
1223 },
1224 Err(e) => ret_from_error(&e),
1225 }
1226}
1227
1228unsafe extern "C" fn destroy_service_trampoline<R: RustBackend>(
1229 server: *mut NrosRmwService,
1230) -> NrosRmwRet {
1231 let _ = unsafe { take_box::<R::Service>(service_data_mut(server)) };
1232
1233 NROS_RMW_RET_OK
1234}
1235
1236unsafe extern "C" fn take_request_trampoline<R: RustBackend>(
1237 server: *const NrosRmwService,
1238 request: *mut crate::generated::rmw_mut_byte_span_t,
1239 seq_out: *mut i64,
1240 taken: *mut bool,
1241) -> NrosRmwRet {
1242 if request.is_null() || taken.is_null() {
1245 return NROS_RMW_RET_INVALID_ARGUMENT;
1246 }
1247 let Some(s) = (unsafe { service_mut::<R::Service>(server) }) else {
1248 return NROS_RMW_RET_INVALID_ARGUMENT;
1249 };
1250 let (buf, buf_len) = unsafe { ((*request).data, (*request).capacity) };
1251 let out_len = unsafe { &raw mut (*request).len };
1252 if (buf.is_null() && buf_len != 0) || seq_out.is_null() {
1253 return NROS_RMW_RET_INVALID_ARGUMENT;
1254 }
1255 let slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
1256 let buf_start = slice.as_ptr() as usize;
1257 match ServiceTrait::take_request(s, slice) {
1258 Ok(Some(req)) => {
1259 let offset = (req.data.as_ptr() as usize).saturating_sub(buf_start);
1264 let len = req.data.len();
1265 unsafe {
1266 *seq_out = req.sequence_number;
1267 }
1268 if offset != 0 {
1272 let total = offset + len;
1273 slice.copy_within(offset..total, 0);
1276 }
1277 unsafe {
1279 *out_len = len;
1280 *taken = true;
1281 }
1282 NROS_RMW_RET_OK
1283 }
1284 Ok(None) => {
1285 unsafe { *taken = false };
1287 NROS_RMW_RET_OK
1288 }
1289 Err(e) => refused_ret(&e, ServiceTrait::refused_request_len(s), out_len),
1290 }
1291}
1292
1293unsafe extern "C" fn has_request_trampoline<R: RustBackend>(
1294 server: *mut NrosRmwService,
1295 out_has_request: *mut bool,
1296) -> NrosRmwRet {
1297 if out_has_request.is_null() {
1299 return NROS_RMW_RET_INVALID_ARGUMENT;
1300 }
1301 let Some(s) = (unsafe { service_ref::<R::Service>(server) }) else {
1302 return NROS_RMW_RET_INVALID_ARGUMENT;
1303 };
1304 unsafe { *out_has_request = ServiceTrait::has_request(s) };
1306 NROS_RMW_RET_OK
1307}
1308
1309unsafe extern "C" fn send_response_trampoline<R: RustBackend>(
1310 server: *const NrosRmwService,
1311 seq: i64,
1312 response: crate::generated::rmw_byte_span_t,
1313) -> NrosRmwRet {
1314 let Some(s) = (unsafe { service_mut::<R::Service>(server) }) else {
1315 return NROS_RMW_RET_INVALID_ARGUMENT;
1316 };
1317 let (data, len) = (response.data, response.len);
1318 if data.is_null() && len != 0 {
1319 return NROS_RMW_RET_INVALID_ARGUMENT;
1320 }
1321 let slice = unsafe { core::slice::from_raw_parts(data, len) };
1322 match ServiceTrait::send_response(s, seq, slice) {
1323 Ok(()) => NROS_RMW_RET_OK,
1324 Err(e) => ret_from_error(&e),
1325 }
1326}
1327
1328unsafe extern "C" fn create_client_trampoline<R: RustBackend>(
1333 node: *const NrosRmwNode,
1334 type_support: *const crate::generated::rmw_service_type_support_t,
1337 service_name: *const core::ffi::c_char,
1338 domain_id: u32,
1339 qos: *const NrosRmwQos,
1340 out: *mut NrosRmwClient,
1341) -> NrosRmwRet {
1342 let Some(ts) = (unsafe { type_support.as_ref() }) else {
1347 return NROS_RMW_RET_INVALID_ARGUMENT;
1348 };
1349 let (type_name, type_hash) = (ts.type_name, ts.type_hash);
1350 if out.is_null() || qos.is_null() {
1351 return NROS_RMW_RET_INVALID_ARGUMENT;
1352 }
1353 if node.is_null() {
1357 return NROS_RMW_RET_INVALID_ARGUMENT;
1358 }
1359 let session = unsafe { (*node).session };
1360 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1361 return NROS_RMW_RET_INVALID_ARGUMENT;
1362 };
1363 let node_name = unsafe { node_name_of(node) };
1364 let namespace = unsafe { node_namespace_of(node) };
1365 let info = nros_rmw::ServiceInfo {
1366 name: unsafe { cstr_to_str(service_name) },
1367 type_name: unsafe { cstr_to_str(type_name) },
1368 type_hash: unsafe { cstr_to_str(type_hash) },
1369 domain_id,
1370 node_name,
1371 namespace,
1372 };
1373 let qos_settings = qos_from_cffi(unsafe { &*qos });
1374 match Session::create_client(s, &info, qos_settings) {
1375 Ok(client) => match box_handle("service client", client) {
1376 Ok(boxed) => {
1377 unsafe {
1378 (*out).backend_data = boxed;
1379 }
1380 NROS_RMW_RET_OK
1381 }
1382 Err(client) => {
1384 drop(client);
1385 NROS_RMW_RET_BAD_ALLOC
1386 }
1387 },
1388 Err(e) => ret_from_error(&e),
1389 }
1390}
1391
1392unsafe extern "C" fn destroy_client_trampoline<R: RustBackend>(
1393 client: *mut NrosRmwClient,
1394) -> NrosRmwRet {
1395 let _ = unsafe { take_box::<R::Client>(client_data_mut(client)) };
1396
1397 NROS_RMW_RET_OK
1398}
1399
1400unsafe extern "C" fn send_request_trampoline<R: RustBackend>(
1403 client: *const NrosRmwClient,
1404 request: crate::generated::rmw_byte_span_t,
1405 sequence_id: *mut i64,
1406) -> NrosRmwRet {
1407 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1408 return NROS_RMW_RET_INVALID_ARGUMENT;
1409 };
1410 let (request, req_len) = (request.data, request.len);
1411 if request.is_null() && req_len != 0 {
1412 return NROS_RMW_RET_INVALID_ARGUMENT;
1413 }
1414 let req = unsafe { core::slice::from_raw_parts(request, req_len) };
1415 match ClientTrait::send_request_raw(c, req) {
1416 Ok(seq) => {
1417 if !sequence_id.is_null() {
1420 unsafe { *sequence_id = seq };
1422 }
1423 NROS_RMW_RET_OK
1424 }
1425 Err(e) => ret_from_error(&e),
1426 }
1427}
1428
1429unsafe extern "C" fn take_response_trampoline<R: RustBackend>(
1430 client: *const NrosRmwClient,
1431 reply: *mut crate::generated::rmw_mut_byte_span_t,
1432 seq_out: *mut i64,
1433 taken: *mut bool,
1434) -> NrosRmwRet {
1435 if reply.is_null() || taken.is_null() {
1438 return NROS_RMW_RET_INVALID_ARGUMENT;
1439 }
1440 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1441 return NROS_RMW_RET_INVALID_ARGUMENT;
1442 };
1443 let (reply_buf, reply_buf_len) = unsafe { ((*reply).data, (*reply).capacity) };
1444 let out_len = unsafe { &raw mut (*reply).len };
1445 if reply_buf.is_null() && reply_buf_len != 0 {
1446 return NROS_RMW_RET_INVALID_ARGUMENT;
1447 }
1448 let reply = unsafe { core::slice::from_raw_parts_mut(reply_buf, reply_buf_len) };
1449 match ClientTrait::take_response_raw(c, reply) {
1450 Ok(Some((n, seq))) => {
1451 unsafe {
1453 *out_len = n;
1454 *taken = true;
1455 if !seq_out.is_null() {
1456 *seq_out = seq;
1457 }
1458 }
1459 NROS_RMW_RET_OK
1460 }
1461 Ok(None) => {
1462 unsafe { *taken = false };
1464 NROS_RMW_RET_OK
1465 }
1466 Err(e) => refused_ret(&e, ClientTrait::refused_response_len(c), out_len),
1467 }
1468}
1469
1470const _: () = {
1495 use core::mem::{align_of, size_of};
1496 assert!(
1497 size_of::<crate::NrosRmwLivelinessChangedStatus>()
1498 == size_of::<nros_rmw::LivelinessChangedStatus>()
1499 );
1500 assert!(
1501 align_of::<crate::NrosRmwLivelinessChangedStatus>()
1502 == align_of::<nros_rmw::LivelinessChangedStatus>()
1503 );
1504 assert!(size_of::<crate::NrosRmwCountStatus>() == size_of::<nros_rmw::CountStatus>());
1505 assert!(align_of::<crate::NrosRmwCountStatus>() == align_of::<nros_rmw::CountStatus>());
1506 use crate::rmw_event_type_t as ck;
1509 assert!(
1510 ck::NROS_RMW_EVENT_LIVELINESS_CHANGED == nros_rmw::EventKind::LivelinessChanged as ck::Type
1511 );
1512 assert!(
1513 ck::NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED
1514 == nros_rmw::EventKind::RequestedDeadlineMissed as ck::Type
1515 );
1516 assert!(ck::NROS_RMW_EVENT_MESSAGE_LOST == nros_rmw::EventKind::MessageLost as ck::Type);
1517 assert!(ck::NROS_RMW_EVENT_LIVELINESS_LOST == nros_rmw::EventKind::LivelinessLost as ck::Type);
1518 assert!(
1519 ck::NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED
1520 == nros_rmw::EventKind::OfferedDeadlineMissed as ck::Type
1521 );
1522};
1523
1524unsafe extern "C" fn subscription_event_init_trampoline<R: RustBackend>(
1525 subscriber: *const NrosRmwSubscription,
1526 kind: NrosRmwEventKind,
1527 deadline_ms: u32,
1528 cb: NrosRmwEventCallback,
1529 user_context: *mut c_void,
1530) -> NrosRmwRet {
1531 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
1532 return NROS_RMW_RET_INVALID_ARGUMENT;
1533 };
1534 let trait_kind: nros_rmw::EventKind = event_kind_from_c(kind);
1538 let Some(cb) = cb else {
1541 return NROS_RMW_RET_INVALID_ARGUMENT;
1542 };
1543 let trait_cb: nros_rmw::EventCallback = unsafe { core::mem::transmute(cb) };
1547 let res = unsafe {
1548 Subscription::register_event_callback(s, trait_kind, deadline_ms, trait_cb, user_context)
1549 };
1550 match res {
1551 Ok(()) => NROS_RMW_RET_OK,
1552 Err(e) => {
1553 let _ = e;
1561 NROS_RMW_RET_UNSUPPORTED
1562 }
1563 }
1564}
1565
1566unsafe extern "C" fn publisher_event_init_trampoline<R: RustBackend>(
1567 publisher: *const NrosRmwPublisher,
1568 kind: NrosRmwEventKind,
1569 deadline_ms: u32,
1570 cb: NrosRmwEventCallback,
1571 user_context: *mut c_void,
1572) -> NrosRmwRet {
1573 if publisher.is_null() {
1576 return NROS_RMW_RET_INVALID_ARGUMENT;
1577 }
1578 let p_ptr = unsafe { (*publisher).backend_data } as *mut R::Publisher;
1579 if p_ptr.is_null() {
1580 return NROS_RMW_RET_INVALID_ARGUMENT;
1581 }
1582 let p = unsafe { &mut *p_ptr };
1583 let trait_kind: nros_rmw::EventKind = event_kind_from_c(kind);
1585 let Some(cb) = cb else {
1586 return NROS_RMW_RET_INVALID_ARGUMENT;
1587 };
1588 let trait_cb: nros_rmw::EventCallback = unsafe { core::mem::transmute(cb) };
1589 let res = unsafe {
1590 Publisher::register_event_callback(p, trait_kind, deadline_ms, trait_cb, user_context)
1591 };
1592 match res {
1593 Ok(()) => NROS_RMW_RET_OK,
1594 Err(_) => NROS_RMW_RET_UNSUPPORTED,
1595 }
1596}
1597
1598unsafe fn write_actual_qos(out: *mut NrosRmwQos, qos: QoSProfile) -> NrosRmwRet {
1619 let Ok(lowered) = NrosRmwQos::try_from(qos) else {
1620 return crate::NROS_RMW_RET_ERROR;
1621 };
1622 unsafe { *out = lowered };
1624 NROS_RMW_RET_OK
1625}
1626
1627unsafe extern "C" fn publisher_get_actual_qos_trampoline<R: RustBackend>(
1628 publisher: *const NrosRmwPublisher,
1629 out: *mut NrosRmwQos,
1630) -> NrosRmwRet {
1631 if out.is_null() {
1632 return NROS_RMW_RET_INVALID_ARGUMENT;
1633 }
1634 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1635 return NROS_RMW_RET_INVALID_ARGUMENT;
1636 };
1637 unsafe { write_actual_qos(out, Publisher::actual_qos(p)) }
1639}
1640
1641unsafe extern "C" fn subscription_get_actual_qos_trampoline<R: RustBackend>(
1642 subscription: *const NrosRmwSubscription,
1643 out: *mut NrosRmwQos,
1644) -> NrosRmwRet {
1645 if out.is_null() {
1646 return NROS_RMW_RET_INVALID_ARGUMENT;
1647 }
1648 let Some(sub) = (unsafe { subscription_ref::<R::Subscription>(subscription) }) else {
1649 return NROS_RMW_RET_INVALID_ARGUMENT;
1650 };
1651 unsafe { write_actual_qos(out, Subscription::actual_qos(sub)) }
1653}
1654
1655unsafe extern "C" fn client_request_publisher_get_actual_qos_trampoline<R: RustBackend>(
1656 client: *const NrosRmwClient,
1657 out: *mut NrosRmwQos,
1658) -> NrosRmwRet {
1659 if out.is_null() {
1660 return NROS_RMW_RET_INVALID_ARGUMENT;
1661 }
1662 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1663 return NROS_RMW_RET_INVALID_ARGUMENT;
1664 };
1665 unsafe { write_actual_qos(out, ClientTrait::request_publisher_actual_qos(c)) }
1667}
1668
1669unsafe extern "C" fn client_response_subscription_get_actual_qos_trampoline<R: RustBackend>(
1670 client: *const NrosRmwClient,
1671 out: *mut NrosRmwQos,
1672) -> NrosRmwRet {
1673 if out.is_null() {
1674 return NROS_RMW_RET_INVALID_ARGUMENT;
1675 }
1676 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1677 return NROS_RMW_RET_INVALID_ARGUMENT;
1678 };
1679 unsafe { write_actual_qos(out, ClientTrait::response_subscription_actual_qos(c)) }
1681}
1682
1683unsafe extern "C" fn service_request_subscription_get_actual_qos_trampoline<R: RustBackend>(
1684 service: *const NrosRmwService,
1685 out: *mut NrosRmwQos,
1686) -> NrosRmwRet {
1687 if out.is_null() {
1688 return NROS_RMW_RET_INVALID_ARGUMENT;
1689 }
1690 let Some(sv) = (unsafe { service_ref::<R::Service>(service) }) else {
1691 return NROS_RMW_RET_INVALID_ARGUMENT;
1692 };
1693 unsafe { write_actual_qos(out, ServiceTrait::request_subscription_actual_qos(sv)) }
1695}
1696
1697unsafe extern "C" fn service_response_publisher_get_actual_qos_trampoline<R: RustBackend>(
1698 service: *const NrosRmwService,
1699 out: *mut NrosRmwQos,
1700) -> NrosRmwRet {
1701 if out.is_null() {
1702 return NROS_RMW_RET_INVALID_ARGUMENT;
1703 }
1704 let Some(sv) = (unsafe { service_ref::<R::Service>(service) }) else {
1705 return NROS_RMW_RET_INVALID_ARGUMENT;
1706 };
1707 unsafe { write_actual_qos(out, ServiceTrait::response_publisher_actual_qos(sv)) }
1709}
1710
1711unsafe extern "C" fn publisher_assert_liveliness_trampoline<R: RustBackend>(
1712 publisher: *const NrosRmwPublisher,
1713) -> NrosRmwRet {
1714 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1715 return NROS_RMW_RET_INVALID_ARGUMENT;
1716 };
1717 match Publisher::assert_liveliness(p) {
1718 Ok(()) => NROS_RMW_RET_OK,
1719 Err(e) => ret_from_error(&e),
1720 }
1721}
1722
1723unsafe extern "C" fn get_gid_for_publisher_trampoline<R: RustBackend>(
1738 publisher: *const NrosRmwPublisher,
1739 gid: *mut NrosRmwGid,
1740) -> NrosRmwRet {
1741 if gid.is_null() {
1742 return NROS_RMW_RET_INVALID_ARGUMENT;
1743 }
1744 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
1745 return NROS_RMW_RET_INVALID_ARGUMENT;
1746 };
1747 match Publisher::get_gid(p) {
1748 Ok(bytes) => {
1749 unsafe {
1751 (*gid).data = bytes;
1752 }
1753 NROS_RMW_RET_OK
1754 }
1755 Err(e) => ret_from_error(&e),
1756 }
1757}
1758
1759unsafe extern "C" fn next_deadline_ms_trampoline<R: RustBackend>(
1760 session: *const NrosRmwSession,
1761 out_ms: *mut u32,
1762 has_deadline: *mut bool,
1763) -> NrosRmwRet {
1764 if out_ms.is_null() || has_deadline.is_null() {
1766 return NROS_RMW_RET_INVALID_ARGUMENT;
1767 }
1768 let Some(s) = (unsafe { session_ref::<R::Session>(session) }) else {
1771 return NROS_RMW_RET_INVALID_ARGUMENT;
1772 };
1773 match Session::next_deadline_ms(s) {
1774 Some(ms) => {
1775 unsafe {
1777 *out_ms = ms;
1778 *has_deadline = true;
1779 }
1780 }
1781 None => {
1782 unsafe { *has_deadline = false };
1784 }
1785 }
1786 NROS_RMW_RET_OK
1787}
1788
1789unsafe extern "C" fn set_wake_callback_trampoline<R: RustBackend>(
1790 session: *mut NrosRmwSession,
1791 cb: Option<unsafe extern "C" fn(ctx: *mut core::ffi::c_void)>,
1792 ctx: *mut core::ffi::c_void,
1793) -> NrosRmwRet {
1794 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1795 return NROS_RMW_RET_INVALID_ARGUMENT;
1796 };
1797 unsafe { Session::set_wake_callback(s, cb, ctx) };
1802 NROS_RMW_RET_OK
1803}
1804
1805unsafe extern "C" fn node_get_graph_guard_condition_trampoline<R: RustBackend>(
1814 session: *mut NrosRmwSession,
1815 callback: crate::generated::rmw_event_callback_t,
1816 user_data: *const core::ffi::c_void,
1817) -> NrosRmwRet {
1818 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1819 return NROS_RMW_RET_INVALID_ARGUMENT;
1820 };
1821 match unsafe { Session::set_graph_change_callback(s, callback, user_data) } {
1824 Ok(()) => NROS_RMW_RET_OK,
1825 Err(e) => ret_from_error(&e),
1826 }
1827}
1828
1829unsafe extern "C" fn service_server_is_available_trampoline<R: RustBackend>(
1830 client: *const NrosRmwClient,
1831 out_available: *mut bool,
1832) -> NrosRmwRet {
1833 if out_available.is_null() {
1844 return NROS_RMW_RET_INVALID_ARGUMENT;
1845 }
1846 let Some(c) = (unsafe { client_mut::<R::Client>(client) }) else {
1847 return NROS_RMW_RET_INVALID_ARGUMENT;
1848 };
1849 match ClientTrait::service_is_ready(c) {
1850 Ok(available) => {
1851 unsafe { *out_available = available };
1853 NROS_RMW_RET_OK
1854 }
1855 Err(e) => ret_from_error(&e),
1856 }
1857}
1858
1859unsafe extern "C" fn ping_session_trampoline<R: RustBackend>(
1860 session: *mut NrosRmwSession,
1861 timeout_ms: i32,
1862) -> NrosRmwRet {
1863 let Some(s) = (unsafe { session_mut::<R::Session>(session) }) else {
1868 return NROS_RMW_RET_INVALID_ARGUMENT;
1869 };
1870 match Session::ping_session(s, timeout_ms) {
1871 Ok(()) => NROS_RMW_RET_OK,
1872 Err(e) => ret_from_error(&e),
1873 }
1874}
1875
1876unsafe extern "C" fn get_node_names_trampoline<R: RustBackend>(
1886 session: *const NrosRmwSession,
1887 visitor: crate::generated::rmw_node_visitor_t,
1890) -> NrosRmwRet {
1891 let (visit, ctx) = (visitor.visit, visitor.ctx);
1892 let Some(visit) = visit else {
1893 return NROS_RMW_RET_INVALID_ARGUMENT;
1894 };
1895 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
1896 return NROS_RMW_RET_INVALID_ARGUMENT;
1897 };
1898
1899 const NAME_MAX: usize = 256;
1901
1902 let mut cb = |name: &str, namespace: &str, enclave: Option<&str>| -> bool {
1903 let mut name_buf = [0u8; NAME_MAX];
1904 let mut ns_buf = [0u8; NAME_MAX];
1905 let mut enc_buf = [0u8; NAME_MAX];
1906 if name.len() >= NAME_MAX || namespace.len() >= NAME_MAX {
1910 return true; }
1912 name_buf[..name.len()].copy_from_slice(name.as_bytes());
1913 ns_buf[..namespace.len()].copy_from_slice(namespace.as_bytes());
1914 let enc_ptr = match enclave {
1915 Some(e) if e.len() < NAME_MAX => {
1916 enc_buf[..e.len()].copy_from_slice(e.as_bytes());
1917 enc_buf.as_ptr() as *const c_char
1918 }
1919 _ => core::ptr::null(),
1923 };
1924 unsafe {
1925 visit(
1926 ctx,
1927 name_buf.as_ptr() as *const c_char,
1928 ns_buf.as_ptr() as *const c_char,
1929 enc_ptr,
1930 )
1931 }
1932 };
1933
1934 match Session::get_node_names(s, &mut cb) {
1935 Ok(()) => NROS_RMW_RET_OK,
1936 Err(e) => ret_from_error(&e),
1937 }
1938}
1939
1940macro_rules! count_trampoline {
1942 ($name:ident, $method:ident) => {
1943 unsafe extern "C" fn $name<R: RustBackend>(
1944 session: *const NrosRmwSession,
1945 topic_name: *const c_char,
1946 count: *mut usize,
1947 ) -> NrosRmwRet {
1948 if topic_name.is_null() || count.is_null() {
1949 return NROS_RMW_RET_INVALID_ARGUMENT;
1950 }
1951 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
1952 return NROS_RMW_RET_INVALID_ARGUMENT;
1953 };
1954 let Ok(topic) = (unsafe { core::ffi::CStr::from_ptr(topic_name) }).to_str() else {
1955 return NROS_RMW_RET_INVALID_ARGUMENT;
1956 };
1957 match Session::$method(s, topic) {
1958 Ok(n) => {
1959 unsafe { *count = n };
1960 NROS_RMW_RET_OK
1961 }
1962 Err(e) => ret_from_error(&e),
1963 }
1964 }
1965 };
1966}
1967
1968count_trampoline!(count_publishers_trampoline, count_publishers);
1969count_trampoline!(count_subscribers_trampoline, count_subscribers);
1970
1971macro_rules! names_and_types_trampoline {
1981 ($name:ident, $method:ident, demangle_flag) => {
1984 unsafe extern "C" fn $name<R: RustBackend>(
1985 session: *const NrosRmwSession,
1986 no_demangle: bool,
1987 visitor: crate::generated::rmw_names_and_types_visitor_t,
1989 ) -> NrosRmwRet {
1990 if no_demangle {
1995 return NROS_RMW_RET_UNSUPPORTED;
1996 }
1997 $crate::names_and_types_body!(R, $method, session, visitor)
1998 }
1999 };
2000 ($name:ident, $method:ident) => {
2001 unsafe extern "C" fn $name<R: RustBackend>(
2002 session: *const NrosRmwSession,
2003 visitor: crate::generated::rmw_names_and_types_visitor_t,
2005 ) -> NrosRmwRet {
2006 $crate::names_and_types_body!(R, $method, session, visitor)
2007 }
2008 };
2009}
2010
2011#[macro_export]
2013#[doc(hidden)]
2014macro_rules! names_and_types_body {
2015 ($R:ident, $method:ident, $session:ident, $visitor:ident) => {{
2016 let (visit_fn, ctx) = ($visitor.visit, $visitor.ctx);
2019 let Some(visit) = visit_fn else {
2020 return NROS_RMW_RET_INVALID_ARGUMENT;
2021 };
2022 let Some(s) = (unsafe { session_mut::<$R::Session>($session.cast_mut()) }) else {
2023 return NROS_RMW_RET_INVALID_ARGUMENT;
2024 };
2025
2026 const NAME_MAX: usize = 256;
2027 const TYPES_MAX: usize = 8;
2028
2029 let mut cb = |name: &str, types: &[&str]| -> bool {
2030 let mut name_buf = [0u8; NAME_MAX];
2031 if name.len() >= NAME_MAX {
2032 return true; }
2034 name_buf[..name.len()].copy_from_slice(name.as_bytes());
2035
2036 let mut type_bufs = [[0u8; NAME_MAX]; TYPES_MAX];
2037 let mut ptrs: [*const c_char; TYPES_MAX] = [core::ptr::null(); TYPES_MAX];
2038 let mut n = 0usize;
2039 for t in types.iter().take(TYPES_MAX) {
2040 if t.len() >= NAME_MAX {
2041 continue;
2042 }
2043 type_bufs[n][..t.len()].copy_from_slice(t.as_bytes());
2044 ptrs[n] = type_bufs[n].as_ptr() as *const c_char;
2045 n += 1;
2046 }
2047 unsafe { visit(ctx, name_buf.as_ptr() as *const c_char, ptrs.as_ptr(), n) }
2048 };
2049
2050 match Session::$method(s, &mut cb) {
2051 Ok(()) => NROS_RMW_RET_OK,
2052 Err(e) => ret_from_error(&e),
2053 }
2054 }};
2055}
2056
2057names_and_types_trampoline!(
2058 get_topic_names_and_types_trampoline,
2059 get_topic_names_and_types,
2060 demangle_flag
2061);
2062names_and_types_trampoline!(
2063 get_service_names_and_types_trampoline,
2064 get_service_names_and_types
2065);
2066
2067unsafe fn by_node_impl<R: RustBackend>(
2076 session: *const NrosRmwSession,
2077 node_name: *const c_char,
2078 node_namespace: *const c_char,
2079 no_demangle: bool,
2080 visitor: crate::generated::rmw_names_and_types_visitor_t,
2082 kind: nros_rmw::GraphEntityKind,
2083) -> NrosRmwRet {
2084 let (visit, ctx) = (visitor.visit, visitor.ctx);
2085 if no_demangle {
2089 return NROS_RMW_RET_UNSUPPORTED;
2090 }
2091 if node_name.is_null() || node_namespace.is_null() {
2092 return NROS_RMW_RET_INVALID_ARGUMENT;
2093 }
2094 let Some(visit) = visit else {
2095 return NROS_RMW_RET_INVALID_ARGUMENT;
2096 };
2097 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
2098 return NROS_RMW_RET_INVALID_ARGUMENT;
2099 };
2100 let (Ok(name), Ok(ns)) = (
2101 unsafe { core::ffi::CStr::from_ptr(node_name) }.to_str(),
2102 unsafe { core::ffi::CStr::from_ptr(node_namespace) }.to_str(),
2103 ) else {
2104 return NROS_RMW_RET_INVALID_ARGUMENT;
2105 };
2106
2107 const NAME_MAX: usize = 256;
2108 const TYPES_MAX: usize = 8;
2109 let mut cb = |n: &str, types: &[&str]| -> bool {
2110 let mut name_buf = [0u8; NAME_MAX];
2111 if n.len() >= NAME_MAX {
2112 return true;
2113 }
2114 name_buf[..n.len()].copy_from_slice(n.as_bytes());
2115 let mut type_bufs = [[0u8; NAME_MAX]; TYPES_MAX];
2116 let mut ptrs: [*const c_char; TYPES_MAX] = [core::ptr::null(); TYPES_MAX];
2117 let mut count = 0usize;
2118 for t in types.iter().take(TYPES_MAX) {
2119 if t.len() >= NAME_MAX {
2120 continue;
2121 }
2122 type_bufs[count][..t.len()].copy_from_slice(t.as_bytes());
2123 ptrs[count] = type_bufs[count].as_ptr() as *const c_char;
2124 count += 1;
2125 }
2126 unsafe {
2127 visit(
2128 ctx,
2129 name_buf.as_ptr() as *const c_char,
2130 ptrs.as_ptr(),
2131 count,
2132 )
2133 }
2134 };
2135 match Session::get_names_and_types_by_node(s, kind, name, ns, &mut cb) {
2136 Ok(()) => NROS_RMW_RET_OK,
2137 Err(e) => ret_from_error(&e),
2138 }
2139}
2140
2141macro_rules! by_node_trampoline {
2142 ($name:ident, $kind:ident, no_demangle) => {
2147 unsafe extern "C" fn $name<R: RustBackend>(
2148 session: *const NrosRmwSession,
2149 node_name: *const c_char,
2150 node_namespace: *const c_char,
2151 no_demangle: bool,
2152 visitor: crate::generated::rmw_names_and_types_visitor_t,
2154 ) -> NrosRmwRet {
2155 unsafe {
2156 by_node_impl::<R>(
2157 session,
2158 node_name,
2159 node_namespace,
2160 no_demangle,
2161 visitor,
2162 nros_rmw::GraphEntityKind::$kind,
2163 )
2164 }
2165 }
2166 };
2167 ($name:ident, $kind:ident) => {
2169 unsafe extern "C" fn $name<R: RustBackend>(
2170 session: *const NrosRmwSession,
2171 node_name: *const c_char,
2172 node_namespace: *const c_char,
2173 visitor: crate::generated::rmw_names_and_types_visitor_t,
2175 ) -> NrosRmwRet {
2176 unsafe {
2177 by_node_impl::<R>(
2178 session,
2179 node_name,
2180 node_namespace,
2181 false,
2182 visitor,
2183 nros_rmw::GraphEntityKind::$kind,
2184 )
2185 }
2186 }
2187 };
2188}
2189
2190by_node_trampoline!(
2191 get_publisher_names_and_types_by_node_trampoline,
2192 Publisher,
2193 no_demangle
2194);
2195by_node_trampoline!(
2196 get_subscriber_names_and_types_by_node_trampoline,
2197 Subscriber,
2198 no_demangle
2199);
2200by_node_trampoline!(get_service_names_and_types_by_node_trampoline, Service);
2201by_node_trampoline!(get_client_names_and_types_by_node_trampoline, Client);
2202
2203unsafe fn endpoint_info_impl<R: RustBackend>(
2208 session: *const NrosRmwSession,
2209 topic_name: *const c_char,
2210 no_mangle: bool,
2211 visitor: crate::generated::rmw_topic_endpoint_info_visitor_t,
2213 publishers: bool,
2214) -> NrosRmwRet {
2215 let (visit, ctx) = (visitor.visit, visitor.ctx);
2216 if no_mangle {
2217 return NROS_RMW_RET_UNSUPPORTED;
2218 }
2219 if topic_name.is_null() {
2220 return NROS_RMW_RET_INVALID_ARGUMENT;
2221 }
2222 let Some(visit) = visit else {
2223 return NROS_RMW_RET_INVALID_ARGUMENT;
2224 };
2225 let Some(s) = (unsafe { session_mut::<R::Session>(session.cast_mut()) }) else {
2226 return NROS_RMW_RET_INVALID_ARGUMENT;
2227 };
2228 let Ok(topic) = (unsafe { core::ffi::CStr::from_ptr(topic_name) }).to_str() else {
2229 return NROS_RMW_RET_INVALID_ARGUMENT;
2230 };
2231
2232 const NAME_MAX: usize = 256;
2233 let mut cb = |info: &nros_rmw::GraphEndpointInfo<'_>| -> bool {
2234 let mut name_buf = [0u8; NAME_MAX];
2235 let mut ns_buf = [0u8; NAME_MAX];
2236 let mut ty_buf = [0u8; NAME_MAX];
2237 if info.node_name.len() >= NAME_MAX
2238 || info.node_namespace.len() >= NAME_MAX
2239 || info.topic_type.len() >= NAME_MAX
2240 {
2241 return true; }
2243 name_buf[..info.node_name.len()].copy_from_slice(info.node_name.as_bytes());
2244 ns_buf[..info.node_namespace.len()].copy_from_slice(info.node_namespace.as_bytes());
2245 ty_buf[..info.topic_type.len()].copy_from_slice(info.topic_type.as_bytes());
2246 let mut qos: rmw_qos_profile_t = unsafe { core::mem::zeroed() };
2258 qos.reliability = NROS_RMW_RELIABILITY_UNKNOWN as u8;
2259 qos.durability = NROS_RMW_DURABILITY_UNKNOWN as u8;
2260 qos.history = NROS_RMW_HISTORY_UNKNOWN as u8;
2261 qos.liveliness_kind = rmw_liveliness_kind_t::NROS_RMW_LIVELINESS_UNKNOWN as u8;
2262 let c_info = rmw_topic_endpoint_info_t {
2263 node_name: name_buf.as_ptr() as *const c_char,
2264 node_namespace: ns_buf.as_ptr() as *const c_char,
2265 topic_type: ty_buf.as_ptr() as *const c_char,
2266 endpoint_type: if info.is_publisher {
2267 rmw_endpoint_type_t::RMW_ENDPOINT_PUBLISHER
2268 } else {
2269 rmw_endpoint_type_t::RMW_ENDPOINT_SUBSCRIPTION
2270 },
2271 endpoint_gid: rmw_gid_t {
2272 implementation_identifier: core::ptr::null(),
2273 data: info.endpoint_gid,
2274 },
2275 qos_profile: qos,
2276 };
2277 unsafe { visit(ctx, &c_info as *const rmw_topic_endpoint_info_t) }
2278 };
2279 match Session::get_endpoint_info_by_topic(s, publishers, topic, &mut cb) {
2280 Ok(()) => NROS_RMW_RET_OK,
2281 Err(e) => ret_from_error(&e),
2282 }
2283}
2284
2285macro_rules! endpoint_info_trampoline {
2286 ($name:ident, $publishers:expr) => {
2287 unsafe extern "C" fn $name<R: RustBackend>(
2288 session: *const NrosRmwSession,
2289 topic_name: *const c_char,
2290 no_mangle: bool,
2291 visitor: crate::generated::rmw_topic_endpoint_info_visitor_t,
2293 ) -> NrosRmwRet {
2294 unsafe { endpoint_info_impl::<R>(session, topic_name, no_mangle, visitor, $publishers) }
2295 }
2296 };
2297}
2298
2299endpoint_info_trampoline!(get_publishers_info_by_topic_trampoline, true);
2300endpoint_info_trampoline!(get_subscriptions_info_by_topic_trampoline, false);
2301
2302unsafe extern "C" fn publish_streamed_trampoline<R: RustBackend>(
2303 publisher: *mut NrosRmwPublisher,
2304 size_cb: Option<unsafe extern "C" fn(out_total_len: *mut usize, user_ctx: *mut c_void)>,
2305 chunk_cb: Option<
2306 unsafe extern "C" fn(
2307 out_buf: *mut u8,
2308 cap: usize,
2309 out_written: *mut usize,
2310 user_ctx: *mut c_void,
2311 ),
2312 >,
2313 user_ctx: *mut c_void,
2314) -> NrosRmwRet {
2315 let Some(p) = (unsafe { publisher_ref::<R::Publisher>(publisher) }) else {
2321 return NROS_RMW_RET_INVALID_ARGUMENT;
2322 };
2323 let (Some(size_cb), Some(chunk_cb)) = (size_cb, chunk_cb) else {
2325 return NROS_RMW_RET_INVALID_ARGUMENT;
2326 };
2327 match unsafe { Publisher::publish_streamed(p, size_cb, chunk_cb, user_ctx) } {
2330 Ok(()) => NROS_RMW_RET_OK,
2331 Err(e) => ret_from_error(&e),
2332 }
2333}
2334
2335unsafe extern "C" fn take_sequence_trampoline<R: RustBackend>(
2336 subscriber: *const NrosRmwSubscription,
2337 buf: *mut u8,
2338 per_msg_cap: usize,
2339 max_msgs: usize,
2340 out_lens: *mut usize,
2341 taken: *mut usize,
2342) -> NrosRmwRet {
2343 if taken.is_null() {
2346 return NROS_RMW_RET_INVALID_ARGUMENT;
2347 }
2348 let Some(s) = (unsafe { subscription_mut::<R::Subscription>(subscriber) }) else {
2353 return NROS_RMW_RET_INVALID_ARGUMENT;
2354 };
2355 if buf.is_null() || out_lens.is_null() || per_msg_cap == 0 {
2356 return NROS_RMW_RET_INVALID_ARGUMENT;
2357 }
2358 let buf_slice =
2363 unsafe { core::slice::from_raw_parts_mut(buf, max_msgs.saturating_mul(per_msg_cap)) };
2364 let lens_slice = unsafe { core::slice::from_raw_parts_mut(out_lens, max_msgs) };
2365 match Subscription::take_sequence(s, buf_slice, per_msg_cap, max_msgs, lens_slice) {
2366 Ok(count) => {
2367 unsafe { *taken = count };
2369 NROS_RMW_RET_OK
2370 }
2371 Err(e) => ret_from_error(&e),
2372 }
2373}
2374
2375#[inline]
2393unsafe fn session_data_mut(session: *mut NrosRmwSession) -> &'static mut *mut c_void {
2394 unsafe { &mut (*session).backend_data }
2395}
2396
2397#[inline]
2398unsafe fn publisher_data_mut(publisher: *mut NrosRmwPublisher) -> &'static mut *mut c_void {
2399 unsafe { &mut (*publisher).backend_data }
2400}
2401
2402#[inline]
2403unsafe fn subscription_data_mut(subscriber: *mut NrosRmwSubscription) -> &'static mut *mut c_void {
2404 unsafe { &mut (*subscriber).backend_data }
2405}
2406
2407#[inline]
2408unsafe fn service_data_mut(server: *mut NrosRmwService) -> &'static mut *mut c_void {
2409 unsafe { &mut (*server).backend_data }
2410}
2411
2412#[inline]
2413unsafe fn client_data_mut(client: *mut NrosRmwClient) -> &'static mut *mut c_void {
2414 unsafe { &mut (*client).backend_data }
2415}
2416
2417#[inline]
2418unsafe fn session_mut<'a, T>(session: *mut NrosRmwSession) -> Option<&'a mut T> {
2419 if session.is_null() {
2420 return None;
2421 }
2422 let p = unsafe { (*session).backend_data } as *mut T;
2423 if p.is_null() {
2424 None
2425 } else {
2426 Some(unsafe { &mut *p })
2427 }
2428}
2429
2430#[inline]
2431unsafe fn session_ref<'a, T>(session: *const NrosRmwSession) -> Option<&'a T> {
2432 if session.is_null() {
2433 return None;
2434 }
2435 let p = unsafe { (*session).backend_data } as *const T;
2436 if p.is_null() {
2437 None
2438 } else {
2439 Some(unsafe { &*p })
2440 }
2441}
2442
2443#[inline]
2444unsafe fn publisher_ref<'a, T>(publisher: *const NrosRmwPublisher) -> Option<&'a T> {
2445 if publisher.is_null() {
2446 return None;
2447 }
2448 let p = unsafe { (*publisher).backend_data } as *const T;
2449 if p.is_null() {
2450 None
2451 } else {
2452 Some(unsafe { &*p })
2453 }
2454}
2455
2456#[inline]
2457unsafe fn subscription_mut<'a, T>(subscriber: *const NrosRmwSubscription) -> Option<&'a mut T> {
2458 if subscriber.is_null() {
2459 return None;
2460 }
2461 let p = unsafe { (*subscriber).backend_data } as *mut T;
2462 if p.is_null() {
2463 None
2464 } else {
2465 Some(unsafe { &mut *p })
2466 }
2467}
2468
2469#[inline]
2470unsafe fn subscription_ref<'a, T>(subscriber: *const NrosRmwSubscription) -> Option<&'a T> {
2471 if subscriber.is_null() {
2472 return None;
2473 }
2474 let p = unsafe { (*subscriber).backend_data } as *const T;
2475 if p.is_null() {
2476 None
2477 } else {
2478 Some(unsafe { &*p })
2479 }
2480}
2481
2482#[inline]
2483unsafe fn service_mut<'a, T>(server: *const NrosRmwService) -> Option<&'a mut T> {
2484 if server.is_null() {
2485 return None;
2486 }
2487 let p = unsafe { (*server).backend_data } as *mut T;
2488 if p.is_null() {
2489 None
2490 } else {
2491 Some(unsafe { &mut *p })
2492 }
2493}
2494
2495#[inline]
2496unsafe fn service_ref<'a, T>(server: *const NrosRmwService) -> Option<&'a T> {
2497 if server.is_null() {
2498 return None;
2499 }
2500 let p = unsafe { (*server).backend_data } as *const T;
2501 if p.is_null() {
2502 None
2503 } else {
2504 Some(unsafe { &*p })
2505 }
2506}
2507
2508#[inline]
2509unsafe fn client_mut<'a, T>(client: *const NrosRmwClient) -> Option<&'a mut T> {
2510 if client.is_null() {
2511 return None;
2512 }
2513 let p = unsafe { (*client).backend_data } as *mut T;
2514 if p.is_null() {
2515 None
2516 } else {
2517 Some(unsafe { &mut *p })
2518 }
2519}
2520
2521fn box_handle<T>(kind: &str, value: T) -> Result<*mut c_void, T> {
2538 match nros_rmw::fallible::try_box(value) {
2539 Ok(boxed) => Ok(Box::into_raw(boxed) as *mut c_void),
2540 Err(value) => {
2541 nros_log::log_error!(
2542 nros_log::get_logger("nros_rmw_cffi"),
2543 "heap exhausted: could not allocate {} bytes for a {} handle; \
2544 the {} is NOT created (NROS_RMW_RET_BAD_ALLOC). Raise the \
2545 platform heap (Zephyr: CONFIG_NROS_ZEPHYR_HEAP_SIZE)",
2546 core::mem::size_of::<T>(),
2547 kind,
2548 kind
2549 );
2550 Err(value)
2551 }
2552 }
2553}
2554
2555#[inline]
2556unsafe fn take_box<T>(slot: &mut *mut c_void) -> Option<Box<T>> {
2557 if slot.is_null() {
2558 return None;
2559 }
2560 let ptr = *slot as *mut T;
2561 *slot = core::ptr::null_mut();
2562 if ptr.is_null() {
2563 None
2564 } else {
2565 Some(unsafe { Box::from_raw(ptr) })
2570 }
2571}
2572
2573#[cfg(test)]
2574mod tests {
2575 use super::*;
2576
2577 #[test]
2585 fn a_zero_filled_c_profile_reads_back_as_the_sentinel() {
2586 let raised = qos_from_cffi(&crate::NROS_RMW_QOS_PROFILE_SYSTEM_DEFAULT);
2587 assert_eq!(raised, nros_rmw::QoSProfile::QOS_PROFILE_SYSTEM_DEFAULT);
2588 assert!(raised.has_unresolved_system_default());
2589 }
2590
2591 #[test]
2595 fn unknown_is_not_raised_to_the_sentinel() {
2596 let mut q = crate::NROS_RMW_QOS_PROFILE_SYSTEM_DEFAULT;
2597 q.reliability = crate::generated::NROS_RMW_RELIABILITY_UNKNOWN as u8;
2598 let raised = qos_from_cffi(&q);
2599 assert_eq!(
2600 raised.reliability,
2601 nros_rmw::QoSReliabilityPolicy::Reliable,
2602 "UNKNOWN must keep folding to the ROS default, not become a sentinel"
2603 );
2604 }
2605}