diff --git a/doc/release-notes/iceoryx2-unreleased.md b/doc/release-notes/iceoryx2-unreleased.md index 7489f1ffa..6dd00bea8 100644 --- a/doc/release-notes/iceoryx2-unreleased.md +++ b/doc/release-notes/iceoryx2-unreleased.md @@ -62,6 +62,7 @@ * Rename `iox2_publisher_loan` to `iox2_publisher_loan_slice_uninit` [#490](https://github.com/eclipse-iceoryx/iceoryx2/issues/490) 1. C always loans slices, for a single element, specify the `number_of_elements` to be 1 +* APIs to retrieve the value of `UniquePortIds` from the C/C++ bindings [#500](https://github.com/eclipse-iceoryx/iceoryx2/issues/500) ### API Breaking Changes diff --git a/iceoryx2-ffi/cxx/include/iox2/unique_port_id.hpp b/iceoryx2-ffi/cxx/include/iox2/unique_port_id.hpp index f4ffa7146..e640b7087 100644 --- a/iceoryx2-ffi/cxx/include/iox2/unique_port_id.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/unique_port_id.hpp @@ -13,9 +13,15 @@ #ifndef IOX2_UNIQUE_PORT_ID_HPP #define IOX2_UNIQUE_PORT_ID_HPP +#include "iox/optional.hpp" +#include "iox/vector.hpp" #include "iox2/internal/iceoryx2.hpp" namespace iox2 { + +constexpr uint64_t UNIQUE_PORT_ID_LENGTH = 16; +using RawIdType = iox::vector; + /// The system-wide unique id of a [`Publisher`]. class UniquePublisherId { public: @@ -25,6 +31,8 @@ class UniquePublisherId { auto operator=(UniquePublisherId&& rhs) noexcept -> UniquePublisherId&; ~UniquePublisherId(); + auto bytes() const -> const iox::optional&; + private: template friend class Publisher; @@ -36,9 +44,9 @@ class UniquePublisherId { void drop(); iox2_unique_publisher_id_h m_handle = nullptr; + mutable iox::optional m_raw_id; }; - /// The system-wide unique id of a [`Subscriber`]. class UniqueSubscriberId { public: @@ -48,6 +56,8 @@ class UniqueSubscriberId { auto operator=(UniqueSubscriberId&& rhs) noexcept -> UniqueSubscriberId&; ~UniqueSubscriberId(); + auto bytes() const -> const iox::optional&; + private: template friend class Subscriber; @@ -58,6 +68,7 @@ class UniqueSubscriberId { void drop(); iox2_unique_subscriber_id_h m_handle = nullptr; + mutable iox::optional m_raw_id; }; /// The system-wide unique id of a [`Notifier`]. @@ -69,6 +80,8 @@ class UniqueNotifierId { auto operator=(UniqueNotifierId&& rhs) noexcept -> UniqueNotifierId&; ~UniqueNotifierId(); + auto bytes() const -> const iox::optional&; + private: template friend class Notifier; @@ -79,6 +92,7 @@ class UniqueNotifierId { void drop(); iox2_unique_notifier_id_h m_handle = nullptr; + mutable iox::optional m_raw_id; }; /// The system-wide unique id of a [`Listener`]. @@ -90,6 +104,8 @@ class UniqueListenerId { auto operator=(UniqueListenerId&& rhs) noexcept -> UniqueListenerId&; ~UniqueListenerId(); + auto bytes() const -> const iox::optional&; + private: template friend class Listener; @@ -100,6 +116,7 @@ class UniqueListenerId { void drop(); iox2_unique_listener_id_h m_handle = nullptr; + mutable iox::optional m_raw_id; }; auto operator==(const UniquePublisherId& lhs, const UniquePublisherId& rhs) -> bool; diff --git a/iceoryx2-ffi/cxx/src/unique_port_id.cpp b/iceoryx2-ffi/cxx/src/unique_port_id.cpp index c8f8116f2..533cc95d6 100644 --- a/iceoryx2-ffi/cxx/src/unique_port_id.cpp +++ b/iceoryx2-ffi/cxx/src/unique_port_id.cpp @@ -43,6 +43,15 @@ UniquePublisherId::UniquePublisherId(iox2_unique_publisher_id_h handle) : m_handle { handle } { } +auto UniquePublisherId::bytes() const -> const iox::optional& { + if (!m_raw_id.has_value() && m_handle != nullptr) { + RawIdType bytes { UNIQUE_PORT_ID_LENGTH, 0 }; + iox2_unique_publisher_id_value(m_handle, bytes.data(), bytes.size()); + m_raw_id.emplace(std::move(bytes)); + } + return m_raw_id; +}; + void UniquePublisherId::drop() { if (m_handle != nullptr) { iox2_unique_publisher_id_drop(m_handle); @@ -81,6 +90,15 @@ UniqueSubscriberId::UniqueSubscriberId(iox2_unique_subscriber_id_h handle) : m_handle { handle } { } +auto UniqueSubscriberId::bytes() const -> const iox::optional& { + if (!m_raw_id.has_value() && m_handle != nullptr) { + RawIdType bytes { UNIQUE_PORT_ID_LENGTH, 0 }; + iox2_unique_subscriber_id_value(m_handle, bytes.data(), bytes.size()); + m_raw_id.emplace(std::move(bytes)); + } + return m_raw_id; +}; + void UniqueSubscriberId::drop() { if (m_handle != nullptr) { iox2_unique_subscriber_id_drop(m_handle); @@ -118,6 +136,15 @@ UniqueNotifierId::UniqueNotifierId(iox2_unique_notifier_id_h handle) : m_handle { handle } { } +auto UniqueNotifierId::bytes() const -> const iox::optional& { + if (!m_raw_id.has_value() && m_handle != nullptr) { + RawIdType bytes { UNIQUE_PORT_ID_LENGTH, 0 }; + iox2_unique_notifier_id_value(m_handle, bytes.data(), bytes.size()); + m_raw_id.emplace(std::move(bytes)); + } + return m_raw_id; +}; + void UniqueNotifierId::drop() { if (m_handle != nullptr) { iox2_unique_notifier_id_drop(m_handle); @@ -155,6 +182,15 @@ UniqueListenerId::UniqueListenerId(iox2_unique_listener_id_h handle) : m_handle { handle } { } +auto UniqueListenerId::bytes() const -> const iox::optional& { + if (!m_raw_id.has_value() && m_handle != nullptr) { + RawIdType bytes { UNIQUE_PORT_ID_LENGTH, 0 }; + iox2_unique_listener_id_value(m_handle, bytes.data(), bytes.size()); + m_raw_id.emplace(std::move(bytes)); + } + return m_raw_id; +}; + void UniqueListenerId::drop() { if (m_handle != nullptr) { iox2_unique_listener_id_drop(m_handle); diff --git a/iceoryx2-ffi/cxx/tests/src/unique_port_id_tests.cpp b/iceoryx2-ffi/cxx/tests/src/unique_port_id_tests.cpp index 939e4ec34..8a318d8ff 100644 --- a/iceoryx2-ffi/cxx/tests/src/unique_port_id_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/unique_port_id_tests.cpp @@ -17,10 +17,12 @@ #include "iox2/publisher.hpp" #include "iox2/service_name.hpp" #include "iox2/subscriber.hpp" +#include "iox2/unique_port_id.hpp" #include "test.hpp" #include +#include namespace { using namespace iox2; @@ -63,6 +65,26 @@ struct UniquePortIdTest : public ::testing::Test { TYPED_TEST_SUITE(UniquePortIdTest, iox2_testing::ServiceTypes); +TYPED_TEST(UniquePortIdTest, unique_port_id_value) { + auto null_id = iox::vector { iox2::UNIQUE_PORT_ID_LENGTH, 0 }; + + auto unique_publisher_id = this->publisher_1.id(); + ASSERT_TRUE(unique_publisher_id.bytes().has_value()); + ASSERT_NE(unique_publisher_id.bytes().value(), null_id); + + auto unique_subscriber_id = this->subscriber_1.id(); + ASSERT_TRUE(unique_subscriber_id.bytes().has_value()); + ASSERT_NE(unique_subscriber_id.bytes().value(), null_id); + + auto unique_notifier_id = this->notifier_1.id(); + ASSERT_TRUE(unique_notifier_id.bytes().has_value()); + ASSERT_NE(unique_notifier_id.bytes().value(), null_id); + + auto unique_listener_id = this->listener_1.id(); + ASSERT_TRUE(unique_listener_id.bytes().has_value()); + ASSERT_NE(unique_listener_id.bytes().value(), null_id); +} + TYPED_TEST(UniquePortIdTest, unique_port_id_from_same_port_is_equal) { ASSERT_TRUE(this->listener_1.id() == this->listener_1.id()); ASSERT_TRUE(this->notifier_1.id() == this->notifier_1.id()); diff --git a/iceoryx2-ffi/ffi/src/api/unique_listener_id.rs b/iceoryx2-ffi/ffi/src/api/unique_listener_id.rs index b286894fa..db1b6ee5c 100644 --- a/iceoryx2-ffi/ffi/src/api/unique_listener_id.rs +++ b/iceoryx2-ffi/ffi/src/api/unique_listener_id.rs @@ -86,6 +86,45 @@ impl HandleToType for iox2_unique_listener_id_h_ref { // BEGIN C API +/// Retrieves the value of a unique listener ID. +/// +/// # Arguments +/// +/// * `handle` - A valid [`iox2_unique_listener_id_h`] +/// * `id_ptr` - Pointer to a buffer where the ID value will be written +/// * `id_length` - The length of the buffer pointed to by `id_ptr` +/// +/// # Safety +/// +/// * `handle` must be a valid, non-null pointer +/// * `id_ptr` must be a valid, non-null pointer to a buffer of at least `id_length` bytes +/// * `id_length` must be large enough to hold the ID value +#[no_mangle] +unsafe extern "C" fn iox2_unique_listener_id_value( + handle: iox2_unique_listener_id_h, + id_ptr: *mut u8, + id_length: usize, +) { + debug_assert!(!id_ptr.is_null()); + handle.assert_non_null(); + + let h = &mut *handle.as_type(); + + if let Some(Some(id)) = (h.value.internal.as_ptr() as *const Option).as_ref() + { + let bytes = id.value().to_ne_bytes(); + debug_assert!(bytes.len() <= id_length, "id_length is too small"); + + unsafe { + std::ptr::copy_nonoverlapping( + bytes.as_ptr(), + id_ptr, + std::cmp::min(bytes.len(), id_length), + ); + } + } +} + /// This function needs to be called to destroy the unique listener id! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/unique_notifier_id.rs b/iceoryx2-ffi/ffi/src/api/unique_notifier_id.rs index 8b289bd8a..6eb2a7600 100644 --- a/iceoryx2-ffi/ffi/src/api/unique_notifier_id.rs +++ b/iceoryx2-ffi/ffi/src/api/unique_notifier_id.rs @@ -86,6 +86,45 @@ impl HandleToType for iox2_unique_notifier_id_h_ref { // BEGIN C API +/// Retrieves the value of a unique notifier ID. +/// +/// # Arguments +/// +/// * `handle` - A valid [`iox2_unique_notifier_id_h`] +/// * `id_ptr` - Pointer to a buffer where the ID value will be written +/// * `id_length` - The length of the buffer pointed to by `id_ptr` +/// +/// # Safety +/// +/// * `handle` must be a valid, non-null pointer +/// * `id_ptr` must be a valid, non-null pointer to a buffer of at least `id_length` bytes +/// * `id_length` must be large enough to hold the ID value +#[no_mangle] +unsafe extern "C" fn iox2_unique_notifier_id_value( + handle: iox2_unique_notifier_id_h, + id_ptr: *mut u8, + id_length: usize, +) { + debug_assert!(!id_ptr.is_null()); + handle.assert_non_null(); + + let h = &mut *handle.as_type(); + + if let Some(Some(id)) = (h.value.internal.as_ptr() as *const Option).as_ref() + { + let bytes = id.value().to_ne_bytes(); + debug_assert!(bytes.len() <= id_length, "id_length is too small"); + + unsafe { + std::ptr::copy_nonoverlapping( + bytes.as_ptr(), + id_ptr, + std::cmp::min(bytes.len(), id_length), + ); + } + } +} + /// This function needs to be called to destroy the unique notifier id! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/unique_publisher_id.rs b/iceoryx2-ffi/ffi/src/api/unique_publisher_id.rs index 4f81be0a6..a2e4d8338 100644 --- a/iceoryx2-ffi/ffi/src/api/unique_publisher_id.rs +++ b/iceoryx2-ffi/ffi/src/api/unique_publisher_id.rs @@ -86,6 +86,45 @@ impl HandleToType for iox2_unique_publisher_id_h_ref { // BEGIN C API +/// Retrieves the value of a unique publisher ID. +/// +/// # Arguments +/// +/// * `handle` - A valid [`iox2_unique_publisher_id_h`] +/// * `id_ptr` - Pointer to a buffer where the ID value will be written +/// * `id_length` - The length of the buffer pointed to by `id_ptr` +/// +/// # Safety +/// +/// * `handle` must be a valid, non-null pointer +/// * `id_ptr` must be a valid, non-null pointer to a buffer of at least `id_length` bytes +/// * `id_length` must be large enough to hold the ID value +#[no_mangle] +unsafe extern "C" fn iox2_unique_publisher_id_value( + handle: iox2_unique_publisher_id_h, + id_ptr: *mut u8, + id_length: usize, +) { + debug_assert!(!id_ptr.is_null()); + handle.assert_non_null(); + + let h = &mut *handle.as_type(); + + if let Some(Some(id)) = (h.value.internal.as_ptr() as *const Option).as_ref() + { + let bytes = id.value().to_ne_bytes(); + debug_assert!(bytes.len() <= id_length, "id_length is too small"); + + unsafe { + std::ptr::copy_nonoverlapping( + bytes.as_ptr(), + id_ptr, + std::cmp::min(bytes.len(), id_length), + ); + } + } +} + /// This function needs to be called to destroy the unique publisher id! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/unique_subscriber_id.rs b/iceoryx2-ffi/ffi/src/api/unique_subscriber_id.rs index 65c8effc2..521c2c8c3 100644 --- a/iceoryx2-ffi/ffi/src/api/unique_subscriber_id.rs +++ b/iceoryx2-ffi/ffi/src/api/unique_subscriber_id.rs @@ -86,6 +86,46 @@ impl HandleToType for iox2_unique_subscriber_id_h_ref { // BEGIN C API +/// Retrieves the value of a unique subscriber ID. +/// +/// # Arguments +/// +/// * `handle` - A valid [`iox2_unique_subscriber_id_h`] +/// * `id_ptr` - Pointer to a buffer where the ID value will be written +/// * `id_length` - The length of the buffer pointed to by `id_ptr` +/// +/// # Safety +/// +/// * `handle` must be a valid, non-null pointer +/// * `id_ptr` must be a valid, non-null pointer to a buffer of at least `id_length` bytes +/// * `id_length` must be large enough to hold the ID value +#[no_mangle] +unsafe extern "C" fn iox2_unique_subscriber_id_value( + handle: iox2_unique_subscriber_id_h, + id_ptr: *mut u8, + id_length: usize, +) { + debug_assert!(!id_ptr.is_null()); + handle.assert_non_null(); + + let h = &mut *handle.as_type(); + + if let Some(Some(id)) = + (h.value.internal.as_ptr() as *const Option).as_ref() + { + let bytes = id.value().to_ne_bytes(); + debug_assert!(bytes.len() <= id_length, "id_length is too small"); + + unsafe { + std::ptr::copy_nonoverlapping( + bytes.as_ptr(), + id_ptr, + std::cmp::min(bytes.len(), id_length), + ); + } + } +} + /// This function needs to be called to destroy the unique subscriber id! /// /// # Arguments diff --git a/iceoryx2/src/port/port_identifiers.rs b/iceoryx2/src/port/port_identifiers.rs index 1826057ec..415de10a3 100644 --- a/iceoryx2/src/port/port_identifiers.rs +++ b/iceoryx2/src/port/port_identifiers.rs @@ -35,6 +35,11 @@ macro_rules! generate_id { pub fn new() -> Self { Self::default() } + + /// Returns the underlying raw value of the ID + pub fn value(&self) -> u128 { + self.0.value() + } } }; }