From 42ddf33b634e6eaf40d9eee225c909ac15d7d935 Mon Sep 17 00:00:00 2001 From: cliffg-softwarelibre Date: Thu, 10 Apr 2025 08:31:28 -0600 Subject: [PATCH 1/3] Minor tweaks to README --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ceb17dc..3804c38 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ While internally all data is kept in `std::byte` buffers, convenience methods ar The generated Doxygen documentation for `shared_buffer` is [here](https://connectivecpp.github.io/shared-buffer/). -## Dependencies +## Library Dependencies -The `shared_buffer` header file does not have any third-party dependencies. It uses C++ standard library headers only. The unit test code does have dependencies as noted below. +The `shared_buffer` header file does not have any third-party dependencies. It uses C++ standard library headers only. The unit test and example code do have dependencies as noted below. ## C++ Standard @@ -44,6 +44,10 @@ The unit test uses utilities from Connective C++'s [utility-rack](https://github Specific version (or branch) specs for the dependencies are in the [test/CMakeLists.txt](test/CMakeLists.txt) file, look for the `CPMAddPackage` commands. +## Example Dependencies + +The example applications use the Connective C++ `utility_rack` reference counted buffer classes. Specific version (or branch) specs for the dependency are in the [example/CMakeLists.txt](example/CMakeLists.txt) file, look for the `CPMAddPackage` command. + ## Build and Run Unit Tests To build and run the unit test program: From b219c2fae8345f47db404b2e120a792fef6f2840 Mon Sep 17 00:00:00 2001 From: cliffg-softwarelibre Date: Sat, 19 Apr 2025 11:46:40 -0600 Subject: [PATCH 2/3] README doc tweaks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3804c38..4135cc6 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Specific version (or branch) specs for the dependencies are in the [test/CMakeLi ## Example Dependencies -The example applications use the Connective C++ `utility_rack` reference counted buffer classes. Specific version (or branch) specs for the dependency are in the [example/CMakeLists.txt](example/CMakeLists.txt) file, look for the `CPMAddPackage` command. +The example applications use the Connective C++ `utility_rack` reference counted buffer classes and `binary_serialize` functions. Specific version (or branch) specs for the dependencies are in the [example/CMakeLists.txt](example/CMakeLists.txt) file, look for the `CPMAddPackage` commands. ## Build and Run Unit Tests From 3ebb369eb88f0b22e2f796d278b132f7ae71d28b Mon Sep 17 00:00:00 2001 From: cliffg-softwarelibre Date: Tue, 22 Apr 2025 18:30:32 -0600 Subject: [PATCH 3/3] Added void pointer construction and append --- include/buffer/shared_buffer.hpp | 102 +++++++++++++++++++++++-------- test/shared_buffer_test.cpp | 31 +++++++--- 2 files changed, 100 insertions(+), 33 deletions(-) diff --git a/include/buffer/shared_buffer.hpp b/include/buffer/shared_buffer.hpp index 8112e9f..8b8493c 100644 --- a/include/buffer/shared_buffer.hpp +++ b/include/buffer/shared_buffer.hpp @@ -32,7 +32,8 @@ * constructing a @c const_shared_buffer for writing to the network. * * Besides the data buffer lifetime management, these utility classes eliminate data - * copies and (obviously) can be utilized in use cases other than networking. + * buffer copies and (obviously) can be utilized in use cases other than networking + * (for example reading and writing disk files). * * ### Additional Details * @@ -53,10 +54,6 @@ * @c mutable_shared_buffer class as well as adding convenience methods to the * @c const_shared_buffer class. * - * It is likely that this shared buffer design and code will change as the C++ - * Networking TS buffer features are expanded, changed, or better understood. Currently - * there are no direct ties to Networking TS buffer features. - * * @note Everything is declared @c noexcept except for the methods that allocate * memory and might throw a memory exception. This is tighter than the @c noexcept * declarations on the underlying @c std::vector methods, since @c std::byte @@ -79,6 +76,7 @@ #include // std::shared_ptr #include // spaceship operator #include +#include // std::bit_cast #include // std::move, std::swap #include // std::copy @@ -99,7 +97,7 @@ class const_shared_buffer; * This class provides ownership, copying, and lifetime management for byte oriented * buffers. In particular, it is designed to be used in conjunction with the * @c const_shared_buffer class for efficient transfer and correct lifetime management - * of buffers in asynchronous libraries (such as the C++ Networking TS). In particular, + * of buffers in asynchronous libraries (such as Asio). In particular, * a reference counted buffer can be passed among multiple layers of software without * any one layer "owning" the buffer. * @@ -223,17 +221,37 @@ class mutable_shared_buffer { * * The pointer passed into this constructor is cast into a @c std::byte pointer and bytes * are then copied. In particular, this method can be used for @c char pointers, - * @c void pointers, @c unsigned @c char pointers, etc. + * @c unsigned @c char pointers, @c std::uint8_t pointers, etc. Non character types that + * are trivially copyable are also allowed, although the usual care must be taken + * (padding bytes, alignment, etc). * * @pre Size cannot be greater than the source buffer. * - * @param buf Non-null pointer to a buffer of data. + * @param buf Non-null pointer to a contiguous array of data. * - * @param sz Size of buffer, in bytes. + * @param num Number of elements in the array. + * + * @note For @c void pointers, see specific constructor taking a @c void pointer. */ template - mutable_shared_buffer(const T* buf, size_type sz) : - mutable_shared_buffer(std::as_bytes(std::span{buf, sz})) { } + mutable_shared_buffer(const T* buf, size_type num) : + mutable_shared_buffer(std::as_bytes(std::span{buf, num})) { } + +/** + * @brief Construct by copying bytes from a void pointer. + * + * The pointer passed into this constructor is cast into a @c std::byte pointer and bytes + * are then copied. + * + * @pre Size cannot be greater than the source buffer. + * + * @param buf Non-null @c void pointer to a buffer of data. + * + * @param sz Size of buffer, in bytes. + */ + mutable_shared_buffer(const void* buf, size_type sz) : + mutable_shared_buffer(std::as_bytes( + std::span{std::bit_cast(buf), sz})) { } /** * @brief Construct from input iterators. @@ -345,7 +363,7 @@ class mutable_shared_buffer { } /** - * @brief Append a @c std::span to the end of the internal buffer. + * @brief Append a @c std::span of @c std::bytes to the end of the internal buffer. * * @param sp @c std::span of @c std::byte data. * @@ -361,15 +379,33 @@ class mutable_shared_buffer { * * The pointer passed into this method is cast into a @c std::byte pointer and bytes * are then copied. In particular, this method can be used for @c char pointers, - * @c void pointers, @ unsigned @c char pointers, etc. + * @c void pointers, @c unsigned @c char pointers, @c std::uint8_t pointers, etc. + * Non character types that are layout compatible with @c std::byte are allowed. * - * @param buf Non-null pointer to a buffer of data. + * @param buf Non-null pointer to an array of data. * - * @param sz Size of buffer, in bytes. + * @param num Number of elements in the array. */ template - mutable_shared_buffer& append(const T* buf, std::size_t sz) { - return append(std::as_bytes(std::span{buf, sz})); + mutable_shared_buffer& append(const T* buf, std::size_t num) { + return append(std::as_bytes(std::span{buf, num})); + } + +/** + * @brief Append by copying bytes from a void pointer. + * + * The pointer passed into this constructor is cast into a @c std::byte pointer and bytes + * are then appended. + * + * @pre Size cannot be greater than the source buffer. + * + * @param buf Non-null @c void pointer to a buffer of data. + * + * @param sz Size of buffer, in bytes. + */ + mutable_shared_buffer& append(const void* buf, size_type sz) { + return append(std::as_bytes( + std::span{std::bit_cast(buf), sz})); } /** @@ -549,20 +585,38 @@ class const_shared_buffer { * * The pointer passed into this constructor is cast into a @c std::byte pointer and bytes * are then copied. In particular, this method can be used for @c char pointers, - * @c void pointers, @c unsigned @c char pointers, etc. + * @c unsigned @c char pointers, @c std::uint8_t pointers, etc. Non character types that + * are trivially copyable are also allowed, although the usual care must be taken + * (padding bytes, alignment, etc). * * The type of the span must be convertible to or be layout compatible with * @c std::byte. * * @pre Size cannot be greater than the source buffer. * - * @param buf Non-null pointer to a buffer of data. + * @param buf Non-null pointer to an array of data. * - * @param sz Size of buffer, in bytes. + * @param num Number of elements in the array. */ template - const_shared_buffer(const T* buf, std::size_t sz) : - const_shared_buffer(std::as_bytes(std::span{buf, sz})) { } + const_shared_buffer(const T* buf, std::size_t num) : + const_shared_buffer(std::as_bytes(std::span{buf, num})) { } + +/** + * @brief Construct by copying bytes from a void pointer. + * + * The pointer passed into this constructor is cast into a @c std::byte pointer and bytes + * are then copied. + * + * @pre Size cannot be greater than the source buffer. + * + * @param buf Non-null @c void pointer to a buffer of data. + * + * @param sz Size of buffer, in bytes. + */ + const_shared_buffer(const void* buf, size_type sz) : + const_shared_buffer(std::as_bytes( + std::span{std::bit_cast(buf), sz})) { } /** * @brief Construct by copying from a @c mutable_shared_buffer object. @@ -581,8 +635,8 @@ class const_shared_buffer { * * This constructor will move from a @c mutable_shared_buffer into a @c const_shared_buffer. * This allows efficient API boundaries, where application code can construct and fill in a - * @c mutable_shared_buffer, then @c std::move it into a @c const_shared_buffer for use - * with asynchronous functions. + * @c mutable_shared_buffer, then use this constructor which will @c std::move it into a + * @c const_shared_buffer for use with asynchronous functions. * * @param rhs @c mutable_shared_buffer to be moved from; after moving the * @c mutable_shared_buffer will be empty. diff --git a/test/shared_buffer_test.cpp b/test/shared_buffer_test.cpp index 3230ec3..fe2131a 100644 --- a/test/shared_buffer_test.cpp +++ b/test/shared_buffer_test.cpp @@ -44,8 +44,7 @@ bool check_sb_against_test_data(SB sb) { } template -SB generic_pointer_construction_test() { - auto ptr { std::bit_cast(test_data.data()) }; +SB generic_pointer_construction_test(const PT * ptr) { SB sb(ptr, test_data_size); REQUIRE_FALSE (sb.empty()); REQUIRE (check_sb_against_test_data(sb)); @@ -53,8 +52,8 @@ SB generic_pointer_construction_test() { } template -void generic_pointer_append_test() { - auto sb { generic_pointer_construction_test() }; +void generic_pointer_append_test(const PT * ptr) { + auto sb { generic_pointer_construction_test(ptr) }; auto sav_sz { sb.size() }; const PT arr[] { 5, 6, 7 }; const PT* ptr_arr { arr }; @@ -149,10 +148,17 @@ void byte_vector_move_test() { TEMPLATE_TEST_CASE ( "Generic pointer construction", "[common]", char, unsigned char, signed char, std::uint8_t ) { - generic_pointer_construction_test(); - generic_pointer_construction_test(); + const TestType* ptr { std::bit_cast(test_data.data()) }; + generic_pointer_construction_test(ptr); + generic_pointer_construction_test(ptr); } +TEST_CASE ( "Void pointer construction", + "[common]" ) { + auto ptr { static_cast(test_data.data()) }; + generic_pointer_construction_test(ptr); + generic_pointer_construction_test(ptr); +} TEMPLATE_TEST_CASE ( "Shared buffer common ctor methods", "[const_shared_buffer] [mutable_shared_buffer] [common]", @@ -261,8 +267,6 @@ TEST_CASE ( "Mutable shared buffer append", REQUIRE (sb == ta); } - - SECTION ( "Append mutable shared buffer" ) { sb.append(ta); REQUIRE (sb == ta); @@ -287,12 +291,21 @@ TEST_CASE ( "Mutable shared buffer append", sb.append(sv.data(), sv.size()); REQUIRE (sb == cb); } + + SECTION ( "Append with void pointer" ) { + std::string_view sv("Haha, Bro!"); + const void* ptr { static_cast(sv.data()) }; + chops::mutable_shared_buffer cb(sv.data(), sv.size()); + sb.append(ptr, sv.size()); + REQUIRE (sb == cb); + } } TEMPLATE_TEST_CASE ( "Generic pointer append", "[mutable_shared_buffer] [pointer] [append]", char, unsigned char, signed char, std::uint8_t ) { - generic_pointer_append_test(); + const TestType* ptr { std::bit_cast(test_data.data()) }; + generic_pointer_append_test(ptr); } TEST_CASE ( "Compare a mutable shared_buffer with a const shared buffer",