diff --git a/ecal/core/include/ecal/ecal_payload_writer.h b/ecal/core/include/ecal/ecal_payload_writer.h index 5d23f37f3d..fa9a7595a9 100644 --- a/ecal/core/include/ecal/ecal_payload_writer.h +++ b/ecal/core/include/ecal/ecal_payload_writer.h @@ -29,56 +29,140 @@ namespace eCAL { - // base payload writer class to allow zero copy memory operations - // - // the `Write`and `Update` calls may operate on the target memory file directly (zero copy mode) + /** + * @brief Base payload writer class to allow zero copy memory operations. + * + * This class serves as the base class for payload writers, allowing zero-copy memory + * operations. The `Write` and `Update` calls may operate on the target memory file + * directly in zero-copy mode. + */ class CPayloadWriter { public: + /** + * @brief Default constructor for CPayloadWriter. + */ CPayloadWriter() = default; + + /** + * @brief Virtual destructor for CPayloadWriter. + */ virtual ~CPayloadWriter() = default; - CPayloadWriter(const CPayloadWriter &) = default; - CPayloadWriter(CPayloadWriter &&) = default; + /** + * @brief Copy constructor (deleted). + */ + CPayloadWriter(const CPayloadWriter&) = default; + + /** + * @brief Move constructor (deleted). + */ + CPayloadWriter(CPayloadWriter&&) = default; + + /** + * @brief Copy assignment operator (deleted). + */ + CPayloadWriter& operator=(const CPayloadWriter&) = default; - CPayloadWriter& operator=(const CPayloadWriter &) = default; - CPayloadWriter& operator=(CPayloadWriter &&) = default; + /** + * @brief Move assignment operator (deleted). + */ + CPayloadWriter& operator=(CPayloadWriter&&) = default; - // the provisioned memory is uninitialized -> - // perform a full write operation + /** + * @brief Perform a full write operation with uninitialized memory. + * + * This virtual function allows derived classes to perform a full write operation + * when the provisioned memory is uninitialized. + * + * @param buffer_ Pointer to the buffer containing the data to be written. + * @param size_ Size of the data to be written. + * + * @return True if the write operation is successful, false otherwise. + */ virtual bool Write(void* buffer_, size_t size_) = 0; - // the provisioned memory is initialized and contains the data from the last write operation -> - // perform a partial write operation or just modify a few bytes here - // - // by default this operation will just call `Write` + /** + * @brief Perform a partial write operation or modify existing data. + * + * This virtual function allows derived classes to perform a partial write operation + * or modify existing data when the provisioned memory is already initialized and + * contains the data from the last write operation. By default, this operation will + * just call the `Write` function. + * + * @param buffer_ Pointer to the buffer containing the data to be written or modified. + * @param size_ Size of the data to be written or modified. + * + * @return True if the write/update operation is successful, false otherwise. + */ virtual bool Update(void* buffer_, size_t size_) { return Write(buffer_, size_); }; - // provide the size of the required memory (eCAL needs to allocate for you). + /** + * @brief Get the size of the required memory. + * + * This virtual function allows derived classes to provide the size of the memory + * that eCAL needs to allocate. + * + * @return The size of the required memory. + */ virtual size_t GetSize() = 0; }; - // payload writer class that wraps classic (void*, size_t) interface + /** + * @brief Payload writer class that wraps a classic (void*, size_t) interface. + * + * This class is a payload writer that wraps a classic interface using `void*` and `size_t` + * arguments. It inherits from the base class CPayloadWriter, allowing zero-copy memory + * operations. + */ class CBufferPayloadWriter : public CPayloadWriter { public: + /** + * @brief Constructor for CBufferPayloadWriter. + * + * @param buffer_ Pointer to the buffer containing the data to be written. + * @param size_ Size of the data to be written. + */ CBufferPayloadWriter(const void* const buffer_, size_t size_) : m_buffer(buffer_), m_size(size_) {}; - // make a dump memory copy - bool Write (void* buffer_, size_t size_) override { - if (buffer_ == nullptr) return false; - if (size_ < m_size) return false; + /** + * @brief Make a dump memory copy of the stored buffer. + * + * This function performs a dump memory copy of the stored buffer to the provided + * memory location (buffer_) with the specified size (size_). The size of the provided + * memory buffer should be equal to or greater than the stored buffer size to avoid + * memory corruption. + * + * @param buffer_ Pointer to the target buffer where the data will be copied. + * @param size_ Size of the target buffer. + * + * @return True if the copy operation is successful, false otherwise. + */ + bool Write(void* buffer_, size_t size_) override + { + if (buffer_ == nullptr) return false; + if (size_ < m_size) return false; if (m_buffer == nullptr) return false; - if (m_size == 0) return false; + if (m_size == 0) return false; memcpy(buffer_, m_buffer, m_size); return true; } - // size of the memory that needs to be copied + /** + * @brief Get the size of the memory that needs to be copied. + * + * This function returns the size of the memory buffer that needs to be copied during + * the write operation. It is used by the base class CPayloadWriter to allocate the + * required memory for eCAL. + * + * @return The size of the memory that needs to be copied. + */ size_t GetSize() override { return m_size; }; - + private: - const void* m_buffer = nullptr; - size_t m_size = 0; + const void* m_buffer = nullptr; ///< Pointer to the buffer containing the data to be written. + size_t m_size = 0; ///< Size of the data to be written. }; -} + +} // namespace eCAL diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index be784c7bb1..8c0e178d60 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -79,8 +79,10 @@ add_subdirectory(cpp/orchestration/component2) add_subdirectory(cpp/orchestration/orchestrator) # pubsub -add_subdirectory(cpp/pubsub/binary/binary_rec_cb) +add_subdirectory(cpp/pubsub/binary/binary_rec) add_subdirectory(cpp/pubsub/binary/binary_snd) +add_subdirectory(cpp/pubsub/binary/binary_zero_copy_rec) +add_subdirectory(cpp/pubsub/binary/binary_zero_copy_snd) add_subdirectory(cpp/pubsub/binary/ping) add_subdirectory(cpp/pubsub/binary/pong) if(HAS_CAPNPROTO) diff --git a/samples/cpp/pubsub/binary/binary_rec_cb/CMakeLists.txt b/samples/cpp/pubsub/binary/binary_rec/CMakeLists.txt similarity index 89% rename from samples/cpp/pubsub/binary/binary_rec_cb/CMakeLists.txt rename to samples/cpp/pubsub/binary/binary_rec/CMakeLists.txt index b72bfab410..8170925bee 100644 --- a/samples/cpp/pubsub/binary/binary_rec_cb/CMakeLists.txt +++ b/samples/cpp/pubsub/binary/binary_rec/CMakeLists.txt @@ -20,15 +20,15 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(binary_rec_cb) +project(binary_rec) find_package(eCAL REQUIRED) -set(binary_rec_cb_src - src/binary_rec_cb.cpp +set(binary_rec_src + src/binary_rec.cpp ) -ecal_add_sample(${PROJECT_NAME} ${binary_rec_cb_src}) +ecal_add_sample(${PROJECT_NAME} ${binary_rec_src}) target_link_libraries(${PROJECT_NAME} eCAL::core) diff --git a/samples/cpp/pubsub/binary/binary_rec_cb/src/binary_rec_cb.cpp b/samples/cpp/pubsub/binary/binary_rec/src/binary_rec.cpp similarity index 97% rename from samples/cpp/pubsub/binary/binary_rec_cb/src/binary_rec_cb.cpp rename to samples/cpp/pubsub/binary/binary_rec/src/binary_rec.cpp index 8db5825832..f5653c1776 100644 --- a/samples/cpp/pubsub/binary/binary_rec_cb/src/binary_rec_cb.cpp +++ b/samples/cpp/pubsub/binary/binary_rec/src/binary_rec.cpp @@ -43,7 +43,7 @@ void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackD int main(int argc, char** argv) { // initialize eCAL API - eCAL::Initialize(argc, argv, "binary_rec_cb"); + eCAL::Initialize(argc, argv, "binary_rec"); // subscriber for topic "blob" eCAL::CSubscriber sub("blob"); diff --git a/samples/cpp/pubsub/binary/binary_zero_copy_rec/CMakeLists.txt b/samples/cpp/pubsub/binary/binary_zero_copy_rec/CMakeLists.txt new file mode 100644 index 0000000000..849da83367 --- /dev/null +++ b/samples/cpp/pubsub/binary/binary_zero_copy_rec/CMakeLists.txt @@ -0,0 +1,38 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2019 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +cmake_minimum_required(VERSION 3.10) + +project(binary_zero_copy_rec) + +# set project properties +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +set(binary_zero_copy_rec_src src/binary_zero_copy_rec.cpp) + +# find packages +message("-- Checking for modules 'eCAL'") +find_package(eCAL REQUIRED) + +# add and configure sample project +ecal_add_sample(${PROJECT_NAME} ${binary_zero_copy_rec_src}) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_link_libraries(${PROJECT_NAME} eCAL::core) + +# install and set properties +ecal_install_sample(${PROJECT_NAME}) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/binary) diff --git a/samples/cpp/pubsub/binary/binary_zero_copy_rec/src/binary_zero_copy_rec.cpp b/samples/cpp/pubsub/binary/binary_zero_copy_rec/src/binary_zero_copy_rec.cpp new file mode 100644 index 0000000000..33436914b7 --- /dev/null +++ b/samples/cpp/pubsub/binary/binary_zero_copy_rec/src/binary_zero_copy_rec.cpp @@ -0,0 +1,96 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include +#include + +// a simple struct to demonstrate +// zero copy modifications +struct alignas(4) SSimpleStruct +{ + uint32_t version = 1; + uint16_t rows = 5; + uint16_t cols = 3; + uint32_t clock = 0; + uint8_t bytes[5 * 3] = { 0 }; +}; + +// SSimpleStruct logging +std::ostream& operator<<(std::ostream& os, const SSimpleStruct& s) +{ + os << "Version : " << s.version << std::endl; + os << "Rows : " << s.rows << std::endl; + os << "Cols : " << s.cols << std::endl; + os << "Clock : " << s.clock << std::endl; + + os << "Bytes : " << std::endl; + for (int i = 0; i < s.rows; ++i) { + for (int j = 0; j < s.cols; ++j) { + os << static_cast(s.bytes[i * s.cols + j]) << " "; + } + os << std::endl; + } + return os; +} + +// subscriber callback function +void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) +{ + if (data_->size < 1) return; + + std::cout << "------------------------------------" << std::endl; + std::cout << "Binary buffer header :" << std::endl; + std::cout << "------------------------------------" << std::endl; + std::cout << " Size : " << data_->size << std::endl; + std::cout << " Id : " << data_->id << std::endl; + std::cout << " Time : " << data_->time << std::endl; + std::cout << " Clock : " << data_->clock << std::endl; + std::cout << std::endl; + std::cout << "------------------------------------" << std::endl; + std::cout << "SSimpleStruct :" << std::endl; + std::cout << "------------------------------------" << std::endl; + std::cout << *static_cast(data_->buf) << std::endl; +} + +int main(int argc, char** argv) +{ + const char* nodeName = "binary_zero_copy_rec"; + const char* topicName = "simple_struct"; + + // initialize eCAL API + eCAL::Initialize(argc, argv, nodeName); + + // create the subscriber + eCAL::CSubscriber sub(topicName); + + // assign callback + sub.AddReceiveCallback(OnReceive); + + // idle main loop + while (eCAL::Ok()) std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + // finalize eCAL API + eCAL::Finalize(); + + return 0; +} diff --git a/samples/cpp/pubsub/binary/binary_zero_copy_snd/CMakeLists.txt b/samples/cpp/pubsub/binary/binary_zero_copy_snd/CMakeLists.txt new file mode 100644 index 0000000000..d3268c50eb --- /dev/null +++ b/samples/cpp/pubsub/binary/binary_zero_copy_snd/CMakeLists.txt @@ -0,0 +1,38 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2019 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +cmake_minimum_required(VERSION 3.10) + +project(binary_zero_copy_snd) + +# set project properties +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +set(binary_zero_copy_snd_src src/binary_zero_copy_snd.cpp) + +# find packages +message("-- Checking for modules 'eCAL'") +find_package(eCAL REQUIRED) + +# add and configure sample project +ecal_add_sample(${PROJECT_NAME} ${binary_zero_copy_snd_src}) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_link_libraries(${PROJECT_NAME} eCAL::core) + +# install and set properties +ecal_install_sample(${PROJECT_NAME}) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/binary) diff --git a/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp b/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp new file mode 100644 index 0000000000..4191e2e17c --- /dev/null +++ b/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp @@ -0,0 +1,146 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include +#include + +// a simple struct to demonstrate +// zero copy modifications +struct alignas(4) SSimpleStruct +{ + uint32_t version = 1; + uint16_t rows = 5; + uint16_t cols = 3; + uint32_t clock = 0; + uint8_t bytes[5 * 3] = { 0 }; +}; + +// SSimpleStruct logging +void log_struct(const char* action_name, const SSimpleStruct& s) +{ + std::cout << "------------------------------------" << std::endl; + std::cout << action_name << std::endl; + std::cout << "------------------------------------" << std::endl; + + std::cout << "Version : " << s.version << std::endl; + std::cout << "Rows : " << s.rows << std::endl; + std::cout << "Cols : " << s.cols << std::endl; + std::cout << "Clock : " << s.clock << std::endl; + + std::cout << "Bytes : " << std::endl; + for (int i = 0; i < s.rows; ++i) { + for (int j = 0; j < s.cols; ++j) { + std::cout << static_cast(s.bytes[i * s.cols + j]) << " "; + } + std::cout << std::endl; + } +} + +// a binary payload object that handles +// SSimpleStruct Write and Update functionality +class CStructPayload : public eCAL::CPayloadWriter +{ +public: + // Write the complete SSimpleStruct to the shared memory + bool Write(void* buf_, size_t len_) override + { + // check available size and pointer + if (len_ < GetSize() || buf_ == nullptr) return false; + + // create a new struct and update its content + SSimpleStruct simple_struct; + UpdateStruct(&simple_struct); + + // copy complete struct into the memory + *static_cast(buf_) = simple_struct; + + // log action + log_struct("Write SSimpleStruct :", simple_struct); + + return true; + }; + + // Modify the SSimpleStruct in the shared memory + bool Update(void* buf_, size_t len_) override + { + // check available size and pointer + if (len_ < GetSize() || buf_ == nullptr) return false; + + // update the struct in memory + UpdateStruct(static_cast(buf_)); + + // log action + log_struct("Update SSimpleStruct :", *static_cast(buf_)); + + return true; + }; + + size_t GetSize() override { return sizeof(SSimpleStruct); }; + +private: + void UpdateStruct(SSimpleStruct* simple_struct) + { + // modify the simple_struct + simple_struct->clock = clock; + for (auto i = 0; i < (simple_struct->rows * simple_struct->cols); ++i) + { + simple_struct->bytes[i] = static_cast(simple_struct->clock); + } + + // increase internal state clock + clock++; + }; + + uint32_t clock = 0; +}; + +int main(int argc, char** argv) +{ + const char* nodeName = "binary_zero_copy_snd"; + const char* topicName = "simple_struct"; + const char* structTypeName = "SSimpleStruct"; + + // initialize eCAL API + eCAL::Initialize(argc, argv, nodeName); + + // create the publisher + eCAL::CPublisher pub(topicName, { "custom", structTypeName, "" }); + + // turn zero copy mode on + pub.ShmEnableZeroCopy(true); + + // create the simple struct payload + CStructPayload struct_payload; + + // send updates every 100 ms + while (eCAL::Ok()) + { + pub.Send(struct_payload); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // finalize eCAL API + eCAL::Finalize(); + + return 0; +}