use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;
use hdds::api::QoS;
use crate::HddsError;
#[repr(C)]
pub struct HddsQoS {
_private: [u8; 0],
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_default() -> *mut HddsQoS {
let qos = Box::new(QoS::default());
Box::into_raw(qos).cast::<HddsQoS>()
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_best_effort() -> *mut HddsQoS {
let qos = Box::new(QoS::best_effort());
Box::into_raw(qos).cast::<HddsQoS>()
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_reliable() -> *mut HddsQoS {
let qos = Box::new(QoS::reliable());
Box::into_raw(qos).cast::<HddsQoS>()
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_rti_defaults() -> *mut HddsQoS {
let qos = Box::new(QoS::rti_defaults());
Box::into_raw(qos).cast::<HddsQoS>()
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_destroy(qos: *mut HddsQoS) {
if !qos.is_null() {
let _ = Box::from_raw(qos.cast::<QoS>());
}
}
#[cfg(feature = "qos-loaders")]
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_load_fastdds_xml(path: *const c_char) -> *mut HddsQoS {
if path.is_null() {
return ptr::null_mut();
}
let Ok(path_str) = CStr::from_ptr(path).to_str() else {
return ptr::null_mut();
};
match QoS::load_fastdds(path_str) {
Ok(qos) => Box::into_raw(Box::new(qos)).cast::<HddsQoS>(),
Err(err) => {
eprintln!("[hdds_c] Failed to load FastDDS XML: {}", err);
ptr::null_mut()
}
}
}
#[cfg(feature = "qos-loaders")]
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_from_xml(path: *const c_char) -> *mut HddsQoS {
if path.is_null() {
return ptr::null_mut();
}
let Ok(path_str) = CStr::from_ptr(path).to_str() else {
return ptr::null_mut();
};
match QoS::from_xml(path_str) {
Ok(qos) => Box::into_raw(Box::new(qos)).cast::<HddsQoS>(),
Err(err) => {
eprintln!("[hdds_c] Failed to load QoS from XML: {}", err);
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_history_depth(qos: *mut HddsQoS, depth: u32) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.history = hdds::dds::qos::History::KeepLast(depth);
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_history_keep_all(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.history = hdds::dds::qos::History::KeepAll;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_volatile(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.durability = hdds::dds::qos::Durability::Volatile;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_transient_local(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.durability = hdds::dds::qos::Durability::TransientLocal;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_persistent(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.durability = hdds::dds::qos::Durability::Persistent;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_reliable(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.reliability = hdds::dds::qos::Reliability::Reliable;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_best_effort(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.reliability = hdds::dds::qos::Reliability::BestEffort;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_deadline_ns(qos: *mut HddsQoS, period_ns: u64) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.deadline = hdds::dds::qos::Deadline::new(std::time::Duration::from_nanos(period_ns));
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_lifespan_ns(
qos: *mut HddsQoS,
duration_ns: u64,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.lifespan = hdds::dds::qos::Lifespan::new(std::time::Duration::from_nanos(duration_ns));
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_ownership_shared(qos: *mut HddsQoS) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.ownership = hdds::dds::qos::Ownership::shared();
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_ownership_exclusive(
qos: *mut HddsQoS,
strength: i32,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.ownership = hdds::dds::qos::Ownership::exclusive();
qos_ref.ownership_strength = hdds::dds::qos::OwnershipStrength::new(strength);
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_add_partition(
qos: *mut HddsQoS,
partition: *const c_char,
) -> HddsError {
if qos.is_null() || partition.is_null() {
return HddsError::HddsInvalidArgument;
}
let Ok(part_str) = CStr::from_ptr(partition).to_str() else {
return HddsError::HddsInvalidArgument;
};
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.partition.add(part_str);
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_is_reliable(qos: *const HddsQoS) -> bool {
if qos.is_null() {
return false;
}
let qos_ref = &*qos.cast::<QoS>();
matches!(qos_ref.reliability, hdds::dds::qos::Reliability::Reliable)
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_is_transient_local(qos: *const HddsQoS) -> bool {
if qos.is_null() {
return false;
}
let qos_ref = &*qos.cast::<QoS>();
matches!(
qos_ref.durability,
hdds::dds::qos::Durability::TransientLocal
)
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_history_depth(qos: *const HddsQoS) -> u32 {
if qos.is_null() {
return 0;
}
let qos_ref = &*qos.cast::<QoS>();
match qos_ref.history {
hdds::dds::qos::History::KeepLast(depth) => depth,
hdds::dds::qos::History::KeepAll => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_deadline_ns(qos: *const HddsQoS) -> u64 {
if qos.is_null() {
return u64::MAX;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.deadline.period.as_nanos() as u64
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_lifespan_ns(qos: *const HddsQoS) -> u64 {
if qos.is_null() {
return u64::MAX;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.lifespan.duration.as_nanos() as u64
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_is_ownership_exclusive(qos: *const HddsQoS) -> bool {
if qos.is_null() {
return false;
}
let qos_ref = &*qos.cast::<QoS>();
matches!(
qos_ref.ownership.kind,
hdds::dds::qos::OwnershipKind::Exclusive
)
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_ownership_strength(qos: *const HddsQoS) -> i32 {
if qos.is_null() {
return 0;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.ownership_strength.value
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(clippy::enum_variant_names)] pub enum HddsLivelinessKind {
HddsLivelinessAutomatic = 0,
HddsLivelinessManualByParticipant = 1,
HddsLivelinessManualByTopic = 2,
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_liveliness_kind(qos: *const HddsQoS) -> HddsLivelinessKind {
if qos.is_null() {
return HddsLivelinessKind::HddsLivelinessAutomatic;
}
let qos_ref = &*qos.cast::<QoS>();
match qos_ref.liveliness.kind {
hdds::dds::qos::LivelinessKind::Automatic => HddsLivelinessKind::HddsLivelinessAutomatic,
hdds::dds::qos::LivelinessKind::ManualByParticipant => {
HddsLivelinessKind::HddsLivelinessManualByParticipant
}
hdds::dds::qos::LivelinessKind::ManualByTopic => {
HddsLivelinessKind::HddsLivelinessManualByTopic
}
}
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_liveliness_lease_ns(qos: *const HddsQoS) -> u64 {
if qos.is_null() {
return u64::MAX;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.liveliness.lease_duration.as_nanos() as u64
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_time_based_filter_ns(qos: *const HddsQoS) -> u64 {
if qos.is_null() {
return 0;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.time_based_filter.minimum_separation.as_nanos() as u64
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_latency_budget_ns(qos: *const HddsQoS) -> u64 {
if qos.is_null() {
return 0;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.latency_budget.duration.as_nanos() as u64
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_transport_priority(qos: *const HddsQoS) -> i32 {
if qos.is_null() {
return 0;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.transport_priority.value
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_max_samples(qos: *const HddsQoS) -> usize {
if qos.is_null() {
return usize::MAX;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.resource_limits.max_samples
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_max_instances(qos: *const HddsQoS) -> usize {
if qos.is_null() {
return usize::MAX;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.resource_limits.max_instances
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_get_max_samples_per_instance(qos: *const HddsQoS) -> usize {
if qos.is_null() {
return usize::MAX;
}
let qos_ref = &*qos.cast::<QoS>();
qos_ref.resource_limits.max_samples_per_instance
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_liveliness_automatic_ns(
qos: *mut HddsQoS,
lease_ns: u64,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.liveliness =
hdds::dds::qos::Liveliness::automatic(std::time::Duration::from_nanos(lease_ns));
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_liveliness_manual_participant_ns(
qos: *mut HddsQoS,
lease_ns: u64,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.liveliness = hdds::dds::qos::Liveliness::manual_by_participant(
std::time::Duration::from_nanos(lease_ns),
);
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_liveliness_manual_topic_ns(
qos: *mut HddsQoS,
lease_ns: u64,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.liveliness =
hdds::dds::qos::Liveliness::manual_by_topic(std::time::Duration::from_nanos(lease_ns));
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_time_based_filter_ns(
qos: *mut HddsQoS,
min_separation_ns: u64,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.time_based_filter =
hdds::dds::qos::TimeBasedFilter::new(std::time::Duration::from_nanos(min_separation_ns));
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_latency_budget_ns(
qos: *mut HddsQoS,
budget_ns: u64,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.latency_budget =
hdds::dds::qos::LatencyBudget::new(std::time::Duration::from_nanos(budget_ns));
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_transport_priority(
qos: *mut HddsQoS,
priority: i32,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.transport_priority.value = priority;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_set_resource_limits(
qos: *mut HddsQoS,
max_samples: usize,
max_instances: usize,
max_samples_per_instance: usize,
) -> HddsError {
if qos.is_null() {
return HddsError::HddsInvalidArgument;
}
let qos_ref = &mut *qos.cast::<QoS>();
qos_ref.resource_limits.max_samples = max_samples;
qos_ref.resource_limits.max_instances = max_instances;
qos_ref.resource_limits.max_samples_per_instance = max_samples_per_instance;
HddsError::HddsOk
}
#[no_mangle]
pub unsafe extern "C" fn hdds_qos_clone(qos: *const HddsQoS) -> *mut HddsQoS {
if qos.is_null() {
return ptr::null_mut();
}
let qos_ref = &*qos.cast::<QoS>();
let cloned = Box::new(qos_ref.clone());
Box::into_raw(cloned).cast::<HddsQoS>()
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
#[test]
fn test_qos_creation_and_destroy() {
unsafe {
let qos = hdds_qos_default();
assert!(!qos.is_null());
hdds_qos_destroy(qos);
let qos = hdds_qos_reliable();
assert!(!qos.is_null());
assert!(hdds_qos_is_reliable(qos));
hdds_qos_destroy(qos);
let qos = hdds_qos_best_effort();
assert!(!qos.is_null());
assert!(!hdds_qos_is_reliable(qos));
hdds_qos_destroy(qos);
}
}
#[test]
fn test_qos_setters() {
unsafe {
let qos = hdds_qos_default();
assert_eq!(hdds_qos_set_history_depth(qos, 50), HddsError::HddsOk);
assert_eq!(hdds_qos_get_history_depth(qos), 50);
assert_eq!(hdds_qos_set_transient_local(qos), HddsError::HddsOk);
assert!(hdds_qos_is_transient_local(qos));
assert_eq!(hdds_qos_set_reliable(qos), HddsError::HddsOk);
assert!(hdds_qos_is_reliable(qos));
let part = CString::new("test_partition").unwrap();
assert_eq!(
hdds_qos_add_partition(qos, part.as_ptr()),
HddsError::HddsOk
);
hdds_qos_destroy(qos);
}
}
#[test]
fn test_qos_clone() {
unsafe {
let qos = hdds_qos_reliable();
hdds_qos_set_history_depth(qos, 42);
hdds_qos_set_transient_local(qos);
let cloned = hdds_qos_clone(qos);
assert!(!cloned.is_null());
assert!(hdds_qos_is_reliable(cloned));
assert!(hdds_qos_is_transient_local(cloned));
assert_eq!(hdds_qos_get_history_depth(cloned), 42);
hdds_qos_destroy(qos);
hdds_qos_destroy(cloned);
}
}
}