Skip to content
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
129 changes: 129 additions & 0 deletions include/boost/capy/any_bufref.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/capy
//

#ifndef BOOST_CAPY_ANY_BUFREF_HPP
#define BOOST_CAPY_ANY_BUFREF_HPP

#include <boost/capy/detail/config.hpp>
#include <boost/capy/buffers.hpp>

#include <cstddef>

namespace boost {
namespace capy {

/** A type-erased buffer sequence I/O parameter.

This class provides a type-erased interface for iterating
over buffer sequences without knowing the concrete type.
It allows asynchronous operations to efficiently type-erase
the buffer sequence parameter, avoiding the need to
templatize the implementation.

@par Example
The following shows the minimal form of an awaitable, templated on the
buffer sequence type, with only an `await_suspend` method. The example
demonstrates that you can construct an `any_bufref` in the parameter
list when calling a virtual interface; there is no need to create a
separate variable if not desired.

@code
template<class Buffers>
struct awaitable
{
Buffers b;

void await_suspend( std::coroutine_handle<> )
{
my_virtual_engine_submit( any_bufref( b ) );
}
};

// Example virtual interface accepting any_bufref
void my_virtual_engine_submit( any_bufref p )
{
capy::mutable_buffer temp[8];
std::size_t n = p.copy_to( temp, 8 );
// ... handle the buffers ...
}
@endcode
*/
class any_bufref
{
public:
/** Construct from a const buffer sequence.

@param bs The buffer sequence to adapt.
*/
template<ConstBufferSequence BS>
explicit
any_bufref(BS const& bs) noexcept
: bs_(&bs)
, fn_(&copy_impl<BS>)
{
}

/** Fill an array with buffers from the sequence.

@param dest Pointer to array of mutable buffer descriptors.
@param n Maximum number of buffers to copy.

@return The number of buffers actually copied.
*/
std::size_t
copy_to(
mutable_buffer* dest,
std::size_t n) const noexcept
{
return fn_(bs_, dest, n);
}

private:
template<ConstBufferSequence BS>
static std::size_t
copy_impl(
void const* p,
mutable_buffer* dest,
std::size_t n)
{
auto const& bs = *static_cast<BS const*>(p);
auto it = begin(bs);
auto const end_it = end(bs);

std::size_t i = 0;
if constexpr (MutableBufferSequence<BS>)
{
for(; it != end_it && i < n; ++it, ++i)
dest[i] = *it;
}
else
{
for(; it != end_it && i < n; ++it, ++i)
{
auto const& buf = *it;
dest[i] = mutable_buffer(
const_cast<char*>(
static_cast<char const*>(buf.data())),
buf.size());
}
}
return i;
}

using fn_t = std::size_t(*)(void const*,
mutable_buffer*, std::size_t);

void const* bs_;
fn_t fn_;
};

} // namespace capy
} // namespace boost

#endif
222 changes: 222 additions & 0 deletions test/unit/buffers/any_bufref.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/capy
//

// Test that header file is self-contained.
#include <boost/capy/any_bufref.hpp>

#include <boost/capy/buffers/buffer_pair.hpp>
#include <boost/core/span.hpp>
#include <array>

#include "test_buffers.hpp"

namespace boost {
namespace capy {

struct any_bufref_test
{
void
testConstBuffer()
{
char const data[] = "Hello";
const_buffer cb(data, 5);

any_bufref ref(cb);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 1);
BOOST_TEST_EQ(dest[0].data(), data);
BOOST_TEST_EQ(dest[0].size(), 5);
}

void
testMutableBuffer()
{
char data[] = "Hello";
mutable_buffer mb(data, 5);

any_bufref ref(mb);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 1);
BOOST_TEST_EQ(dest[0].data(), data);
BOOST_TEST_EQ(dest[0].size(), 5);
}

void
testConstBufferPair()
{
char const data1[] = "Hello";
char const data2[] = "World";
const_buffer_pair cbp{{
const_buffer(data1, 5),
const_buffer(data2, 5) }};

any_bufref ref(cbp);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 2);
BOOST_TEST_EQ(dest[0].data(), data1);
BOOST_TEST_EQ(dest[0].size(), 5);
BOOST_TEST_EQ(dest[1].data(), data2);
BOOST_TEST_EQ(dest[1].size(), 5);
}

void
testMutableBufferPair()
{
char data1[] = "Hello";
char data2[] = "World";
mutable_buffer_pair mbp{{
mutable_buffer(data1, 5),
mutable_buffer(data2, 5) }};

any_bufref ref(mbp);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 2);
BOOST_TEST_EQ(dest[0].data(), data1);
BOOST_TEST_EQ(dest[0].size(), 5);
BOOST_TEST_EQ(dest[1].data(), data2);
BOOST_TEST_EQ(dest[1].size(), 5);
}

void
testSpan()
{
char const data1[] = "One";
char const data2[] = "Two";
char const data3[] = "Three";
const_buffer arr[3] = {
const_buffer(data1, 3),
const_buffer(data2, 3),
const_buffer(data3, 5) };
span<const_buffer const> s(arr, 3);

any_bufref ref(s);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 3);
BOOST_TEST_EQ(dest[0].data(), data1);
BOOST_TEST_EQ(dest[0].size(), 3);
BOOST_TEST_EQ(dest[1].data(), data2);
BOOST_TEST_EQ(dest[1].size(), 3);
BOOST_TEST_EQ(dest[2].data(), data3);
BOOST_TEST_EQ(dest[2].size(), 5);
}

void
testArray()
{
char const data1[] = "One";
char const data2[] = "Two";
char const data3[] = "Three";
std::array<const_buffer, 3> arr{{
const_buffer(data1, 3),
const_buffer(data2, 3),
const_buffer(data3, 5) }};

any_bufref ref(arr);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 3);
BOOST_TEST_EQ(dest[0].data(), data1);
BOOST_TEST_EQ(dest[0].size(), 3);
BOOST_TEST_EQ(dest[1].data(), data2);
BOOST_TEST_EQ(dest[1].size(), 3);
BOOST_TEST_EQ(dest[2].data(), data3);
BOOST_TEST_EQ(dest[2].size(), 5);
}

void
testCArray()
{
char const data1[] = "One";
char const data2[] = "Two";
char const data3[] = "Three";
const_buffer arr[3] = {
const_buffer(data1, 3),
const_buffer(data2, 3),
const_buffer(data3, 5) };

any_bufref ref(arr);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 3);
BOOST_TEST_EQ(dest[0].data(), data1);
BOOST_TEST_EQ(dest[0].size(), 3);
BOOST_TEST_EQ(dest[1].data(), data2);
BOOST_TEST_EQ(dest[1].size(), 3);
BOOST_TEST_EQ(dest[2].data(), data3);
BOOST_TEST_EQ(dest[2].size(), 5);
}

void
testLimitedCopy()
{
char const data1[] = "One";
char const data2[] = "Two";
char const data3[] = "Three";
const_buffer arr[3] = {
const_buffer(data1, 3),
const_buffer(data2, 3),
const_buffer(data3, 5) };

any_bufref ref(arr);

// copy only 2 buffers
mutable_buffer dest[2];
auto n = ref.copy_to(dest, 2);
BOOST_TEST_EQ(n, 2);
BOOST_TEST_EQ(dest[0].data(), data1);
BOOST_TEST_EQ(dest[0].size(), 3);
BOOST_TEST_EQ(dest[1].data(), data2);
BOOST_TEST_EQ(dest[1].size(), 3);
}

void
testEmptySequence()
{
const_buffer cb;
any_bufref ref(cb);

mutable_buffer dest[8];
auto n = ref.copy_to(dest, 8);
BOOST_TEST_EQ(n, 1);
BOOST_TEST_EQ(dest[0].size(), 0);
}

void
run()
{
testConstBuffer();
testMutableBuffer();
testConstBufferPair();
testMutableBufferPair();
testSpan();
testArray();
testCArray();
testLimitedCopy();
testEmptySequence();
}
};

TEST_SUITE(
any_bufref_test,
"boost.capy.any_bufref");

} // capy
} // boost
Loading