1mod info;
14mod listener;
15mod logging;
16mod pubsub;
17mod qos;
18#[cfg(feature = "rmw")]
19mod rmw;
20mod telemetry;
21mod waitset;
22
23pub use info::*;
25pub use listener::*;
26pub use logging::*;
27pub use pubsub::*;
28pub use telemetry::*;
29
30pub use qos::HddsQoS;
32
33use std::collections::HashMap;
34use std::convert::TryFrom;
35use std::ffi::CStr;
36use std::os::raw::{c_char, c_void};
37use std::ptr;
38use std::sync::{Arc, Mutex, OnceLock};
39use std::time::Duration;
40use waitset::ForeignWaitSet;
41
42use hdds::api::{DataReader, DataWriter, GuardCondition, Participant, QoS, StatusCondition, DDS};
43use hdds::core::types::{Distro, TypeDescriptor, TypeObjectHandle, ROS_HASH_SIZE};
44use hdds::dds::Condition;
45use serde::{Deserialize, Serialize};
46
47#[cfg(feature = "xtypes")]
49use hdds::xtypes::{rosidl_message_type_support_t, RosidlError};
50
51#[cfg(feature = "rmw")]
53use hdds::api::Error as ApiError;
54#[cfg(feature = "rmw")]
55use rmw::{
56 decode_special, deserialize_dynamic_to_ros, encode_special, map_api_error,
57 ros2_type_to_descriptor, serialize_from_ros, ForeignRmwContext, ForeignRmwWaitSet,
58 Ros2CodecKind,
59};
60#[cfg(feature = "rmw")]
61use std::ffi::CString;
62#[cfg(feature = "rmw")]
63use std::slice;
64
65#[cfg(feature = "rmw")]
66type HddsNodeVisitor = Option<unsafe extern "C" fn(*const c_char, *const c_char, *mut c_void)>;
67#[cfg(feature = "rmw")]
68type HddsNodeEnclaveVisitor =
69 Option<unsafe extern "C" fn(*const c_char, *const c_char, *const c_char, *mut c_void)>;
70#[cfg(feature = "rmw")]
71type HddsEndpointVisitor = Option<
72 unsafe extern "C" fn(
73 *const c_char,
74 *const c_char,
75 *const u8,
76 *const HddsRmwQosProfile,
77 *mut c_void,
78 ),
79>;
80
81#[cfg(feature = "rmw")]
82fn normalize_ros_type_name(type_name: &str) -> String {
83 if type_name.contains("::") {
84 type_name.replace("::", "/")
85 } else {
86 type_name.to_string()
87 }
88}
89
90#[repr(C)]
92pub struct HddsParticipant {
93 _private: [u8; 0],
94}
95
96#[repr(C)]
98pub struct HddsDataWriter {
99 _private: [u8; 0],
100}
101
102#[repr(C)]
104pub struct HddsDataReader {
105 _private: [u8; 0],
106}
107
108#[repr(C)]
110pub struct HddsWaitSet {
111 _private: [u8; 0],
112}
113
114#[repr(C)]
116pub struct HddsGuardCondition {
117 _private: [u8; 0],
118}
119
120#[repr(C)]
122pub struct HddsStatusCondition {
123 _private: [u8; 0],
124}
125
126#[cfg(feature = "rmw")]
128#[repr(C)]
129pub struct HddsRmwContext {
130 _private: [u8; 0],
131}
132
133#[cfg(feature = "rmw")]
134pub const HDDS_RMW_GID_SIZE: usize = hdds::rmw::graph::RMW_GID_STORAGE_SIZE;
135
136#[cfg(feature = "rmw")]
137#[repr(C)]
138#[derive(Clone, Copy, Debug, Default)]
139pub struct HddsRmwQosProfile {
140 pub history: u8,
141 pub depth: u32,
142 pub reliability: u8,
143 pub durability: u8,
144 pub deadline_ns: u64,
145 pub lifespan_ns: u64,
146 pub liveliness: u8,
147 pub liveliness_lease_ns: u64,
148 pub avoid_ros_namespace_conventions: bool,
149}
150
151#[cfg(feature = "rmw")]
153#[repr(C)]
154pub struct HddsRmwWaitSet {
155 _private: [u8; 0],
156}
157
158#[cfg(feature = "rmw")]
159pub type HddsTopicVisitor =
160 unsafe extern "C" fn(*const c_char, *const c_char, u32, u32, *mut c_void);
161
162#[cfg(feature = "rmw")]
163pub type HddsLocatorVisitor = unsafe extern "C" fn(*const c_char, u16, *mut c_void);
164
165#[cfg(feature = "xtypes")]
166#[repr(C)]
167pub struct HddsTypeObject {
168 _private: [u8; 0],
169}
170
171struct GuardRegistryEntry {
172 guard: Arc<GuardCondition>,
173 handles: usize,
174}
175
176struct StatusRegistryEntry {
177 status: Arc<StatusCondition>,
178 handles: usize,
179}
180
181fn guard_registry() -> &'static Mutex<HashMap<usize, GuardRegistryEntry>> {
182 static REGISTRY: OnceLock<Mutex<HashMap<usize, GuardRegistryEntry>>> = OnceLock::new();
183 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
184}
185
186fn status_registry() -> &'static Mutex<HashMap<usize, StatusRegistryEntry>> {
187 static REGISTRY: OnceLock<Mutex<HashMap<usize, StatusRegistryEntry>>> = OnceLock::new();
188 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
189}
190
191fn guard_registry_add_handle(raw: *const HddsGuardCondition, guard: Arc<GuardCondition>) {
192 if raw.is_null() {
193 return;
194 }
195
196 let mut registry = guard_registry()
197 .lock()
198 .unwrap_or_else(|err| err.into_inner());
199 let entry = registry
200 .entry(raw as usize)
201 .or_insert_with(|| GuardRegistryEntry { guard, handles: 0 });
202 entry.handles = entry.handles.saturating_add(1);
203}
204
205fn guard_registry_clone(raw: *const HddsGuardCondition) -> Option<Arc<GuardCondition>> {
206 if raw.is_null() {
207 return None;
208 }
209
210 let registry = guard_registry()
211 .lock()
212 .unwrap_or_else(|err| err.into_inner());
213 registry
214 .get(&(raw as usize))
215 .map(|entry| entry.guard.clone())
216}
217
218fn guard_registry_release(raw: *const HddsGuardCondition) -> bool {
219 if raw.is_null() {
220 return false;
221 }
222
223 let mut registry = guard_registry()
224 .lock()
225 .unwrap_or_else(|err| err.into_inner());
226 let Some(entry) = registry.get_mut(&(raw as usize)) else {
227 return false;
228 };
229
230 if entry.handles == 0 {
231 return false;
232 }
233
234 entry.handles -= 1;
235 if entry.handles == 0 {
236 registry.remove(&(raw as usize));
237 }
238
239 true
240}
241
242fn status_registry_add_handle(raw: *const HddsStatusCondition, status: Arc<StatusCondition>) {
243 if raw.is_null() {
244 return;
245 }
246
247 let mut registry = status_registry()
248 .lock()
249 .unwrap_or_else(|err| err.into_inner());
250 let entry = registry
251 .entry(raw as usize)
252 .or_insert_with(|| StatusRegistryEntry { status, handles: 0 });
253 entry.handles = entry.handles.saturating_add(1);
254}
255
256fn status_registry_clone(raw: *const HddsStatusCondition) -> Option<Arc<StatusCondition>> {
257 if raw.is_null() {
258 return None;
259 }
260
261 let registry = status_registry()
262 .lock()
263 .unwrap_or_else(|err| err.into_inner());
264 registry
265 .get(&(raw as usize))
266 .map(|entry| entry.status.clone())
267}
268
269fn status_registry_release(raw: *const HddsStatusCondition) -> bool {
270 if raw.is_null() {
271 return false;
272 }
273
274 let mut registry = status_registry()
275 .lock()
276 .unwrap_or_else(|err| err.into_inner());
277 let Some(entry) = registry.get_mut(&(raw as usize)) else {
278 return false;
279 };
280
281 if entry.handles == 0 {
282 return false;
283 }
284
285 entry.handles -= 1;
286 if entry.handles == 0 {
287 registry.remove(&(raw as usize));
288 }
289
290 true
291}
292
293#[cfg(feature = "xtypes")]
294fn distro_from_u32(value: u32) -> Option<Distro> {
295 match value {
296 0 => Some(Distro::Humble),
297 1 => Some(Distro::Iron),
298 2 => Some(Distro::Jazzy),
299 _ => None,
300 }
301}
302
303#[cfg(feature = "xtypes")]
304fn map_rosidl_error(err: &RosidlError) -> HddsError {
305 match err {
306 RosidlError::NullTypeSupport
307 | RosidlError::NullMembers
308 | RosidlError::InvalidUtf8(_)
309 | RosidlError::BoundOverflow { .. }
310 | RosidlError::UnsupportedType(_) => HddsError::HddsInvalidArgument,
311 RosidlError::MissingHash | RosidlError::Builder(_) => HddsError::HddsOperationFailed,
312 }
313}
314
315#[derive(Serialize, Deserialize, Clone)]
317pub(crate) struct BytePayload {
318 data: Vec<u8>,
319}
320
321impl BytePayload {
323 #[cfg(test)]
324 #[allow(dead_code)]
325 pub(crate) fn as_slice(&self) -> &[u8] {
326 &self.data
327 }
328}
329
330impl DDS for BytePayload {
331 fn type_descriptor() -> &'static TypeDescriptor {
332 static DESCRIPTOR: TypeDescriptor = TypeDescriptor {
336 type_id: 0x0000_0000, type_name: "RawBytes",
338 size_bytes: 0, alignment: 1, is_variable_size: true,
341 fields: &[],
342 };
343 &DESCRIPTOR
344 }
345
346 fn encode_cdr2(&self, buf: &mut [u8]) -> hdds::api::Result<usize> {
347 if buf.len() < self.data.len() {
349 return Err(hdds::api::Error::BufferTooSmall);
350 }
351
352 buf[..self.data.len()].copy_from_slice(&self.data);
353 Ok(self.data.len())
354 }
355
356 fn decode_cdr2(buf: &[u8]) -> hdds::api::Result<Self> {
357 Ok(BytePayload { data: buf.to_vec() })
358 }
359}
360
361#[repr(C)]
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum HddsError {
374 HddsOk = 0,
376 HddsInvalidArgument = 1,
378 HddsNotFound = 2,
380 HddsOperationFailed = 3,
382 HddsOutOfMemory = 4,
384
385 HddsConfigError = 10,
388 HddsInvalidDomainId = 11,
390 HddsInvalidParticipantId = 12,
392 HddsNoAvailableParticipantId = 13,
394 HddsInvalidState = 14,
396
397 HddsIoError = 20,
400 HddsTransportError = 21,
402 HddsRegistrationFailed = 22,
404 HddsWouldBlock = 23,
406
407 HddsTypeMismatch = 30,
410 HddsSerializationError = 31,
412 HddsBufferTooSmall = 32,
414 HddsEndianMismatch = 33,
416
417 HddsQosIncompatible = 40,
420 HddsUnsupported = 41,
422
423 HddsPermissionDenied = 50,
426 HddsAuthenticationFailed = 51,
428}
429
430#[repr(C)]
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub enum HddsTransportMode {
434 HddsTransportIntraProcess = 0,
436 HddsTransportUdpMulticast = 1,
438}
439
440#[no_mangle]
449pub unsafe extern "C" fn hdds_participant_create(name: *const c_char) -> *mut HddsParticipant {
450 hdds_participant_create_with_transport(name, HddsTransportMode::HddsTransportUdpMulticast)
451}
452
453#[no_mangle]
468pub unsafe extern "C" fn hdds_participant_create_with_transport(
469 name: *const c_char,
470 transport: HddsTransportMode,
471) -> *mut HddsParticipant {
472 use std::sync::Once;
474 static INIT: Once = Once::new();
475 INIT.call_once(|| {
476 let _ = env_logger::try_init();
477 });
478
479 if name.is_null() {
480 return ptr::null_mut();
481 }
482
483 let Ok(name_str) = CStr::from_ptr(name).to_str() else {
484 return ptr::null_mut();
485 };
486
487 use hdds::TransportMode;
488 let mode = match transport {
489 HddsTransportMode::HddsTransportIntraProcess => TransportMode::IntraProcess,
490 HddsTransportMode::HddsTransportUdpMulticast => TransportMode::UdpMulticast,
491 };
492
493 let domain_id = std::env::var("HDDS_DOMAIN_ID")
508 .ok()
509 .and_then(|v| v.parse::<u32>().ok())
510 .unwrap_or(0);
511
512 if domain_id != 0 {
513 log::info!(
514 "[HDDS-C] Using domain_id={} from HDDS_DOMAIN_ID env",
515 domain_id
516 );
517 }
518
519 let Ok(participant) = Participant::builder(name_str)
520 .domain_id(domain_id)
521 .with_transport(mode)
522 .build()
523 else {
524 return ptr::null_mut();
525 };
526
527 Box::into_raw(Box::new(participant)).cast::<HddsParticipant>()
529}
530
531#[no_mangle]
537pub unsafe extern "C" fn hdds_participant_destroy(participant: *mut HddsParticipant) {
538 if !participant.is_null() {
539 let _ = Box::from_raw(participant.cast::<Arc<Participant>>());
541 }
542}
543
544#[no_mangle]
549pub unsafe extern "C" fn hdds_participant_graph_guard_condition(
550 participant: *mut HddsParticipant,
551) -> *const HddsGuardCondition {
552 if participant.is_null() {
553 return ptr::null();
554 }
555
556 let participant_ref = &*participant.cast::<Arc<Participant>>();
557 let guard = participant_ref.graph_guard();
558 let raw = Arc::into_raw(guard.clone()) as *const HddsGuardCondition;
559 guard_registry_add_handle(raw, guard);
560 raw
561}
562
563#[cfg(feature = "xtypes")]
570#[no_mangle]
571pub unsafe extern "C" fn hdds_participant_register_type_support(
572 participant: *mut HddsParticipant,
573 distro: u32,
574 type_support: *const rosidl_message_type_support_t,
575 out_handle: *mut *const HddsTypeObject,
576) -> HddsError {
577 if participant.is_null() || type_support.is_null() || out_handle.is_null() {
578 return HddsError::HddsInvalidArgument;
579 }
580
581 let Some(distro) = distro_from_u32(distro) else {
582 return HddsError::HddsInvalidArgument;
583 };
584
585 let participant_ref = &*participant.cast::<Arc<Participant>>();
586 out_handle.write(std::ptr::null());
587
588 match unsafe { participant_ref.register_type_from_type_support(distro, type_support) } {
589 Ok(handle) => {
590 let raw: *const TypeObjectHandle = Arc::into_raw(handle);
591 out_handle.write(raw.cast::<HddsTypeObject>());
592 HddsError::HddsOk
593 }
594 Err(err) => map_rosidl_error(&err),
595 }
596}
597
598#[cfg(feature = "xtypes")]
603#[no_mangle]
604pub unsafe extern "C" fn hdds_type_object_release(handle: *const HddsTypeObject) {
605 if !handle.is_null() {
606 let _ = Arc::from_raw(handle.cast::<TypeObjectHandle>());
607 }
608}
609
610#[cfg(feature = "xtypes")]
616#[no_mangle]
617pub unsafe extern "C" fn hdds_type_object_hash(
618 handle: *const HddsTypeObject,
619 out_version: *mut u8,
620 out_value: *mut u8,
621 value_len: usize,
622) -> HddsError {
623 if handle.is_null() || out_value.is_null() {
624 return HddsError::HddsInvalidArgument;
625 }
626
627 if value_len < ROS_HASH_SIZE {
628 return HddsError::HddsInvalidArgument;
629 }
630
631 let arc = Arc::from_raw(handle.cast::<TypeObjectHandle>());
632 if !out_version.is_null() {
633 *out_version = arc.ros_hash_version;
634 }
635 ptr::copy_nonoverlapping(arc.ros_hash.as_ref().as_ptr(), out_value, ROS_HASH_SIZE);
636 let _ = Arc::into_raw(arc);
637 HddsError::HddsOk
638}
639
640#[no_mangle]
645pub unsafe extern "C" fn hdds_version() -> *const c_char {
646 static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
647 VERSION.as_ptr().cast::<c_char>()
648}
649
650#[no_mangle]
656pub unsafe extern "C" fn hdds_writer_create(
657 participant: *mut HddsParticipant,
658 topic_name: *const c_char,
659) -> *mut HddsDataWriter {
660 if participant.is_null() || topic_name.is_null() {
661 return ptr::null_mut();
662 }
663
664 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
665 return ptr::null_mut();
666 };
667
668 let participant_ref = &*participant.cast::<Arc<Participant>>();
669
670 let Ok(writer) = participant_ref.create_writer::<BytePayload>(topic_str, QoS::default()) else {
671 return ptr::null_mut();
672 };
673
674 Box::into_raw(Box::new(writer)).cast::<HddsDataWriter>()
675}
676
677#[no_mangle]
683pub unsafe extern "C" fn hdds_writer_write(
684 writer: *mut HddsDataWriter,
685 data: *const c_void,
686 len: usize,
687) -> HddsError {
688 if writer.is_null() || data.is_null() {
689 return HddsError::HddsInvalidArgument;
690 }
691
692 let writer_ref = &*writer.cast::<DataWriter<BytePayload>>();
693 let data_slice = std::slice::from_raw_parts(data.cast::<u8>(), len);
694
695 let payload = BytePayload {
696 data: data_slice.to_vec(),
697 };
698
699 match writer_ref.write(&payload) {
700 Ok(()) => HddsError::HddsOk,
701 Err(_) => HddsError::HddsOperationFailed,
702 }
703}
704
705#[no_mangle]
711pub unsafe extern "C" fn hdds_writer_destroy(writer: *mut HddsDataWriter) {
712 if !writer.is_null() {
713 let _ = Box::from_raw(writer.cast::<DataWriter<BytePayload>>());
714 }
715}
716
717#[no_mangle]
724pub unsafe extern "C" fn hdds_writer_create_with_qos(
725 participant: *mut HddsParticipant,
726 topic_name: *const c_char,
727 qos: *const HddsQoS,
728) -> *mut HddsDataWriter {
729 if participant.is_null() || topic_name.is_null() {
730 return ptr::null_mut();
731 }
732
733 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
734 return ptr::null_mut();
735 };
736
737 let participant_ref = &*participant.cast::<Arc<Participant>>();
738
739 let qos_value = if qos.is_null() {
741 QoS::default()
742 } else {
743 (*qos.cast::<QoS>()).clone()
744 };
745
746 let Ok(writer) = participant_ref.create_writer::<BytePayload>(topic_str, qos_value) else {
747 return ptr::null_mut();
748 };
749
750 Box::into_raw(Box::new(writer)).cast::<HddsDataWriter>()
751}
752
753#[no_mangle]
765pub unsafe extern "C" fn hdds_writer_create_with_type(
766 participant: *mut HddsParticipant,
767 topic_name: *const c_char,
768 type_name: *const c_char,
769 qos: *const HddsQoS,
770) -> *mut HddsDataWriter {
771 if participant.is_null() || topic_name.is_null() {
772 return ptr::null_mut();
773 }
774
775 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
776 return ptr::null_mut();
777 };
778
779 let participant_ref = &*participant.cast::<Arc<Participant>>();
780
781 let qos_value = if qos.is_null() {
782 QoS::default()
783 } else {
784 (*qos.cast::<QoS>()).clone()
785 };
786
787 if !type_name.is_null() {
789 if let Ok(type_str) = CStr::from_ptr(type_name).to_str() {
790 if !type_str.is_empty() {
791 log::info!(
792 "[HDDS-C] Creating writer topic='{}' type='{}'",
793 topic_str,
794 type_str
795 );
796 let Ok(writer) = participant_ref
797 .create_writer_with_type::<BytePayload>(topic_str, qos_value, type_str, None)
798 else {
799 return ptr::null_mut();
800 };
801 return Box::into_raw(Box::new(writer)).cast::<HddsDataWriter>();
802 }
803 }
804 }
805
806 let Ok(writer) = participant_ref.create_writer::<BytePayload>(topic_str, qos_value) else {
808 return ptr::null_mut();
809 };
810
811 Box::into_raw(Box::new(writer)).cast::<HddsDataWriter>()
812}
813
814#[no_mangle]
820pub unsafe extern "C" fn hdds_reader_create(
821 participant: *mut HddsParticipant,
822 topic_name: *const c_char,
823) -> *mut HddsDataReader {
824 if participant.is_null() || topic_name.is_null() {
825 return ptr::null_mut();
826 }
827
828 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
829 return ptr::null_mut();
830 };
831
832 let participant_ref = &*participant.cast::<Arc<Participant>>();
833
834 let Ok(reader) = participant_ref.create_reader::<BytePayload>(topic_str, QoS::default()) else {
835 return ptr::null_mut();
836 };
837
838 Box::into_raw(Box::new(reader)).cast::<HddsDataReader>()
839}
840
841#[no_mangle]
848pub unsafe extern "C" fn hdds_reader_create_with_qos(
849 participant: *mut HddsParticipant,
850 topic_name: *const c_char,
851 qos: *const HddsQoS,
852) -> *mut HddsDataReader {
853 if participant.is_null() || topic_name.is_null() {
854 return ptr::null_mut();
855 }
856
857 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
858 return ptr::null_mut();
859 };
860
861 let participant_ref = &*participant.cast::<Arc<Participant>>();
862
863 let qos_value = if qos.is_null() {
865 QoS::default()
866 } else {
867 (*qos.cast::<QoS>()).clone()
868 };
869
870 let Ok(reader) = participant_ref.create_reader::<BytePayload>(topic_str, qos_value) else {
871 return ptr::null_mut();
872 };
873
874 Box::into_raw(Box::new(reader)).cast::<HddsDataReader>()
875}
876
877#[no_mangle]
889pub unsafe extern "C" fn hdds_reader_create_with_type(
890 participant: *mut HddsParticipant,
891 topic_name: *const c_char,
892 type_name: *const c_char,
893 qos: *const HddsQoS,
894) -> *mut HddsDataReader {
895 if participant.is_null() || topic_name.is_null() {
896 return ptr::null_mut();
897 }
898
899 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
900 return ptr::null_mut();
901 };
902
903 let participant_ref = &*participant.cast::<Arc<Participant>>();
904
905 let qos_value = if qos.is_null() {
906 QoS::default()
907 } else {
908 (*qos.cast::<QoS>()).clone()
909 };
910
911 if !type_name.is_null() {
913 if let Ok(type_str) = CStr::from_ptr(type_name).to_str() {
914 if !type_str.is_empty() {
915 log::info!(
916 "[HDDS-C] Creating reader topic='{}' type='{}'",
917 topic_str,
918 type_str
919 );
920 let Ok(reader) = participant_ref
921 .create_reader_with_type::<BytePayload>(topic_str, qos_value, type_str, None)
922 else {
923 return ptr::null_mut();
924 };
925 return Box::into_raw(Box::new(reader)).cast::<HddsDataReader>();
926 }
927 }
928 }
929
930 let Ok(reader) = participant_ref.create_reader::<BytePayload>(topic_str, qos_value) else {
932 return ptr::null_mut();
933 };
934
935 Box::into_raw(Box::new(reader)).cast::<HddsDataReader>()
936}
937
938#[no_mangle]
945pub unsafe extern "C" fn hdds_reader_take(
946 reader: *mut HddsDataReader,
947 data_out: *mut c_void,
948 max_len: usize,
949 len_out: *mut usize,
950) -> HddsError {
951 if reader.is_null() || data_out.is_null() || len_out.is_null() {
952 return HddsError::HddsInvalidArgument;
953 }
954
955 let reader_ref = &*reader.cast::<DataReader<BytePayload>>();
956
957 match reader_ref.take() {
958 Ok(Some(payload)) => {
959 let required = payload.data.len();
960 *len_out = required;
961 if required > max_len {
962 return HddsError::HddsOutOfMemory;
963 }
964
965 std::ptr::copy_nonoverlapping(payload.data.as_ptr(), data_out.cast::<u8>(), required);
966 HddsError::HddsOk
967 }
968 Ok(None) => HddsError::HddsNotFound,
969 Err(_) => HddsError::HddsOperationFailed,
970 }
971}
972
973#[no_mangle]
979pub unsafe extern "C" fn hdds_reader_destroy(reader: *mut HddsDataReader) {
980 if !reader.is_null() {
981 let _ = Box::from_raw(reader.cast::<DataReader<BytePayload>>());
982 }
983}
984
985#[no_mangle]
990pub unsafe extern "C" fn hdds_reader_get_status_condition(
991 reader: *mut HddsDataReader,
992) -> *const HddsStatusCondition {
993 if reader.is_null() {
994 return ptr::null();
995 }
996
997 let reader_ref = &*reader.cast::<DataReader<BytePayload>>();
998 let condition = reader_ref.get_status_condition();
999 let raw = Arc::into_raw(condition.clone()) as *const HddsStatusCondition;
1000 status_registry_add_handle(raw, condition);
1001 raw
1002}
1003
1004#[no_mangle]
1009pub unsafe extern "C" fn hdds_status_condition_release(condition: *const HddsStatusCondition) {
1010 if status_registry_release(condition) {
1011 let _ = Arc::from_raw(condition.cast::<StatusCondition>());
1012 }
1013}
1014
1015#[no_mangle]
1020pub unsafe extern "C" fn hdds_guard_condition_create() -> *const HddsGuardCondition {
1021 let guard = Arc::new(GuardCondition::new());
1022 let raw = Arc::into_raw(guard.clone()) as *const HddsGuardCondition;
1023 guard_registry_add_handle(raw, guard);
1024 raw
1025}
1026
1027#[no_mangle]
1032pub unsafe extern "C" fn hdds_guard_condition_release(condition: *const HddsGuardCondition) {
1033 if guard_registry_release(condition) {
1034 let _ = Arc::from_raw(condition.cast::<GuardCondition>());
1035 }
1036}
1037
1038#[no_mangle]
1043pub unsafe extern "C" fn hdds_guard_condition_set_trigger(
1044 condition: *const HddsGuardCondition,
1045 active: bool,
1046) {
1047 let Some(guard) = guard_registry_clone(condition) else {
1048 return;
1049 };
1050 guard.set_trigger_value(active);
1051}
1052
1053#[no_mangle]
1058pub unsafe extern "C" fn hdds_guard_condition_get_trigger(
1059 condition: *const HddsGuardCondition,
1060) -> bool {
1061 let Some(guard) = guard_registry_clone(condition) else {
1062 return false;
1063 };
1064 guard.get_trigger_value()
1065}
1066
1067#[no_mangle]
1072pub unsafe extern "C" fn hdds_waitset_create() -> *mut HddsWaitSet {
1073 Box::into_raw(Box::new(ForeignWaitSet::new())) as *mut HddsWaitSet
1074}
1075
1076#[no_mangle]
1081pub unsafe extern "C" fn hdds_waitset_destroy(waitset: *mut HddsWaitSet) {
1082 if !waitset.is_null() {
1083 let _ = Box::from_raw(waitset.cast::<ForeignWaitSet>());
1084 }
1085}
1086
1087#[no_mangle]
1093pub unsafe extern "C" fn hdds_waitset_attach_status_condition(
1094 waitset: *mut HddsWaitSet,
1095 condition: *const HddsStatusCondition,
1096) -> HddsError {
1097 if waitset.is_null() || condition.is_null() {
1098 return HddsError::HddsInvalidArgument;
1099 }
1100
1101 let waitset_ref = &*waitset.cast::<ForeignWaitSet>();
1102 let Some(clone) = status_registry_clone(condition) else {
1103 return HddsError::HddsInvalidArgument;
1104 };
1105
1106 match waitset_ref.attach_status(clone, condition.cast()) {
1107 Ok(()) => HddsError::HddsOk,
1108 Err(err) => err.into(),
1109 }
1110}
1111
1112#[no_mangle]
1118pub unsafe extern "C" fn hdds_waitset_attach_guard_condition(
1119 waitset: *mut HddsWaitSet,
1120 condition: *const HddsGuardCondition,
1121) -> HddsError {
1122 if waitset.is_null() || condition.is_null() {
1123 return HddsError::HddsInvalidArgument;
1124 }
1125
1126 let waitset_ref = &*waitset.cast::<ForeignWaitSet>();
1127 let Some(clone) = guard_registry_clone(condition) else {
1128 return HddsError::HddsInvalidArgument;
1129 };
1130
1131 match waitset_ref.attach_guard(clone, condition.cast()) {
1132 Ok(()) => HddsError::HddsOk,
1133 Err(err) => err.into(),
1134 }
1135}
1136
1137#[no_mangle]
1143pub unsafe extern "C" fn hdds_waitset_detach_condition(
1144 waitset: *mut HddsWaitSet,
1145 condition: *const c_void,
1146) -> HddsError {
1147 if waitset.is_null() || condition.is_null() {
1148 return HddsError::HddsInvalidArgument;
1149 }
1150
1151 let waitset_ref = &*waitset.cast::<ForeignWaitSet>();
1152 match waitset_ref.detach(condition) {
1153 Ok(()) => HddsError::HddsOk,
1154 Err(err) => err.into(),
1155 }
1156}
1157
1158#[no_mangle]
1165pub unsafe extern "C" fn hdds_waitset_wait(
1166 waitset: *mut HddsWaitSet,
1167 timeout_ns: i64,
1168 out_conditions: *mut *const c_void,
1169 max_conditions: usize,
1170 out_len: *mut usize,
1171) -> HddsError {
1172 if waitset.is_null() || out_conditions.is_null() || out_len.is_null() {
1173 return HddsError::HddsInvalidArgument;
1174 }
1175
1176 *out_len = 0;
1177
1178 let waitset_ref = &*waitset.cast::<ForeignWaitSet>();
1179 let timeout = if timeout_ns < 0 {
1180 None
1181 } else {
1182 match u64::try_from(timeout_ns) {
1183 Ok(nanos) => Some(Duration::from_nanos(nanos)),
1184 Err(_) => None,
1185 }
1186 };
1187
1188 let triggered = match waitset_ref.wait(timeout) {
1189 Ok(list) => list,
1190 Err(err) => return err.into(),
1191 };
1192
1193 if triggered.len() > max_conditions {
1194 return HddsError::HddsOperationFailed;
1195 }
1196
1197 for (idx, ptr_value) in triggered.iter().enumerate() {
1198 *out_conditions.add(idx) = *ptr_value;
1199 }
1200
1201 *out_len = triggered.len();
1202 HddsError::HddsOk
1203}
1204
1205#[cfg(feature = "rmw")]
1214#[no_mangle]
1215pub unsafe extern "C" fn hdds_rmw_context_create(name: *const c_char) -> *mut HddsRmwContext {
1216 if name.is_null() {
1217 return ptr::null_mut();
1218 }
1219
1220 let Ok(name_str) = CStr::from_ptr(name).to_str() else {
1221 return ptr::null_mut();
1222 };
1223
1224 match ForeignRmwContext::create(name_str) {
1225 Ok(ctx) => {
1226 #[allow(clippy::arc_with_non_send_sync)]
1227 let arc = Arc::new(ctx);
1228 Box::into_raw(Box::new(arc)).cast::<HddsRmwContext>()
1229 }
1230 Err(_err) => ptr::null_mut(),
1231 }
1232}
1233
1234#[cfg(feature = "rmw")]
1239#[no_mangle]
1240pub unsafe extern "C" fn hdds_rmw_context_destroy(ctx: *mut HddsRmwContext) {
1241 if !ctx.is_null() {
1242 let _ = Box::from_raw(ctx.cast::<Arc<ForeignRmwContext>>());
1243 }
1244}
1245
1246#[cfg(feature = "rmw")]
1251#[no_mangle]
1252pub unsafe extern "C" fn hdds_rmw_context_graph_guard_key(ctx: *mut HddsRmwContext) -> u64 {
1253 if ctx.is_null() {
1254 return 0;
1255 }
1256 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1257 ctx_ref.as_ref().graph_guard_key()
1258}
1259
1260#[cfg(feature = "rmw")]
1268#[no_mangle]
1269pub unsafe extern "C" fn hdds_rmw_context_guid_prefix(
1270 ctx: *mut HddsRmwContext,
1271 out_prefix: *mut u8,
1272) -> HddsError {
1273 if ctx.is_null() || out_prefix.is_null() {
1274 return HddsError::HddsInvalidArgument;
1275 }
1276 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1277 let prefix = ctx_ref.guid_prefix();
1278 std::ptr::copy_nonoverlapping(prefix.as_ptr(), out_prefix, 12);
1279 HddsError::HddsOk
1280}
1281
1282#[cfg(feature = "rmw")]
1287#[no_mangle]
1288pub unsafe extern "C" fn hdds_rmw_context_graph_guard_condition(
1289 ctx: *mut HddsRmwContext,
1290) -> *const HddsGuardCondition {
1291 if ctx.is_null() {
1292 return ptr::null();
1293 }
1294
1295 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1296 let guard = ctx_ref.as_ref().graph_guard_condition();
1297 let raw = Arc::into_raw(guard.clone());
1298 guard_registry_add_handle(raw.cast::<HddsGuardCondition>(), guard);
1299 ctx_ref.as_ref().register_graph_guard_ptr(raw);
1300 raw.cast::<HddsGuardCondition>()
1301}
1302
1303#[cfg(feature = "rmw")]
1308#[no_mangle]
1309pub unsafe extern "C" fn hdds_rmw_context_attach_guard_condition(
1310 ctx: *mut HddsRmwContext,
1311 guard: *const HddsGuardCondition,
1312 out_key: *mut u64,
1313) -> HddsError {
1314 if ctx.is_null() || guard.is_null() || out_key.is_null() {
1315 return HddsError::HddsInvalidArgument;
1316 }
1317
1318 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1319 let Some(guard_clone) = guard_registry_clone(guard) else {
1320 return HddsError::HddsInvalidArgument;
1321 };
1322
1323 match ctx_ref
1324 .as_ref()
1325 .attach_guard(guard_clone, guard.cast::<GuardCondition>())
1326 {
1327 Ok(key) => {
1328 *out_key = key;
1329 HddsError::HddsOk
1330 }
1331 Err(err) => map_api_error(err),
1332 }
1333}
1334
1335#[cfg(feature = "rmw")]
1340#[no_mangle]
1341pub unsafe extern "C" fn hdds_rmw_context_attach_status_condition(
1342 ctx: *mut HddsRmwContext,
1343 status: *const HddsStatusCondition,
1344 out_key: *mut u64,
1345) -> HddsError {
1346 if ctx.is_null() || status.is_null() || out_key.is_null() {
1347 return HddsError::HddsInvalidArgument;
1348 }
1349
1350 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1351 let Some(status_clone) = status_registry_clone(status) else {
1352 return HddsError::HddsInvalidArgument;
1353 };
1354
1355 match ctx_ref
1356 .as_ref()
1357 .attach_status(status_clone, status.cast::<StatusCondition>())
1358 {
1359 Ok(key) => {
1360 *out_key = key;
1361 HddsError::HddsOk
1362 }
1363 Err(err) => map_api_error(err),
1364 }
1365}
1366
1367#[cfg(feature = "rmw")]
1372#[no_mangle]
1373pub unsafe extern "C" fn hdds_rmw_context_attach_reader(
1374 ctx: *mut HddsRmwContext,
1375 reader: *mut HddsDataReader,
1376 out_key: *mut u64,
1377) -> HddsError {
1378 if ctx.is_null() || reader.is_null() || out_key.is_null() {
1379 return HddsError::HddsInvalidArgument;
1380 }
1381
1382 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1383 let reader_ptr = reader.cast::<c_void>();
1384 let status_arc = match ctx_ref.status_for_reader(reader_ptr) {
1385 Ok(status) => status,
1386 Err(err) => return map_api_error(err),
1387 };
1388 let raw_status = Arc::as_ptr(&status_arc);
1389
1390 match ctx_ref.attach_reader(reader_ptr, status_arc, raw_status) {
1391 Ok(key) => {
1392 *out_key = key;
1393 HddsError::HddsOk
1394 }
1395 Err(err) => map_api_error(err),
1396 }
1397}
1398
1399#[cfg(feature = "rmw")]
1404#[no_mangle]
1405pub unsafe extern "C" fn hdds_rmw_context_create_reader(
1406 ctx: *mut HddsRmwContext,
1407 topic_name: *const c_char,
1408 out_reader: *mut *mut HddsDataReader,
1409) -> HddsError {
1410 if ctx.is_null() || topic_name.is_null() || out_reader.is_null() {
1411 return HddsError::HddsInvalidArgument;
1412 }
1413
1414 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1415 return HddsError::HddsInvalidArgument;
1416 };
1417
1418 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1419 match ctx_ref.create_reader_raw(topic_str) {
1420 Ok(reader_ptr) => {
1421 out_reader.write(reader_ptr.cast::<HddsDataReader>());
1422 HddsError::HddsOk
1423 }
1424 Err(err) => map_api_error(err),
1425 }
1426}
1427
1428#[cfg(feature = "rmw")]
1429#[no_mangle]
1434pub unsafe extern "C" fn hdds_rmw_context_create_reader_with_qos(
1435 ctx: *mut HddsRmwContext,
1436 topic_name: *const c_char,
1437 qos: *const HddsQoS,
1438 out_reader: *mut *mut HddsDataReader,
1439) -> HddsError {
1440 if ctx.is_null() || topic_name.is_null() || out_reader.is_null() {
1441 return HddsError::HddsInvalidArgument;
1442 }
1443
1444 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1445 return HddsError::HddsInvalidArgument;
1446 };
1447
1448 let qos_ref = if qos.is_null() {
1449 QoS::default()
1450 } else {
1451 (*qos.cast::<QoS>()).clone()
1452 };
1453
1454 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1455 match ctx_ref.create_reader_raw_with_qos(topic_str, &qos_ref) {
1456 Ok(reader_ptr) => {
1457 out_reader.write(reader_ptr.cast::<HddsDataReader>());
1458 HddsError::HddsOk
1459 }
1460 Err(err) => map_api_error(err),
1461 }
1462}
1463
1464#[cfg(feature = "rmw")]
1469#[no_mangle]
1470pub unsafe extern "C" fn hdds_rmw_context_destroy_reader(
1471 ctx: *mut HddsRmwContext,
1472 reader: *mut HddsDataReader,
1473) -> HddsError {
1474 if ctx.is_null() || reader.is_null() {
1475 return HddsError::HddsInvalidArgument;
1476 }
1477
1478 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1479 match ctx_ref.destroy_reader_raw(reader.cast::<c_void>()) {
1480 Ok(()) => HddsError::HddsOk,
1481 Err(err) => map_api_error(err),
1482 }
1483}
1484#[cfg(feature = "rmw")]
1488#[no_mangle]
1489pub unsafe extern "C" fn hdds_rmw_context_create_writer(
1490 ctx: *mut HddsRmwContext,
1491 topic_name: *const c_char,
1492 out_writer: *mut *mut HddsDataWriter,
1493) -> HddsError {
1494 if ctx.is_null() || topic_name.is_null() || out_writer.is_null() {
1495 return HddsError::HddsInvalidArgument;
1496 }
1497
1498 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1499 return HddsError::HddsInvalidArgument;
1500 };
1501
1502 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1503 match ctx_ref.create_writer_raw(topic_str) {
1504 Ok(writer_ptr) => {
1505 out_writer.write(writer_ptr.cast::<HddsDataWriter>());
1506 HddsError::HddsOk
1507 }
1508 Err(err) => map_api_error(err),
1509 }
1510}
1511#[cfg(feature = "rmw")]
1515#[no_mangle]
1516pub unsafe extern "C" fn hdds_rmw_context_create_writer_with_qos(
1517 ctx: *mut HddsRmwContext,
1518 topic_name: *const c_char,
1519 qos: *const HddsQoS,
1520 out_writer: *mut *mut HddsDataWriter,
1521) -> HddsError {
1522 if ctx.is_null() || topic_name.is_null() || out_writer.is_null() {
1523 return HddsError::HddsInvalidArgument;
1524 }
1525
1526 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1527 return HddsError::HddsInvalidArgument;
1528 };
1529
1530 let qos_ref = if qos.is_null() {
1531 QoS::default()
1532 } else {
1533 (*qos.cast::<QoS>()).clone()
1534 };
1535
1536 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1537 match ctx_ref.create_writer_raw_with_qos(topic_str, &qos_ref) {
1538 Ok(writer_ptr) => {
1539 out_writer.write(writer_ptr.cast::<HddsDataWriter>());
1540 HddsError::HddsOk
1541 }
1542 Err(err) => map_api_error(err),
1543 }
1544}
1545#[cfg(feature = "rmw")]
1549#[no_mangle]
1550pub unsafe extern "C" fn hdds_rmw_context_bind_topic_type(
1551 ctx: *mut HddsRmwContext,
1552 topic_name: *const c_char,
1553 type_support: *const rosidl_message_type_support_t,
1554) -> HddsError {
1555 if ctx.is_null() || topic_name.is_null() || type_support.is_null() {
1556 return HddsError::HddsInvalidArgument;
1557 }
1558
1559 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1560 return HddsError::HddsInvalidArgument;
1561 };
1562
1563 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1564 match ctx_ref.bind_topic_type(topic_str, type_support) {
1565 Ok(()) => HddsError::HddsOk,
1566 Err(ApiError::Config) => HddsError::HddsOk,
1567 Err(ApiError::Unsupported) => HddsError::HddsOk,
1568 Err(err) => map_api_error(err),
1569 }
1570}
1571#[cfg(feature = "rmw")]
1575#[no_mangle]
1576pub unsafe extern "C" fn hdds_rmw_context_destroy_writer(
1577 ctx: *mut HddsRmwContext,
1578 writer: *mut HddsDataWriter,
1579) -> HddsError {
1580 if ctx.is_null() || writer.is_null() {
1581 return HddsError::HddsInvalidArgument;
1582 }
1583
1584 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1585 match ctx_ref.destroy_writer_raw(writer.cast::<c_void>()) {
1586 Ok(()) => HddsError::HddsOk,
1587 Err(err) => map_api_error(err),
1588 }
1589}
1590#[cfg(feature = "rmw")]
1594#[no_mangle]
1595pub unsafe extern "C" fn hdds_rmw_context_register_node(
1596 ctx: *mut HddsRmwContext,
1597 node_name: *const c_char,
1598 node_namespace: *const c_char,
1599 node_enclave: *const c_char,
1600) -> HddsError {
1601 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() {
1602 return HddsError::HddsInvalidArgument;
1603 }
1604
1605 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1606 return HddsError::HddsInvalidArgument;
1607 };
1608
1609 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1610 return HddsError::HddsInvalidArgument;
1611 };
1612
1613 let enclave_str = if node_enclave.is_null() {
1614 ""
1615 } else {
1616 match CStr::from_ptr(node_enclave).to_str() {
1617 Ok(value) => value,
1618 Err(_) => return HddsError::HddsInvalidArgument,
1619 }
1620 };
1621
1622 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1623 ctx_ref.register_node_info(name_str, namespace_str, enclave_str);
1624 HddsError::HddsOk
1625}
1626#[cfg(feature = "rmw")]
1630#[no_mangle]
1631pub unsafe extern "C" fn hdds_rmw_context_unregister_node(
1632 ctx: *mut HddsRmwContext,
1633 node_name: *const c_char,
1634 node_namespace: *const c_char,
1635) -> HddsError {
1636 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() {
1637 return HddsError::HddsInvalidArgument;
1638 }
1639
1640 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1641 return HddsError::HddsInvalidArgument;
1642 };
1643
1644 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1645 return HddsError::HddsInvalidArgument;
1646 };
1647
1648 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1649 ctx_ref.unregister_node_info(name_str, namespace_str);
1650 HddsError::HddsOk
1651}
1652#[cfg(feature = "rmw")]
1656#[no_mangle]
1657pub unsafe extern "C" fn hdds_rmw_context_register_publisher_endpoint(
1658 ctx: *mut HddsRmwContext,
1659 node_name: *const c_char,
1660 node_namespace: *const c_char,
1661 topic_name: *const c_char,
1662 type_support: *const rosidl_message_type_support_t,
1663 endpoint_gid: *const u8,
1664 qos_profile: *const HddsRmwQosProfile,
1665) -> HddsError {
1666 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() || topic_name.is_null() {
1667 return HddsError::HddsInvalidArgument;
1668 }
1669
1670 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1671 return HddsError::HddsInvalidArgument;
1672 };
1673
1674 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1675 return HddsError::HddsInvalidArgument;
1676 };
1677
1678 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1679 return HddsError::HddsInvalidArgument;
1680 };
1681
1682 let mut gid = [0u8; HDDS_RMW_GID_SIZE];
1683 if !endpoint_gid.is_null() {
1684 let src = slice::from_raw_parts(endpoint_gid, gid.len());
1685 gid.copy_from_slice(src);
1686 }
1687
1688 let qos = if qos_profile.is_null() {
1689 hdds::rmw::graph::EndpointQos::default()
1690 } else {
1691 hdds::rmw::graph::EndpointQos::from(*qos_profile)
1692 };
1693
1694 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1695 match ctx_ref.register_publisher_endpoint(
1696 name_str,
1697 namespace_str,
1698 topic_str,
1699 type_support,
1700 gid,
1701 qos,
1702 ) {
1703 Ok(()) => HddsError::HddsOk,
1704 Err(ApiError::Config) => HddsError::HddsOk,
1705 Err(ApiError::Unsupported) => HddsError::HddsOk,
1706 Err(err) => map_api_error(err),
1707 }
1708}
1709#[cfg(feature = "rmw")]
1713#[no_mangle]
1714pub unsafe extern "C" fn hdds_rmw_context_unregister_publisher_endpoint(
1715 ctx: *mut HddsRmwContext,
1716 node_name: *const c_char,
1717 node_namespace: *const c_char,
1718 topic_name: *const c_char,
1719 endpoint_gid: *const u8,
1720) -> HddsError {
1721 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() || topic_name.is_null() {
1722 return HddsError::HddsInvalidArgument;
1723 }
1724
1725 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1726 return HddsError::HddsInvalidArgument;
1727 };
1728
1729 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1730 return HddsError::HddsInvalidArgument;
1731 };
1732
1733 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1734 return HddsError::HddsInvalidArgument;
1735 };
1736
1737 let mut gid = [0u8; HDDS_RMW_GID_SIZE];
1738 if !endpoint_gid.is_null() {
1739 let src = slice::from_raw_parts(endpoint_gid, gid.len());
1740 gid.copy_from_slice(src);
1741 }
1742
1743 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1744 ctx_ref.unregister_publisher_endpoint(name_str, namespace_str, topic_str, &gid);
1745 HddsError::HddsOk
1746}
1747#[cfg(feature = "rmw")]
1751#[no_mangle]
1752pub unsafe extern "C" fn hdds_rmw_context_register_subscription_endpoint(
1753 ctx: *mut HddsRmwContext,
1754 node_name: *const c_char,
1755 node_namespace: *const c_char,
1756 topic_name: *const c_char,
1757 type_support: *const rosidl_message_type_support_t,
1758 endpoint_gid: *const u8,
1759 qos_profile: *const HddsRmwQosProfile,
1760) -> HddsError {
1761 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() || topic_name.is_null() {
1762 return HddsError::HddsInvalidArgument;
1763 }
1764
1765 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1766 return HddsError::HddsInvalidArgument;
1767 };
1768
1769 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1770 return HddsError::HddsInvalidArgument;
1771 };
1772
1773 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1774 return HddsError::HddsInvalidArgument;
1775 };
1776
1777 let mut gid = [0u8; HDDS_RMW_GID_SIZE];
1778 if !endpoint_gid.is_null() {
1779 let src = slice::from_raw_parts(endpoint_gid, gid.len());
1780 gid.copy_from_slice(src);
1781 }
1782
1783 let qos = if qos_profile.is_null() {
1784 hdds::rmw::graph::EndpointQos::default()
1785 } else {
1786 hdds::rmw::graph::EndpointQos::from(*qos_profile)
1787 };
1788
1789 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1790 match ctx_ref.register_subscription_endpoint(
1791 name_str,
1792 namespace_str,
1793 topic_str,
1794 type_support,
1795 gid,
1796 qos,
1797 ) {
1798 Ok(()) => HddsError::HddsOk,
1799 Err(ApiError::Config) => HddsError::HddsOk,
1800 Err(ApiError::Unsupported) => HddsError::HddsOk,
1801 Err(err) => map_api_error(err),
1802 }
1803}
1804#[cfg(feature = "rmw")]
1808#[no_mangle]
1809pub unsafe extern "C" fn hdds_rmw_context_unregister_subscription_endpoint(
1810 ctx: *mut HddsRmwContext,
1811 node_name: *const c_char,
1812 node_namespace: *const c_char,
1813 topic_name: *const c_char,
1814 endpoint_gid: *const u8,
1815) -> HddsError {
1816 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() || topic_name.is_null() {
1817 return HddsError::HddsInvalidArgument;
1818 }
1819
1820 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1821 return HddsError::HddsInvalidArgument;
1822 };
1823
1824 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1825 return HddsError::HddsInvalidArgument;
1826 };
1827
1828 let Ok(topic_str) = CStr::from_ptr(topic_name).to_str() else {
1829 return HddsError::HddsInvalidArgument;
1830 };
1831
1832 let mut gid = [0u8; HDDS_RMW_GID_SIZE];
1833 if !endpoint_gid.is_null() {
1834 let src = slice::from_raw_parts(endpoint_gid, gid.len());
1835 gid.copy_from_slice(src);
1836 }
1837
1838 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1839 ctx_ref.unregister_subscription_endpoint(name_str, namespace_str, topic_str, &gid);
1840 HddsError::HddsOk
1841}
1842#[cfg(feature = "rmw")]
1846#[no_mangle]
1847pub unsafe extern "C" fn hdds_rmw_context_for_each_node(
1848 ctx: *mut HddsRmwContext,
1849 visitor: HddsNodeVisitor,
1850 user_data: *mut c_void,
1851 out_version: *mut u64,
1852 out_count: *mut usize,
1853) -> HddsError {
1854 if ctx.is_null() {
1855 return HddsError::HddsInvalidArgument;
1856 }
1857
1858 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1859 let mut error: Option<HddsError> = None;
1860
1861 let (version, count) = ctx_ref.list_nodes_with(|name, namespace_| {
1862 if error.is_some() {
1863 return;
1864 }
1865
1866 if let Some(cb) = visitor {
1867 let Ok(name_cstr) = CString::new(name) else {
1868 error = Some(HddsError::HddsInvalidArgument);
1869 return;
1870 };
1871 let Ok(namespace_cstr) = CString::new(namespace_) else {
1872 error = Some(HddsError::HddsInvalidArgument);
1873 return;
1874 };
1875
1876 unsafe {
1877 cb(name_cstr.as_ptr(), namespace_cstr.as_ptr(), user_data);
1878 }
1879 }
1880 });
1881
1882 if !out_version.is_null() {
1883 out_version.write(version);
1884 }
1885
1886 if !out_count.is_null() {
1887 out_count.write(count);
1888 }
1889
1890 if let Some(err) = error {
1891 err
1892 } else {
1893 HddsError::HddsOk
1894 }
1895}
1896#[cfg(feature = "rmw")]
1900#[no_mangle]
1901pub unsafe extern "C" fn hdds_rmw_context_for_each_node_with_enclave(
1902 ctx: *mut HddsRmwContext,
1903 visitor: HddsNodeEnclaveVisitor,
1904 user_data: *mut c_void,
1905 out_version: *mut u64,
1906 out_count: *mut usize,
1907) -> HddsError {
1908 if ctx.is_null() {
1909 return HddsError::HddsInvalidArgument;
1910 }
1911
1912 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1913 let mut error: Option<HddsError> = None;
1914
1915 let (version, count) = ctx_ref.list_nodes_with_enclave(|name, namespace_, enclave| {
1916 if error.is_some() {
1917 return;
1918 }
1919
1920 if let Some(cb) = visitor {
1921 let Ok(name_cstr) = CString::new(name) else {
1922 error = Some(HddsError::HddsInvalidArgument);
1923 return;
1924 };
1925 let Ok(namespace_cstr) = CString::new(namespace_) else {
1926 error = Some(HddsError::HddsInvalidArgument);
1927 return;
1928 };
1929 let Ok(enclave_cstr) = CString::new(enclave) else {
1930 error = Some(HddsError::HddsInvalidArgument);
1931 return;
1932 };
1933
1934 unsafe {
1935 cb(
1936 name_cstr.as_ptr(),
1937 namespace_cstr.as_ptr(),
1938 enclave_cstr.as_ptr(),
1939 user_data,
1940 );
1941 }
1942 }
1943 });
1944
1945 if !out_version.is_null() {
1946 out_version.write(version);
1947 }
1948
1949 if !out_count.is_null() {
1950 out_count.write(count);
1951 }
1952
1953 if let Some(err) = error {
1954 err
1955 } else {
1956 HddsError::HddsOk
1957 }
1958}
1959#[cfg(feature = "rmw")]
1963#[no_mangle]
1964pub unsafe extern "C" fn hdds_rmw_context_for_each_publisher_endpoint(
1965 ctx: *mut HddsRmwContext,
1966 node_name: *const c_char,
1967 node_namespace: *const c_char,
1968 visitor: HddsEndpointVisitor,
1969 user_data: *mut c_void,
1970 out_version: *mut u64,
1971 out_count: *mut usize,
1972) -> HddsError {
1973 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() {
1974 return HddsError::HddsInvalidArgument;
1975 }
1976
1977 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
1978 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
1979 return HddsError::HddsInvalidArgument;
1980 };
1981 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
1982 return HddsError::HddsInvalidArgument;
1983 };
1984
1985 let mut error: Option<HddsError> = None;
1986 let visit_result = ctx_ref.visit_publishers_with(name_str, namespace_str, |endpoint| {
1987 if error.is_some() {
1988 return;
1989 }
1990
1991 if let Some(cb) = visitor {
1992 let Ok(topic_cstr) = CString::new(endpoint.topic.as_str()) else {
1993 error = Some(HddsError::HddsInvalidArgument);
1994 return;
1995 };
1996 let type_name = normalize_ros_type_name(endpoint.type_name.as_str());
1997 let Ok(type_cstr) = CString::new(type_name.as_str()) else {
1998 error = Some(HddsError::HddsInvalidArgument);
1999 return;
2000 };
2001
2002 let qos_profile = HddsRmwQosProfile::from(&endpoint.qos);
2003 unsafe {
2004 cb(
2005 topic_cstr.as_ptr(),
2006 type_cstr.as_ptr(),
2007 endpoint.gid.as_ptr(),
2008 &qos_profile,
2009 user_data,
2010 );
2011 }
2012 }
2013 });
2014
2015 match visit_result {
2016 Ok((version, count)) => {
2017 if !out_version.is_null() {
2018 out_version.write(version);
2019 }
2020 if !out_count.is_null() {
2021 out_count.write(count);
2022 }
2023
2024 if let Some(err) = error {
2025 err
2026 } else {
2027 HddsError::HddsOk
2028 }
2029 }
2030 Err(err) => map_api_error(err),
2031 }
2032}
2033#[cfg(feature = "rmw")]
2037#[no_mangle]
2038pub unsafe extern "C" fn hdds_rmw_context_for_each_subscription_endpoint(
2039 ctx: *mut HddsRmwContext,
2040 node_name: *const c_char,
2041 node_namespace: *const c_char,
2042 visitor: HddsEndpointVisitor,
2043 user_data: *mut c_void,
2044 out_version: *mut u64,
2045 out_count: *mut usize,
2046) -> HddsError {
2047 if ctx.is_null() || node_name.is_null() || node_namespace.is_null() {
2048 return HddsError::HddsInvalidArgument;
2049 }
2050
2051 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2052 let Ok(name_str) = CStr::from_ptr(node_name).to_str() else {
2053 return HddsError::HddsInvalidArgument;
2054 };
2055 let Ok(namespace_str) = CStr::from_ptr(node_namespace).to_str() else {
2056 return HddsError::HddsInvalidArgument;
2057 };
2058
2059 let mut error: Option<HddsError> = None;
2060 let visit_result = ctx_ref.visit_subscriptions_with(name_str, namespace_str, |endpoint| {
2061 if error.is_some() {
2062 return;
2063 }
2064
2065 if let Some(cb) = visitor {
2066 let Ok(topic_cstr) = CString::new(endpoint.topic.as_str()) else {
2067 error = Some(HddsError::HddsInvalidArgument);
2068 return;
2069 };
2070 let type_name = normalize_ros_type_name(endpoint.type_name.as_str());
2071 let Ok(type_cstr) = CString::new(type_name.as_str()) else {
2072 error = Some(HddsError::HddsInvalidArgument);
2073 return;
2074 };
2075
2076 let qos_profile = HddsRmwQosProfile::from(&endpoint.qos);
2077 unsafe {
2078 cb(
2079 topic_cstr.as_ptr(),
2080 type_cstr.as_ptr(),
2081 endpoint.gid.as_ptr(),
2082 &qos_profile,
2083 user_data,
2084 );
2085 }
2086 }
2087 });
2088
2089 match visit_result {
2090 Ok((version, count)) => {
2091 if !out_version.is_null() {
2092 out_version.write(version);
2093 }
2094 if !out_count.is_null() {
2095 out_count.write(count);
2096 }
2097
2098 if let Some(err) = error {
2099 err
2100 } else {
2101 HddsError::HddsOk
2102 }
2103 }
2104 Err(err) => map_api_error(err),
2105 }
2106}
2107#[cfg(feature = "rmw")]
2111#[no_mangle]
2112pub unsafe extern "C" fn hdds_rmw_context_for_each_topic(
2113 ctx: *mut HddsRmwContext,
2114 visitor: Option<HddsTopicVisitor>,
2115 user_data: *mut c_void,
2116 out_version: *mut u64,
2117) -> HddsError {
2118 if ctx.is_null() {
2119 return HddsError::HddsInvalidArgument;
2120 }
2121
2122 let Some(callback) = visitor else {
2123 return HddsError::HddsInvalidArgument;
2124 };
2125
2126 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2127 let mut status = HddsError::HddsOk;
2128 let version = ctx_ref.for_each_topic(|topic, type_name, writers, readers| {
2129 if status != HddsError::HddsOk {
2130 return;
2131 }
2132
2133 let topic_c = match CString::new(topic) {
2134 Ok(value) => value,
2135 Err(_) => {
2136 status = HddsError::HddsInvalidArgument;
2137 return;
2138 }
2139 };
2140
2141 let type_c = match CString::new(type_name) {
2142 Ok(value) => value,
2143 Err(_) => {
2144 status = HddsError::HddsInvalidArgument;
2145 return;
2146 }
2147 };
2148
2149 unsafe {
2150 callback(
2151 topic_c.as_ptr(),
2152 type_c.as_ptr(),
2153 writers,
2154 readers,
2155 user_data,
2156 );
2157 }
2158 });
2159
2160 if !out_version.is_null() {
2161 out_version.write(version);
2162 }
2163
2164 status
2165}
2166#[cfg(feature = "rmw")]
2170#[no_mangle]
2171pub unsafe extern "C" fn hdds_rmw_context_for_each_user_locator(
2172 ctx: *mut HddsRmwContext,
2173 visitor: Option<HddsLocatorVisitor>,
2174 user_data: *mut c_void,
2175 out_count: *mut usize,
2176) -> HddsError {
2177 if ctx.is_null() {
2178 return HddsError::HddsInvalidArgument;
2179 }
2180
2181 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2182 let locators = ctx_ref.user_unicast_locators();
2183 if !out_count.is_null() {
2184 out_count.write(locators.len());
2185 }
2186
2187 let Some(callback) = visitor else {
2188 return HddsError::HddsOk;
2189 };
2190
2191 for locator in locators {
2192 let addr = locator.ip().to_string();
2193 let Ok(addr_cstr) = CString::new(addr.as_str()) else {
2194 return HddsError::HddsInvalidArgument;
2195 };
2196 callback(addr_cstr.as_ptr(), locator.port(), user_data);
2197 }
2198
2199 HddsError::HddsOk
2200}
2201#[cfg(feature = "rmw")]
2205#[no_mangle]
2206pub unsafe extern "C" fn hdds_rmw_context_publish(
2207 ctx: *mut HddsRmwContext,
2208 writer: *mut HddsDataWriter,
2209 type_support: *const rosidl_message_type_support_t,
2210 ros_message: *const c_void,
2211) -> HddsError {
2212 if ctx.is_null() || writer.is_null() || type_support.is_null() || ros_message.is_null() {
2213 return HddsError::HddsInvalidArgument;
2214 }
2215
2216 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2217 let payload = match serialize_from_ros(type_support, ros_message) {
2218 Ok(p) => Some(p),
2219 Err(ApiError::Config) => None,
2220 Err(err) => return map_api_error(err),
2221 };
2222
2223 if let Some(payload) = payload {
2224 match ctx_ref.publish_writer(writer.cast::<c_void>(), &payload) {
2225 Ok(()) => HddsError::HddsOk,
2226 Err(err) => map_api_error(err),
2227 }
2228 } else {
2229 HddsError::HddsOk
2230 }
2231}
2232#[cfg(feature = "rmw")]
2236#[no_mangle]
2237pub unsafe extern "C" fn hdds_rmw_context_publish_with_codec(
2238 ctx: *mut HddsRmwContext,
2239 writer: *mut HddsDataWriter,
2240 codec_kind: u8,
2241 ros_message: *const c_void,
2242) -> HddsError {
2243 if ctx.is_null() || writer.is_null() || ros_message.is_null() {
2244 return HddsError::HddsInvalidArgument;
2245 }
2246
2247 let Some(codec) = Ros2CodecKind::try_from(codec_kind) else {
2248 return HddsError::HddsInvalidArgument;
2249 };
2250
2251 if codec == Ros2CodecKind::None {
2252 return HddsError::HddsInvalidArgument;
2253 }
2254
2255 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2256 match encode_special(codec, ros_message) {
2257 Ok(Some(payload)) => match ctx_ref.publish_writer(writer.cast(), &payload) {
2258 Ok(()) => HddsError::HddsOk,
2259 Err(err) => {
2260 if matches!(err, ApiError::WouldBlock) {
2262 HddsError::HddsOk
2263 } else {
2264 map_api_error(err)
2265 }
2266 }
2267 },
2268 Ok(None) => HddsError::HddsOperationFailed,
2269 Err(err) => map_api_error(err),
2270 }
2271}
2272
2273#[cfg(feature = "rmw")]
2284#[no_mangle]
2285pub unsafe extern "C" fn hdds_rmw_context_shm_try_take(
2286 ctx: *mut HddsRmwContext,
2287 topic: *const c_char,
2288 data_out: *mut c_void,
2289 max_len: usize,
2290 len_out: *mut usize,
2291) -> HddsError {
2292 if ctx.is_null() || topic.is_null() || data_out.is_null() || len_out.is_null() {
2293 return HddsError::HddsInvalidArgument;
2294 }
2295
2296 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2297 let topic_str = match CStr::from_ptr(topic).to_str() {
2298 Ok(s) => s,
2299 Err(_) => return HddsError::HddsInvalidArgument,
2300 };
2301
2302 let buf = std::slice::from_raw_parts_mut(data_out.cast::<u8>(), max_len);
2303 match ctx_ref.try_shm_take(topic_str, buf) {
2304 Some(len) => {
2305 *len_out = len;
2306 HddsError::HddsOk
2307 }
2308 None => {
2309 *len_out = 0;
2310 HddsError::HddsNotFound
2311 }
2312 }
2313}
2314
2315#[cfg(feature = "rmw")]
2323#[no_mangle]
2324pub unsafe extern "C" fn hdds_rmw_context_shm_has_data(
2325 ctx: *mut HddsRmwContext,
2326 topic: *const c_char,
2327) -> bool {
2328 if ctx.is_null() || topic.is_null() {
2329 return false;
2330 }
2331
2332 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2333 let Ok(topic_str) = CStr::from_ptr(topic).to_str() else {
2334 return false;
2335 };
2336
2337 ctx_ref.shm_has_data(topic_str)
2338}
2339#[cfg(feature = "rmw")]
2343#[no_mangle]
2344pub unsafe extern "C" fn hdds_rmw_deserialize_with_codec(
2345 codec_kind: u8,
2346 data: *const u8,
2347 data_len: usize,
2348 ros_message: *mut c_void,
2349) -> HddsError {
2350 if ros_message.is_null() {
2351 return HddsError::HddsInvalidArgument;
2352 }
2353
2354 let Some(codec) = Ros2CodecKind::try_from(codec_kind) else {
2355 return HddsError::HddsInvalidArgument;
2356 };
2357
2358 if codec == Ros2CodecKind::None {
2359 return HddsError::HddsInvalidArgument;
2360 }
2361
2362 let slice = if data_len == 0 {
2363 &[]
2364 } else if data.is_null() {
2365 return HddsError::HddsInvalidArgument;
2366 } else {
2367 slice::from_raw_parts(data, data_len)
2368 };
2369
2370 match decode_special(codec, slice, ros_message) {
2371 Ok(true) => HddsError::HddsOk,
2372 Ok(false) => HddsError::HddsOperationFailed,
2373 Err(err) => map_api_error(err),
2374 }
2375}
2376
2377#[cfg(feature = "rmw")]
2383#[no_mangle]
2384pub unsafe extern "C" fn hdds_rmw_has_type_descriptor(type_name: *const c_char) -> bool {
2385 if type_name.is_null() {
2386 return false;
2387 }
2388
2389 let type_str = match CStr::from_ptr(type_name).to_str() {
2390 Ok(s) => s,
2391 Err(_) => return false,
2392 };
2393
2394 ros2_type_to_descriptor(type_str).is_some()
2395}
2396
2397#[cfg(feature = "rmw")]
2403#[no_mangle]
2404pub unsafe extern "C" fn hdds_rmw_deserialize_dynamic(
2405 type_name: *const c_char,
2406 data: *const u8,
2407 data_len: usize,
2408 ros_message: *mut c_void,
2409) -> HddsError {
2410 if type_name.is_null() || ros_message.is_null() {
2411 return HddsError::HddsInvalidArgument;
2412 }
2413
2414 let type_str = match CStr::from_ptr(type_name).to_str() {
2415 Ok(s) => s,
2416 Err(_) => return HddsError::HddsInvalidArgument,
2417 };
2418
2419 let slice = if data_len == 0 {
2420 &[]
2421 } else if data.is_null() {
2422 return HddsError::HddsInvalidArgument;
2423 } else {
2424 slice::from_raw_parts(data, data_len)
2425 };
2426
2427 match deserialize_dynamic_to_ros(type_str, slice, ros_message) {
2428 Ok(()) => HddsError::HddsOk,
2429 Err(_) => HddsError::HddsOperationFailed,
2430 }
2431}
2432
2433#[cfg(feature = "rmw")]
2438#[no_mangle]
2439pub unsafe extern "C" fn hdds_rmw_context_detach_condition(
2440 ctx: *mut HddsRmwContext,
2441 key: u64,
2442) -> HddsError {
2443 if ctx.is_null() {
2444 return HddsError::HddsInvalidArgument;
2445 }
2446
2447 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2448 match ctx_ref.detach_condition(key) {
2449 Ok(()) => HddsError::HddsOk,
2450 Err(err) => map_api_error(err),
2451 }
2452}
2453
2454#[cfg(feature = "rmw")]
2459#[no_mangle]
2460pub unsafe extern "C" fn hdds_rmw_context_detach_reader(
2461 ctx: *mut HddsRmwContext,
2462 reader: *mut HddsDataReader,
2463) -> HddsError {
2464 if ctx.is_null() || reader.is_null() {
2465 return HddsError::HddsInvalidArgument;
2466 }
2467
2468 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2469 match ctx_ref.as_ref().detach_reader(reader.cast::<c_void>()) {
2470 Ok(()) => HddsError::HddsOk,
2471 Err(err) => map_api_error(err),
2472 }
2473}
2474
2475#[cfg(feature = "rmw")]
2480#[no_mangle]
2481pub unsafe extern "C" fn hdds_rmw_context_wait(
2482 ctx: *mut HddsRmwContext,
2483 timeout_ns: i64,
2484 out_keys: *mut u64,
2485 out_conditions: *mut *const c_void,
2486 max_conditions: usize,
2487 out_len: *mut usize,
2488) -> HddsError {
2489 if ctx.is_null() || out_len.is_null() || out_keys.is_null() || out_conditions.is_null() {
2490 return HddsError::HddsInvalidArgument;
2491 }
2492
2493 *out_len = 0;
2494
2495 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2496 let ctx_ref = Arc::clone(ctx_ref);
2497 let timeout = if timeout_ns < 0 {
2498 None
2499 } else {
2500 match u64::try_from(timeout_ns) {
2501 Ok(nanos) => Some(Duration::from_nanos(nanos)),
2502 Err(_) => None,
2503 }
2504 };
2505
2506 let hits = match ctx_ref.wait(timeout) {
2507 Ok(list) => list,
2508 Err(err) => return map_api_error(err),
2509 };
2510
2511 if hits.len() > max_conditions {
2512 return HddsError::HddsOperationFailed;
2513 }
2514
2515 for (idx, hit) in hits.iter().enumerate() {
2516 *out_keys.add(idx) = hit.key;
2517 *out_conditions.add(idx) = hit.condition_ptr;
2518 }
2519
2520 *out_len = hits.len();
2521 HddsError::HddsOk
2522}
2523
2524#[cfg(feature = "rmw")]
2529#[no_mangle]
2530pub unsafe extern "C" fn hdds_rmw_context_wait_readers(
2531 ctx: *mut HddsRmwContext,
2532 timeout_ns: i64,
2533 out_readers: *mut *mut HddsDataReader,
2534 max_readers: usize,
2535 out_len: *mut usize,
2536 out_guard_triggered: *mut bool,
2537) -> HddsError {
2538 if ctx.is_null() || out_readers.is_null() || out_len.is_null() {
2539 return HddsError::HddsInvalidArgument;
2540 }
2541
2542 if !out_guard_triggered.is_null() {
2543 *out_guard_triggered = false;
2544 }
2545
2546 *out_len = 0;
2547
2548 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2549 let ctx_ref = Arc::clone(ctx_ref);
2550 let timeout = if timeout_ns < 0 {
2551 None
2552 } else {
2553 match u64::try_from(timeout_ns) {
2554 Ok(nanos) => Some(Duration::from_nanos(nanos)),
2555 Err(_) => None,
2556 }
2557 };
2558
2559 let hits = match ctx_ref.wait(timeout) {
2560 Ok(list) => list,
2561 Err(err) => return map_api_error(err),
2562 };
2563
2564 let mut reader_count = 0usize;
2565
2566 for hit in hits {
2567 if hit.key == ctx_ref.graph_guard_key() {
2568 if !out_guard_triggered.is_null() {
2569 *out_guard_triggered = true;
2570 }
2571 continue;
2572 }
2573
2574 if let Some(reader_ptr) = hit.reader_ptr {
2575 if reader_count >= max_readers {
2576 return HddsError::HddsOperationFailed;
2577 }
2578 *out_readers.add(reader_count) =
2579 reader_ptr.cast::<HddsDataReader>() as *mut HddsDataReader;
2580 reader_count += 1;
2581 }
2582 }
2583
2584 *out_len = reader_count;
2585 HddsError::HddsOk
2586}
2587
2588#[cfg(feature = "rmw")]
2593#[no_mangle]
2594pub unsafe extern "C" fn hdds_rmw_waitset_create(ctx: *mut HddsRmwContext) -> *mut HddsRmwWaitSet {
2595 if ctx.is_null() {
2596 return ptr::null_mut();
2597 }
2598
2599 let ctx_ref = &*ctx.cast::<Arc<ForeignRmwContext>>();
2600 let waitset = ForeignRmwWaitSet::new(Arc::clone(ctx_ref));
2601 Box::into_raw(Box::new(waitset)).cast::<HddsRmwWaitSet>()
2602}
2603
2604#[cfg(feature = "rmw")]
2609#[no_mangle]
2610pub unsafe extern "C" fn hdds_rmw_waitset_destroy(waitset: *mut HddsRmwWaitSet) {
2611 if !waitset.is_null() {
2612 let boxed = Box::from_raw(waitset.cast::<ForeignRmwWaitSet>());
2613 boxed.detach_all();
2614 }
2615}
2616
2617#[cfg(feature = "rmw")]
2622#[no_mangle]
2623pub unsafe extern "C" fn hdds_rmw_waitset_attach_reader(
2624 waitset: *mut HddsRmwWaitSet,
2625 reader: *mut HddsDataReader,
2626) -> HddsError {
2627 if waitset.is_null() || reader.is_null() {
2628 return HddsError::HddsInvalidArgument;
2629 }
2630
2631 let waitset_ref = &*waitset.cast::<ForeignRmwWaitSet>();
2632 match waitset_ref.attach_reader(reader.cast::<c_void>()) {
2633 Ok(()) => HddsError::HddsOk,
2634 Err(err) => map_api_error(err),
2635 }
2636}
2637
2638#[cfg(feature = "rmw")]
2643#[no_mangle]
2644pub unsafe extern "C" fn hdds_rmw_waitset_detach_reader(
2645 waitset: *mut HddsRmwWaitSet,
2646 reader: *mut HddsDataReader,
2647) -> HddsError {
2648 if waitset.is_null() || reader.is_null() {
2649 return HddsError::HddsInvalidArgument;
2650 }
2651
2652 let waitset_ref = &*waitset.cast::<ForeignRmwWaitSet>();
2653 match waitset_ref.detach_reader(reader.cast::<c_void>()) {
2654 Ok(()) => HddsError::HddsOk,
2655 Err(err) => map_api_error(err),
2656 }
2657}
2658
2659#[cfg(feature = "rmw")]
2664#[no_mangle]
2665pub unsafe extern "C" fn hdds_rmw_waitset_wait(
2666 waitset: *mut HddsRmwWaitSet,
2667 timeout_ns: i64,
2668 out_readers: *mut *mut HddsDataReader,
2669 max_readers: usize,
2670 out_len: *mut usize,
2671 out_guard_triggered: *mut bool,
2672) -> HddsError {
2673 if waitset.is_null() || out_len.is_null() {
2675 return HddsError::HddsInvalidArgument;
2676 }
2677 if out_readers.is_null() && max_readers > 0 {
2678 return HddsError::HddsInvalidArgument;
2679 }
2680
2681 if !out_guard_triggered.is_null() {
2682 *out_guard_triggered = false;
2683 }
2684
2685 *out_len = 0;
2686
2687 let waitset_ref = &*waitset.cast::<ForeignRmwWaitSet>();
2688 let timeout = if timeout_ns < 0 {
2689 None
2690 } else {
2691 match u64::try_from(timeout_ns) {
2692 Ok(nanos) => Some(Duration::from_nanos(nanos)),
2693 Err(_) => None,
2694 }
2695 };
2696
2697 let (readers, guard_hit) = match waitset_ref.wait(timeout) {
2698 Ok(result) => result,
2699 Err(err) => return map_api_error(err),
2700 };
2701
2702 if readers.len() > max_readers {
2703 return HddsError::HddsOperationFailed;
2704 }
2705
2706 for (idx, reader_ptr) in readers.iter().enumerate() {
2707 *out_readers.add(idx) = (*reader_ptr).cast::<HddsDataReader>() as *mut HddsDataReader;
2708 }
2709
2710 if !out_guard_triggered.is_null() {
2711 *out_guard_triggered = guard_hit;
2712 }
2713
2714 *out_len = readers.len();
2715 HddsError::HddsOk
2716}
2717
2718#[cfg(test)]
2719mod tests {
2720 #![allow(clippy::all)]
2721 use super::*;
2722 use std::ffi::CString;
2723 use std::ptr;
2724
2725 #[cfg(feature = "xtypes")]
2726 use hdds::core::types::ROS_HASH_SIZE;
2727 #[cfg(feature = "xtypes")]
2728 use hdds::xtypes::{
2729 rosidl_message_type_support_t, rosidl_type_hash_t,
2730 rosidl_typesupport_introspection_c__MessageMember,
2731 rosidl_typesupport_introspection_c__MessageMembers,
2732 };
2733
2734 #[cfg(feature = "xtypes")]
2735 #[allow(clippy::cast_possible_truncation)]
2736 const fn hash_bytes() -> [u8; ROS_HASH_SIZE] {
2737 let mut arr = [0u8; ROS_HASH_SIZE];
2738 let mut i = 0;
2739 while i < ROS_HASH_SIZE {
2740 arr[i] = i as u8;
2741 i += 1;
2742 }
2743 arr
2744 }
2745
2746 #[cfg(feature = "xtypes")]
2747 const HASH_BYTES: [u8; ROS_HASH_SIZE] = hash_bytes();
2748
2749 #[cfg(feature = "xtypes")]
2750 static HASH: rosidl_type_hash_t = rosidl_type_hash_t {
2751 version: 1,
2752 value: hash_bytes(),
2753 };
2754
2755 #[cfg(feature = "xtypes")]
2756 unsafe extern "C" fn stub_hash(
2757 _: *const rosidl_message_type_support_t,
2758 ) -> *const rosidl_type_hash_t {
2759 std::ptr::from_ref(&HASH)
2760 }
2761
2762 #[test]
2763 fn test_participant_create_destroy() {
2764 unsafe {
2765 let name = CString::new("test_participant").unwrap();
2766 let participant = hdds_participant_create(name.as_ptr());
2767 assert!(!participant.is_null());
2768 hdds_participant_destroy(participant);
2769 }
2770 }
2771
2772 #[cfg(feature = "rmw")]
2773 #[test]
2774 fn test_rmw_context_graph_guard_wait() {
2775 unsafe {
2776 let name = CString::new("rmw_ctx_guard").unwrap();
2777 let ctx = hdds_rmw_context_create(name.as_ptr());
2778 assert!(!ctx.is_null());
2779
2780 let guard = hdds_rmw_context_graph_guard_condition(ctx);
2781 assert!(!guard.is_null());
2782
2783 hdds_guard_condition_set_trigger(guard, true);
2784
2785 let mut keys = [0u64; 4];
2786 let mut ptrs = [ptr::null(); 4];
2787 let mut len = 0usize;
2788
2789 let ret = hdds_rmw_context_wait(
2790 ctx,
2791 1_000_000,
2792 keys.as_mut_ptr(),
2793 ptrs.as_mut_ptr(),
2794 keys.len(),
2795 &mut len,
2796 );
2797
2798 assert_eq!(ret, HddsError::HddsOk);
2799 assert_eq!(len, 1);
2800 assert_eq!(keys[0], hdds_rmw_context_graph_guard_key(ctx));
2801 assert_eq!(ptrs[0], guard.cast());
2802
2803 let mut readers = [ptr::null_mut(); 4];
2804 let mut reader_len = 0usize;
2805 let mut guard_hit = false;
2806 let ret = hdds_rmw_context_wait_readers(
2807 ctx,
2808 1,
2809 readers.as_mut_ptr(),
2810 readers.len(),
2811 &mut reader_len,
2812 &mut guard_hit,
2813 );
2814 assert_eq!(ret, HddsError::HddsOk);
2815 assert_eq!(reader_len, 0);
2816 assert!(guard_hit);
2817
2818 hdds_guard_condition_release(guard);
2819 hdds_rmw_context_destroy(ctx);
2820 }
2821 }
2822
2823 #[cfg(feature = "rmw")]
2824 #[test]
2825 fn test_rmw_context_attach_reader_lifecycle() {
2826 unsafe {
2827 let ctx_name = CString::new("rmw_ctx_reader").unwrap();
2828 let ctx = hdds_rmw_context_create(ctx_name.as_ptr());
2829 assert!(!ctx.is_null());
2830
2831 let topic = CString::new("rmw_ctx_reader_topic").unwrap();
2832 let mut reader = ptr::null_mut();
2833 let create_ret = hdds_rmw_context_create_reader(ctx, topic.as_ptr(), &mut reader);
2834 assert_eq!(create_ret, HddsError::HddsOk);
2835 assert!(!reader.is_null());
2836
2837 let mut key = 0u64;
2838 let ret = hdds_rmw_context_attach_reader(ctx, reader, &mut key);
2839 assert_eq!(ret, HddsError::HddsOk);
2840 assert_ne!(key, 0);
2841
2842 let ret_dup = hdds_rmw_context_attach_reader(ctx, reader, &mut key);
2844 assert_eq!(ret_dup, HddsError::HddsInvalidArgument);
2845
2846 let ret = hdds_rmw_context_detach_reader(ctx, reader);
2848 assert_eq!(ret, HddsError::HddsOk);
2849
2850 let mut new_key = 0u64;
2851 let ret = hdds_rmw_context_attach_reader(ctx, reader, &mut new_key);
2852 assert_eq!(ret, HddsError::HddsOk);
2853 assert_ne!(new_key, 0);
2854
2855 let ret = hdds_rmw_context_detach_reader(ctx, reader);
2856 assert_eq!(ret, HddsError::HddsOk);
2857
2858 let destroy_ret = hdds_rmw_context_destroy_reader(ctx, reader);
2859 assert_eq!(destroy_ret, HddsError::HddsOk);
2860 hdds_rmw_context_destroy(ctx);
2861 }
2862 }
2863
2864 #[cfg(feature = "rmw")]
2865 #[test]
2866 fn test_rmw_waitset_basic_flow() {
2867 unsafe {
2868 let ctx_name = CString::new("rmw_waitset_ctx").unwrap();
2869 let ctx = hdds_rmw_context_create(ctx_name.as_ptr());
2870 assert!(!ctx.is_null());
2871
2872 let topic = CString::new("rmw_waitset_topic").unwrap();
2873 let mut reader = ptr::null_mut();
2874 let create_ret = hdds_rmw_context_create_reader(ctx, topic.as_ptr(), &mut reader);
2875 assert_eq!(create_ret, HddsError::HddsOk);
2876 assert!(!reader.is_null());
2877
2878 let waitset = hdds_rmw_waitset_create(ctx);
2879 assert!(!waitset.is_null());
2880
2881 let guard = hdds_rmw_context_graph_guard_condition(ctx);
2882 hdds_guard_condition_set_trigger(guard, true);
2883
2884 let mut readers = [ptr::null_mut(); 4];
2885 let mut reader_len = 0usize;
2886 let mut guard_hit = false;
2887 let ret = hdds_rmw_waitset_wait(
2888 waitset,
2889 1_000_000,
2890 readers.as_mut_ptr(),
2891 readers.len(),
2892 &mut reader_len,
2893 &mut guard_hit,
2894 );
2895 assert_eq!(ret, HddsError::HddsOk);
2896 assert_eq!(reader_len, 0);
2897 assert!(guard_hit);
2898
2899 let attach_ret = hdds_rmw_waitset_attach_reader(waitset, reader);
2900 assert_eq!(attach_ret, HddsError::HddsOk);
2901
2902 let detach_ret = hdds_rmw_waitset_detach_reader(waitset, reader);
2903 assert_eq!(detach_ret, HddsError::HddsOk);
2904
2905 hdds_guard_condition_release(guard);
2906 hdds_rmw_waitset_destroy(waitset);
2907 let destroy_ret = hdds_rmw_context_destroy_reader(ctx, reader);
2908 assert_eq!(destroy_ret, HddsError::HddsOk);
2909 hdds_rmw_context_destroy(ctx);
2910 }
2911 }
2912
2913 #[test]
2914 fn test_participant_graph_guard_waitset_integration() {
2915 unsafe {
2916 let name = CString::new("graph_guard").unwrap();
2917 let participant = hdds_participant_create(name.as_ptr());
2918 assert!(!participant.is_null());
2919
2920 let guard = hdds_participant_graph_guard_condition(participant);
2921 assert!(!guard.is_null());
2922
2923 let waitset = hdds_waitset_create();
2924 assert!(!waitset.is_null());
2925
2926 assert_eq!(
2927 hdds_waitset_attach_guard_condition(waitset, guard),
2928 HddsError::HddsOk
2929 );
2930
2931 hdds_guard_condition_set_trigger(guard, true);
2932
2933 let mut triggered = [ptr::null(); 4];
2934 let mut len = 0usize;
2935 let ret = hdds_waitset_wait(
2936 waitset,
2937 1_000_000,
2938 triggered.as_mut_ptr(),
2939 triggered.len(),
2940 &mut len,
2941 );
2942 assert_eq!(ret, HddsError::HddsOk);
2943 assert_eq!(len, 1);
2944 assert_eq!(triggered[0], guard.cast());
2945
2946 assert_eq!(
2947 hdds_waitset_detach_condition(waitset, guard.cast()),
2948 HddsError::HddsOk
2949 );
2950 hdds_waitset_destroy(waitset);
2951 hdds_guard_condition_release(guard);
2952 hdds_participant_destroy(participant);
2953 }
2954 }
2955
2956 #[test]
2957 #[cfg(feature = "xtypes")]
2958 fn test_register_type_support_and_hash() {
2959 unsafe {
2960 let members_vec = vec![
2961 rosidl_typesupport_introspection_c__MessageMember {
2962 name_: b"x\0".as_ptr().cast(),
2963 type_id_: 1,
2964 string_upper_bound_: 0,
2965 members_: ptr::null(),
2966 is_array_: false,
2967 array_size_: 0,
2968 is_upper_bound_: false,
2969 offset_: 0,
2970 default_value_: ptr::null(),
2971 size_function: None,
2972 get_const_function: None,
2973 get_function: None,
2974 fetch_function: None,
2975 assign_function: None,
2976 resize_function: None,
2977 },
2978 rosidl_typesupport_introspection_c__MessageMember {
2979 name_: b"labels\0".as_ptr().cast(),
2980 type_id_: 16,
2981 string_upper_bound_: 0,
2982 members_: ptr::null(),
2983 is_array_: true,
2984 array_size_: 0,
2985 is_upper_bound_: true,
2986 offset_: 0,
2987 default_value_: ptr::null(),
2988 size_function: None,
2989 get_const_function: None,
2990 get_function: None,
2991 fetch_function: None,
2992 assign_function: None,
2993 resize_function: None,
2994 },
2995 ];
2996
2997 let members_box = members_vec.into_boxed_slice();
2998 let members_ptr = members_box.as_ptr();
2999 let members_slice = Box::leak(members_box);
3000
3001 let descriptor = Box::leak(Box::new(
3002 rosidl_typesupport_introspection_c__MessageMembers {
3003 message_namespace_: b"test_pkg__msg\0".as_ptr().cast(),
3004 message_name_: b"TaggedPoint\0".as_ptr().cast(),
3005 member_count_: u32::try_from(members_slice.len())
3006 .expect("member count fits in u32"),
3007 size_of_: 0,
3008 members_: members_ptr,
3009 init_function: None,
3010 fini_function: None,
3011 },
3012 ));
3013
3014 let type_support = Box::leak(Box::new(rosidl_message_type_support_t {
3015 typesupport_identifier: b"rosidl_typesupport_introspection_c\0".as_ptr().cast(),
3016 data: std::ptr::from_ref(&*descriptor).cast::<c_void>(),
3017 func: None,
3018 get_type_hash_func: Some(stub_hash),
3019 get_type_description_func: None,
3020 get_type_description_sources_func: None,
3021 }));
3022
3023 let name = CString::new("ffi_register").unwrap();
3024 let participant = hdds_participant_create(name.as_ptr());
3025 assert!(!participant.is_null());
3026
3027 let mut handle: *const HddsTypeObject = ptr::null();
3028 let err = hdds_participant_register_type_support(
3029 participant,
3030 0,
3031 type_support,
3032 std::ptr::addr_of_mut!(handle),
3033 );
3034 assert_eq!(err, HddsError::HddsOk);
3035 assert!(!handle.is_null());
3036
3037 let mut version = 0u8;
3038 let mut buf = [0u8; ROS_HASH_SIZE];
3039 let err = hdds_type_object_hash(
3040 handle,
3041 std::ptr::addr_of_mut!(version),
3042 buf.as_mut_ptr(),
3043 buf.len(),
3044 );
3045 assert_eq!(err, HddsError::HddsOk);
3046 assert_eq!(version, 1);
3047 assert_eq!(buf, HASH_BYTES);
3048
3049 hdds_type_object_release(handle);
3050 hdds_participant_destroy(participant);
3051 }
3052 }
3053
3054 #[test]
3055 fn test_participant_create_null_name() {
3056 unsafe {
3057 let participant = hdds_participant_create(ptr::null());
3058 assert!(participant.is_null());
3059 }
3060 }
3061
3062 #[test]
3063 fn test_writer_reader_lifecycle() {
3064 unsafe {
3065 let name = CString::new("test_participant_ffi").unwrap();
3067 let participant = hdds_participant_create(name.as_ptr());
3068 assert!(!participant.is_null());
3069
3070 let topic = CString::new("test_topic_ffi").unwrap();
3072 let writer = hdds_writer_create(participant, topic.as_ptr());
3073 let reader = hdds_reader_create(participant, topic.as_ptr());
3074 assert!(!writer.is_null());
3075 assert!(!reader.is_null());
3076
3077 hdds_writer_destroy(writer);
3083 hdds_reader_destroy(reader);
3084 hdds_participant_destroy(participant);
3085 }
3086 }
3087
3088 #[test]
3089 fn test_writer_write_null_checks() {
3090 unsafe {
3091 let test_data = b"test";
3093 let result = hdds_writer_write(
3094 ptr::null_mut(),
3095 test_data.as_ptr().cast::<c_void>(),
3096 test_data.len(),
3097 );
3098 assert_eq!(result, HddsError::HddsInvalidArgument);
3099
3100 let name = CString::new("test_participant").unwrap();
3102 let participant = hdds_participant_create(name.as_ptr());
3103 let topic = CString::new("test_topic").unwrap();
3104 let writer = hdds_writer_create(participant, topic.as_ptr());
3105
3106 let result = hdds_writer_write(writer, ptr::null(), 10);
3107 assert_eq!(result, HddsError::HddsInvalidArgument);
3108
3109 hdds_writer_destroy(writer);
3110 hdds_participant_destroy(participant);
3111 }
3112 }
3113}