Skip to content
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

Merge develop to main #4

Merged
merged 17 commits into from
Aug 23, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/build_run_unit_test_cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
build_matrix:
strategy:
matrix:
# os: [ubuntu-latest, windows-latest, macos-14]
os: [ubuntu-latest]
os: [ubuntu-latest, windows-latest, macos-14]
# os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

![GH Tag](https://img.shields.io/github/v/tag/connectivecpp/shared-buffer?label=GH%20tag)

![License](https://img.shields.io/badge/License-Boost%201.0-blue)

## Overview

The `shared_buffer` classes are reference counted `std::byte` buffer classes useful for asynchronous networking. In particular, the Asio asynchronous networking library requires a buffer to be kept alive and valid until the outstanding IO operation (e.g. a network write) is completed. A straightforward and idiomatic way to achieve this is by using reference counted buffers.
Expand Down Expand Up @@ -40,7 +42,7 @@ The unit test code uses [Catch2](https://github.com/catchorg/Catch2). If the `SH

The unit test uses utilities from Connective C++'s [utility-rack](https://github.com/connectivecpp/utility-rack).

Specific version (or branch) specs for the dependenies are in `test/CMakeLists.txt`.
Specific version (or branch) specs for the dependencies are in the [test/CMakeLists.txt](test/CMakeLists.txt) file, look for the `CPMAddPackage` commands.

## Build and Run Unit Tests

Expand Down
176 changes: 123 additions & 53 deletions include/buffer/shared_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@
#include <span>

#include <utility> // std::move, std::swap
#include <cstring> // std::memcpy
#include <algorithm> // std::copy

// TODO - add conecepts and / or requires
// TODO - add concepts and / or requires
//
// modify the templated constructor that takes a buffer of any valid
// byte type, add constraints; this makes the "reinterpret_cast" safe
// byte type, add constraints which makes the casting safer

namespace chops {

Expand Down Expand Up @@ -147,6 +147,34 @@ class mutable_shared_buffer {
mutable_shared_buffer() noexcept :
m_data{std::make_shared<byte_vec>(size_type(0))} { }

/**
* @brief Construct by copying from a @c std::span of @c std::byte.
*
* @param sp @c std::byte span pointing to buffer of data. The data is
* copied into the internal buffer of the @c mutable_shared_buffer.
*
*/
template <std::size_t Ext>
explicit mutable_shared_buffer(std::span<const std::byte, Ext> sp) :
m_data{std::make_shared<byte_vec>(sp.data(), sp.data()+sp.size())} { }

/**
* @brief Construct by copying from a @c std::byte array.
*
* A @c std::span is first created, then the constructor taking
* a @c std::span is called.
*
* @pre Size cannot be greater than the source buffer.
*
* @param buf Non-null pointer to a @c std::byte buffer of data. The
* data is copied into the internal buffer of the @c mutable_shared_buffer.
*
* @param sz Size of buffer.
*
*/
mutable_shared_buffer(const std::byte* buf, std::size_t sz) :
mutable_shared_buffer(std::as_bytes(std::span<const std::byte>{buf, sz})) { }

/**
* @brief Move construct from a @c std::vector of @c std::bytes.
*
Expand All @@ -161,45 +189,34 @@ class mutable_shared_buffer {
m_data{std::make_shared<byte_vec>(size_type(0))} {
*m_data = std::move(bv);
}

/**
* @brief Construct a @c mutable_shared_buffer with an initial size, contents
* set to zero.
* of each byte set to zero.
*
* Allocate zero initialized space which can be overwritten with data as needed.
* The @c data method is called to get access to the underlying @c std::byte
* buffer.
*
* @param sz Size for internal @c std::byte buffer.
*/
explicit mutable_shared_buffer(size_type sz) noexcept :
explicit mutable_shared_buffer(size_type sz) :
m_data{std::make_shared<byte_vec>(sz)} { }

/**
* @brief Construct by copying from a @c std::span of @c std::byte.
*
* @param sp @c std::byte span pointing to buffer of data. The data is
* copied into the internal buffer of the @c mutable_shared_buffer.
*
*/
mutable_shared_buffer(std::span<const std::byte> sp) noexcept :
m_data{std::make_shared<byte_vec>(sp.data(), sp.data()+sp.size())} { }

/**
* @brief Construct by copying from a @c std::byte array.
*
* A @c std::span is first created, then the constructor taking
* a @c std::span is called.
* @brief Construct by copying bytes from a @c std::span.
*
* @pre Size cannot be greater than the source buffer.
*
* @param buf @c std::byte array containing buffer of data. The data is
* copied into the internal buffer of the @c mutable_shared_buffer.
* The type of the span must be convertible to or be layout compatible with
* @c std::byte.
*
* @param sz Size of buffer.
* @param sp @c std::span pointing to buffer of data. The @c std::span
* pointer is cast into a @c std::byte pointer and bytes are then copied.
*
*/
mutable_shared_buffer(const std::byte* buf, size_type sz) noexcept :
mutable_shared_buffer(std::span<const std::byte>(buf, sz)) { }
template <typename T, std::size_t Ext>
mutable_shared_buffer(std::span<const T, Ext> sp) :
mutable_shared_buffer(std::as_bytes(sp)) { }

/**
* @brief Construct by copying bytes from an arbitrary pointer.
Expand All @@ -210,14 +227,13 @@ class mutable_shared_buffer {
*
* @pre Size cannot be greater than the source buffer.
*
* @param buf Pointer to a buffer of data. The pointer must be convertible
* to a @c void pointer and then to a @c std::byte pointer.
* @param buf Non-null pointer to a buffer of data.
*
* @param sz Size of buffer, in bytes.
*/
template <typename T>
mutable_shared_buffer(const T* buf, size_type sz) :
mutable_shared_buffer(reinterpret_cast<const std::byte *>(buf), sz) { }
mutable_shared_buffer(std::as_bytes(std::span<const T>{buf, sz})) { }

/**
* @brief Construct from input iterators.
Expand Down Expand Up @@ -256,23 +272,23 @@ class mutable_shared_buffer {
const std::byte* data() const noexcept { return m_data->data(); }

/**
* @brief Return access to underlying @c std::vector.
*
* This can be used to instantiate a dynamic_buffer as defined in the Networking TS.
* Changing the @c std::vector from outside this class works because no state
* data is stored within this object that needs to be consistent with the @c std::vector
* contents.
* @brief Return size (number of bytes) of buffer.
*
* @return Reference to @c std::vector<std::byte>.
* @return Size of buffer, which may be zero.
*/
byte_vec& get_byte_vec() noexcept { return *m_data; }
size_type size() const noexcept { return m_data->size(); }

/**
* @brief Return size (number of bytes) of buffer.
* @brief Return access to underlying @c std::vector.
*
* @return Size of buffer, which may be zero.
* This can be used to instantiate a @c dynamic_buffer as defined in the Networking TS
* or Asio API. Changing the @c std::vector from outside this class works because no
* state data is stored within this object that needs to be consistent with the
* @c std::vector contents.
*
* @return Reference to @c std::vector<std::byte>.
*/
size_type size() const noexcept { return m_data->size(); }
byte_vec& get_byte_vec() noexcept { return *m_data; }

/**
* @brief Query to see if size is zero.
Expand Down Expand Up @@ -315,34 +331,63 @@ class mutable_shared_buffer {
/**
* @brief Append a @c std::byte buffer to the end of the internal buffer.
*
* @param buf @c std::byte array containing buffer of data.
* @param buf Non-null pointer to @c std::byte buffer of data.
*
* @param sz Size of buffer.
*
* @return Reference to @c this (to allow method chaining).
*/
mutable_shared_buffer& append(const std::byte* buf, size_type sz) {
mutable_shared_buffer& append(const std::byte* buf, std::size_t sz) {
size_type old_sz = size();
resize(old_sz + sz); // set up buffer space
std::memcpy(data() + old_sz, buf, sz);
std::copy(buf, buf+sz, data()+old_sz);
return *this;
}

/**
* @brief Append a @c std::span to the end of the internal buffer.
*
* @param sp @c std::span of @c std::byte data.
*
* @return Reference to @c this (to allow method chaining).
*/
template <std::size_t Ext>
mutable_shared_buffer& append(std::span<const std::byte, Ext> sp) {
return append(sp.data(), sp.size());
}

/**
* @brief Append by copying bytes from an arbitrary pointer.
*
* 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.
*
* @param buf Pointer to a buffer of data. The pointer must be convertible
* to a @c void pointer and then to a @c std::byte pointer.
* @param buf Non-null pointer to a buffer of data.
*
* @param sz Size of buffer, in bytes.
*/
template <typename T>
mutable_shared_buffer& append(const T* buf, size_type sz) {
return append(reinterpret_cast<const std::byte *>(buf), sz);
mutable_shared_buffer& append(const T* buf, std::size_t sz) {
return append(std::as_bytes(std::span<const T>{buf, sz}));
}

/**
* @brief Append a @c std::span that is a non @c std::byte buffer.
*
* The @c std::span passed into this method is performs a cast on the
* data. In particular, this method can be used for @c char pointers,
* @c void pointers, @ unsigned @c char pointers, etc.
*
* The type of the span must be convertible to or be layout compatible with
* @c std::byte.
*
* @param sp @c std::span of arbitrary bytes.
*
*/
template <typename T, std::size_t Ext>
mutable_shared_buffer& append(std::span<const T, Ext> sp) {
return append(std::as_bytes(sp));
}

/**
Expand Down Expand Up @@ -463,36 +508,61 @@ class const_shared_buffer {
const_shared_buffer& operator=(const const_shared_buffer&) = delete;
const_shared_buffer& operator=(const_shared_buffer&&) = delete;

/**
* @brief Construct by copying from a @c std::span of @c std::byte.
*
* @param sp @c std::byte span pointing to buffer of data. The data is
* copied into the internal buffer of the @c const_shared_buffer.
*
*/
template <std::size_t Ext>
explicit const_shared_buffer(std::span<const std::byte, Ext> sp) :
m_data(std::make_shared<byte_vec>(sp.data(), sp.data()+sp.size())) { }
/**
* @brief Construct by copying from a @c std::byte array.
*
* @pre Size cannot be greater than the source buffer.
*
* @param buf @c std::byte array containing buffer of data. The data is
* @param buf Non-null pointer to @c std::byte buffer of data. The data is
* copied into the internal buffer of the @c const_shared_buffer.
*
* @param sz Size of buffer.
*/
const_shared_buffer(const std::byte* buf, size_type sz) :
m_data(std::make_shared<byte_vec>(buf, buf+sz)) { }
const_shared_buffer(const std::byte* buf, std::size_t sz) :
const_shared_buffer(std::as_bytes(std::span<const std::byte>{buf, sz})) { }

/**
* @brief Construct by copying from a @c std::span.
*
* The type of the span must be convertible to or be layout compatible with
* @c std::byte.
*
* @param sp @c std::span pointing to buffer of data. The @c std::span
* pointer is cast into a @c std::byte pointer and bytes are then copied.
*
*/
template <typename T, std::size_t Ext>
const_shared_buffer(std::span<const T, Ext> sp) :
const_shared_buffer(std::as_bytes(sp)) { }
/**
* @brief Construct by copying bytes from an arbitrary pointer.
*
* 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.
*
* 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 Pointer to a buffer of data. The pointer must be convertible
* to a @c void pointer and then to a @c std::byte pointer.
* @param buf Non-null pointer to a buffer of data.
*
* @param sz Size of buffer, in bytes.
*/
template <typename T>
const_shared_buffer(const T* buf, size_type sz) :
const_shared_buffer(reinterpret_cast<const std::byte *>(buf), sz) { }
const_shared_buffer(const T* buf, std::size_t sz) :
const_shared_buffer(std::as_bytes(std::span<const T>{buf, sz})) { }

/**
* @brief Construct by copying from a @c mutable_shared_buffer object.
Expand Down
4 changes: 2 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ target_compile_features ( shared_buffer_test PRIVATE cxx_std_20 )
# add dependencies
include ( ../cmake/download_cpm.cmake )

CPMAddPackage ( "gh:catchorg/Catch2@3.6.0" )
CPMAddPackage ( "gh:connectivecpp/utility-rack@1.0.0" )
CPMAddPackage ( "gh:catchorg/Catch2@3.7.0" )
CPMAddPackage ( "gh:connectivecpp/utility-rack@1.0.3" )

# link dependencies
target_link_libraries ( shared_buffer_test PRIVATE shared_buffer utility_rack Catch2::Catch2WithMain )
Expand Down
Loading