Skip to content

Merge develop to main #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 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

To build and run the unit test program:
Expand Down
102 changes: 78 additions & 24 deletions include/buffer/shared_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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
Expand All @@ -79,6 +76,7 @@
#include <memory> // std::shared_ptr
#include <compare> // spaceship operator
#include <span>
#include <bit> // std::bit_cast

#include <utility> // std::move, std::swap
#include <algorithm> // std::copy
Expand All @@ -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.
*
Expand Down Expand Up @@ -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 <typename T>
mutable_shared_buffer(const T* buf, size_type sz) :
mutable_shared_buffer(std::as_bytes(std::span<const T>{buf, sz})) { }
mutable_shared_buffer(const T* buf, size_type num) :
mutable_shared_buffer(std::as_bytes(std::span<const T>{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<const std::byte>{std::bit_cast<const std::byte*>(buf), sz})) { }

/**
* @brief Construct from input iterators.
Expand Down Expand Up @@ -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.
*
Expand All @@ -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 <typename T>
mutable_shared_buffer& append(const T* buf, std::size_t sz) {
return append(std::as_bytes(std::span<const T>{buf, sz}));
mutable_shared_buffer& append(const T* buf, std::size_t num) {
return append(std::as_bytes(std::span<const T>{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<const std::byte>{std::bit_cast<const std::byte*>(buf), sz}));
}

/**
Expand Down Expand Up @@ -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 <typename T>
const_shared_buffer(const T* buf, std::size_t sz) :
const_shared_buffer(std::as_bytes(std::span<const T>{buf, sz})) { }
const_shared_buffer(const T* buf, std::size_t num) :
const_shared_buffer(std::as_bytes(std::span<const T>{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<const std::byte>{std::bit_cast<const std::byte*>(buf), sz})) { }

/**
* @brief Construct by copying from a @c mutable_shared_buffer object.
Expand All @@ -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.
Expand Down
31 changes: 22 additions & 9 deletions test/shared_buffer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,16 @@ bool check_sb_against_test_data(SB sb) {
}

template <typename SB, typename PT>
SB generic_pointer_construction_test() {
auto ptr { std::bit_cast<const PT *>(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));
return sb;
}

template <typename PT>
void generic_pointer_append_test() {
auto sb { generic_pointer_construction_test<chops::mutable_shared_buffer, PT>() };
void generic_pointer_append_test(const PT * ptr) {
auto sb { generic_pointer_construction_test<chops::mutable_shared_buffer>(ptr) };
auto sav_sz { sb.size() };
const PT arr[] { 5, 6, 7 };
const PT* ptr_arr { arr };
Expand Down Expand Up @@ -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<chops::mutable_shared_buffer, TestType>();
generic_pointer_construction_test<chops::const_shared_buffer, TestType>();
const TestType* ptr { std::bit_cast<const TestType *>(test_data.data()) };
generic_pointer_construction_test<chops::mutable_shared_buffer>(ptr);
generic_pointer_construction_test<chops::const_shared_buffer>(ptr);
}

TEST_CASE ( "Void pointer construction",
"[common]" ) {
auto ptr { static_cast<const void *>(test_data.data()) };
generic_pointer_construction_test<chops::mutable_shared_buffer>(ptr);
generic_pointer_construction_test<chops::const_shared_buffer>(ptr);
}

TEMPLATE_TEST_CASE ( "Shared buffer common ctor methods",
"[const_shared_buffer] [mutable_shared_buffer] [common]",
Expand Down Expand Up @@ -261,8 +267,6 @@ TEST_CASE ( "Mutable shared buffer append",
REQUIRE (sb == ta);
}



SECTION ( "Append mutable shared buffer" ) {
sb.append(ta);
REQUIRE (sb == ta);
Expand All @@ -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<const void*>(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<TestType>();
const TestType* ptr { std::bit_cast<const TestType *>(test_data.data()) };
generic_pointer_append_test<TestType>(ptr);
}

TEST_CASE ( "Compare a mutable shared_buffer with a const shared buffer",
Expand Down