Skip to content

Commit

Permalink
[#490] Implement send_copy for slices
Browse files Browse the repository at this point in the history
  • Loading branch information
orecham committed Nov 2, 2024
1 parent 1468900 commit 5411dd6
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 12 deletions.
24 changes: 24 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/publisher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ class Publisher {
/// Copies the input `value` into a [`SampleMut`] and delivers it.
/// On success it returns the number of [`Subscriber`]s that received
/// the data, otherwise a [`PublisherSendError`] describing the failure.
template <typename T = Payload, typename = std::enable_if_t<!iox::IsSlice<T>::VALUE, void>>
auto send_copy(const Payload& payload) const -> iox::expected<size_t, PublisherSendError>;

template <typename T = Payload, typename = std::enable_if_t<iox::IsSlice<T>::VALUE, void>>
auto send_slice_copy(const Payload& payload) const -> iox::expected<size_t, PublisherSendError>;

/// Loans/allocates a [`SampleMutUninit`] from the underlying data segment of the [`Publisher`].
/// The user has to initialize the payload before it can be sent.
///
Expand Down Expand Up @@ -167,6 +171,7 @@ inline auto Publisher<S, Payload, UserHeader>::id() const -> UniquePublisherId {
}

template <ServiceType S, typename Payload, typename UserHeader>
template <typename T, typename>
inline auto Publisher<S, Payload, UserHeader>::send_copy(const Payload& payload) const
-> iox::expected<size_t, PublisherSendError> {
static_assert(std::is_trivially_copyable<Payload>::value);
Expand All @@ -182,6 +187,25 @@ inline auto Publisher<S, Payload, UserHeader>::send_copy(const Payload& payload)
return iox::err(iox::into<PublisherSendError>(result));
}

template <ServiceType S, typename Payload, typename UserHeader>
template <typename T, typename>
inline auto Publisher<S, Payload, UserHeader>::send_slice_copy(const Payload& payload) const
-> iox::expected<size_t, PublisherSendError> {
size_t number_of_recipients = 0;
auto result = iox2_publisher_send_slice_copy(&m_handle,
payload.data(),
sizeof(typename Payload::ValueType),
alignof(typename Payload::ValueType),
payload.number_of_elements(),
&number_of_recipients);

if (result == IOX2_OK) {
return iox::ok(number_of_recipients);
}

return iox::err(iox::into<PublisherSendError>(result));
}

template <ServiceType S, typename Payload, typename UserHeader>
template <typename T, typename>
inline auto Publisher<S, Payload, UserHeader>::loan_uninit()
Expand Down
71 changes: 60 additions & 11 deletions iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

#include "iox/uninitialized_array.hpp"
#include "iox2/node.hpp"
#include "iox2/node_name.hpp"
#include "iox2/service.hpp"
Expand Down Expand Up @@ -199,10 +200,58 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_uninit_send_receive_works) {
ASSERT_THAT(**recv_sample, Eq(payload));
}

TYPED_TEST(ServicePublishSubscribeTest, slice_copy_send_receive_works) {
constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE;
constexpr auto SLICE_MAX_LENGTH = 10;

constexpr uint64_t DEFAULT_VALUE_A = 42;
constexpr uint64_t DEFAULT_VALUE_B = 777;
constexpr uint64_t DEFAULT_VALUE_Z = 21;
struct MyNestedStruct {
uint64_t a { DEFAULT_VALUE_A };
uint64_t b { DEFAULT_VALUE_B };
};
struct MyStruct {
uint64_t z { DEFAULT_VALUE_Z };
MyNestedStruct data;
};

const auto service_name = iox2_testing::generate_service_name();

auto node = NodeBuilder().create<SERVICE_TYPE>().expect("");
auto service =
node.service_builder(service_name).template publish_subscribe<iox::Slice<MyStruct>>().create().expect("");

auto sut_publisher = service.publisher_builder().max_slice_len(SLICE_MAX_LENGTH).create().expect("");
auto sut_subscriber = service.subscriber_builder().create().expect("");

iox::UninitializedArray<MyStruct, SLICE_MAX_LENGTH, iox::ZeroedBuffer> elements;
for (auto& item : elements) {
new (&item) MyStruct {};
}
auto payload = iox::Slice(elements.begin(), SLICE_MAX_LENGTH);
sut_publisher.send_slice_copy(payload).expect("");

auto recv_result = sut_subscriber.receive().expect("");
ASSERT_TRUE(recv_result.has_value());
auto recv_sample = std::move(recv_result.value());

auto iterations = 0;
for (const auto& item : recv_sample.payload_slice()) {
ASSERT_THAT(item.z, Eq(DEFAULT_VALUE_Z));
ASSERT_THAT(item.data.a, Eq(DEFAULT_VALUE_A));
ASSERT_THAT(item.data.b, Eq(DEFAULT_VALUE_B));
++iterations;
}

ASSERT_THAT(recv_sample.payload_slice().number_of_elements(), Eq(SLICE_MAX_LENGTH));
ASSERT_THAT(iterations, Eq(SLICE_MAX_LENGTH));
}

TYPED_TEST(ServicePublishSubscribeTest, loan_slice_send_receive_works) {
constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE;
constexpr uint64_t PAYLOAD_ALIGNMENT = 8;
constexpr auto CXX_SLICE_MAX_LENGTH = 10;
constexpr auto SLICE_MAX_LENGTH = 10;

constexpr uint64_t DEFAULT_VALUE_A = 42;
constexpr uint64_t DEFAULT_VALUE_B = 777;
Expand All @@ -225,10 +274,10 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_slice_send_receive_works) {
.create()
.expect("");

auto sut_publisher = service.publisher_builder().max_slice_len(CXX_SLICE_MAX_LENGTH).create().expect("");
auto sut_publisher = service.publisher_builder().max_slice_len(SLICE_MAX_LENGTH).create().expect("");
auto sut_subscriber = service.subscriber_builder().create().expect("");

auto send_sample = sut_publisher.loan_slice(CXX_SLICE_MAX_LENGTH).expect("");
auto send_sample = sut_publisher.loan_slice(SLICE_MAX_LENGTH).expect("");

send(std::move(send_sample)).expect("");

Expand All @@ -244,14 +293,14 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_slice_send_receive_works) {
++iterations;
}

ASSERT_THAT(recv_sample.payload_slice().number_of_elements(), Eq(CXX_SLICE_MAX_LENGTH));
ASSERT_THAT(iterations, Eq(CXX_SLICE_MAX_LENGTH));
ASSERT_THAT(recv_sample.payload_slice().number_of_elements(), Eq(SLICE_MAX_LENGTH));
ASSERT_THAT(iterations, Eq(SLICE_MAX_LENGTH));
}

TYPED_TEST(ServicePublishSubscribeTest, loan_slice_uninit_send_receive_works) {
constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE;
constexpr uint64_t PAYLOAD_ALIGNMENT = 8;
constexpr auto CXX_SLICE_MAX_LENGTH = 10;
constexpr auto SLICE_MAX_LENGTH = 10;

constexpr uint64_t DEFAULT_VALUE_A = 42;
constexpr uint64_t DEFAULT_VALUE_B = 777;
Expand All @@ -274,10 +323,10 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_slice_uninit_send_receive_works) {
.create()
.expect("");

auto sut_publisher = service.publisher_builder().max_slice_len(CXX_SLICE_MAX_LENGTH).create().expect("");
auto sut_publisher = service.publisher_builder().max_slice_len(SLICE_MAX_LENGTH).create().expect("");
auto sut_subscriber = service.subscriber_builder().create().expect("");

auto send_sample = sut_publisher.loan_slice_uninit(CXX_SLICE_MAX_LENGTH).expect("");
auto send_sample = sut_publisher.loan_slice_uninit(SLICE_MAX_LENGTH).expect("");

auto iterations = 0;
for (auto& item : send_sample.payload_slice()) {
Expand All @@ -300,14 +349,14 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_slice_uninit_send_receive_works) {
++iterations;
}

ASSERT_THAT(recv_sample.payload_slice().number_of_elements(), Eq(CXX_SLICE_MAX_LENGTH));
ASSERT_THAT(iterations, Eq(CXX_SLICE_MAX_LENGTH));
ASSERT_THAT(recv_sample.payload_slice().number_of_elements(), Eq(SLICE_MAX_LENGTH));
ASSERT_THAT(iterations, Eq(SLICE_MAX_LENGTH));
}

TYPED_TEST(ServicePublishSubscribeTest, loan_slice_uninit_with_bytes_send_receive_works) {
constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE;
constexpr uint64_t PAYLOAD_ALIGNMENT = 8;
constexpr auto CXX_SLICE_MAX_LENGTH = 10;
constexpr auto SLICE_MAX_LENGTH = 10;

constexpr uint64_t DEFAULT_VALUE_A = 42;
constexpr uint64_t DEFAULT_VALUE_B = 777;
Expand Down
69 changes: 68 additions & 1 deletion iceoryx2-ffi/ffi/src/api/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl HandleToType for iox2_publisher_h_ref {
unsafe fn send_copy<S: Service>(
publisher: &Publisher<S, PayloadFfi, UserHeaderFfi>,
data_ptr: *const c_void,
data_len: usize,
size_of_element: usize,
number_of_recipients: *mut usize,
) -> c_int {
// loan_slice_uninit(1) <= 1 is correct here since it defines the number of
Expand All @@ -193,6 +193,38 @@ unsafe fn send_copy<S: Service>(
Err(e) => return e.into_c_int(),
};

if sample.payload().len() < size_of_element {
return iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE as c_int;
}

let sample_ptr = sample.payload_mut().as_mut_ptr();
core::ptr::copy_nonoverlapping(data_ptr, sample_ptr.cast(), size_of_element);
match sample.assume_init().send() {
Ok(v) => {
if !number_of_recipients.is_null() {
*number_of_recipients = v;
}
}
Err(e) => return e.into_c_int(),
}

IOX2_OK
}

unsafe fn send_slice_copy<S: Service>(
publisher: &Publisher<S, PayloadFfi, UserHeaderFfi>,
data_ptr: *const c_void,
size_of_element: usize,
alignment_of_element: usize,
number_of_elements: usize,
number_of_recipients: *mut usize,
) -> c_int {
let mut sample = match publisher.loan_custom_payload(number_of_elements) {
Ok(sample) => sample,
Err(e) => return e.into_c_int(),
};

let data_len = (size_of_element * number_of_elements).next_multiple_of(alignment_of_element);
if sample.payload().len() < data_len {
return iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE as c_int;
}
Expand Down Expand Up @@ -316,6 +348,41 @@ pub unsafe extern "C" fn iox2_publisher_id(
*id_handle_ptr = (*storage_ptr).as_handle();
}

#[no_mangle]
pub unsafe extern "C" fn iox2_publisher_send_slice_copy(
publisher_handle: iox2_publisher_h_ref,
data_ptr: *const c_void,
size_of_element: usize,
alignment_of_element: usize,
number_of_elements: usize,
number_of_recipients: *mut usize,
) -> c_int {
publisher_handle.assert_non_null();
debug_assert!(!data_ptr.is_null());
debug_assert!(size_of_element != 0);

let publisher = &mut *publisher_handle.as_type();

match publisher.service_type {
iox2_service_type_e::IPC => send_slice_copy(
&publisher.value.as_mut().ipc,
data_ptr,
size_of_element,
alignment_of_element,
number_of_elements,
number_of_recipients,
),
iox2_service_type_e::LOCAL => send_slice_copy(
&publisher.value.as_mut().local,
data_ptr,
size_of_element,
alignment_of_element,
number_of_elements,
number_of_recipients,
),
}
}

/// Sends a copy of the provided data via the publisher. The data must be copyable via `memcpy`.
///
/// # Arguments
Expand Down

0 comments on commit 5411dd6

Please sign in to comment.