Buffer Sequences

Corosio socket operations accept any buffer sequence compatible with Boost.Buffers. Internally, the socket implementation uses a type-erased interface (any_bufref) to avoid templating the implementation on buffer types.

Buffer Types

Boost.Buffers provides two fundamental buffer types:

// Mutable buffer - for reading into
boost::buffers::mutable_buffer buf(ptr, size);

// Const buffer - for writing from
boost::buffers::const_buffer buf(ptr, size);

Using Buffers with Sockets

Pass buffers directly to socket operations:

char data[1024];

// Read into a single buffer
auto [ec, n] = co_await s.read_some(
    boost::buffers::mutable_buffer(data, sizeof(data)));

// Write from a single buffer
auto [ec2, n2] = co_await s.write_some(
    boost::buffers::const_buffer(data, n));

Buffer Sequences

A buffer sequence is any range of buffers. Use arrays or vectors for scatter/gather I/O:

// Scatter read into multiple buffers
std::array<boost::buffers::mutable_buffer, 2> read_bufs = {
    boost::buffers::mutable_buffer(header, header_size),
    boost::buffers::mutable_buffer(body, body_size)
};
auto [ec, n] = co_await s.read_some(read_bufs);

// Gather write from multiple buffers
std::array<boost::buffers::const_buffer, 2> write_bufs = {
    boost::buffers::const_buffer(header, header_size),
    boost::buffers::const_buffer(body, body_size)
};
auto [ec2, n2] = co_await s.write_some(write_bufs);

The any_bufref Interface

Internally, Corosio uses any_bufref to type-erase buffer sequences. This allows the socket implementation to remain non-templated while accepting any buffer sequence type.

The interface is simple:

class any_bufref
{
public:
    // Construct from any const buffer sequence
    template<capy::const_buffer_sequence BufferSequence>
    explicit any_bufref(BufferSequence const& bs) noexcept;

    // Copy buffers to an array (e.g., WSABUF array)
    std::size_t copy_to(capy::mutable_buffer* dest, std::size_t n);
};

Why Type Erasure?

Without type erasure, the socket implementation would need to be templated on every buffer sequence type used:

// Without type erasure - explosion of template instantiations
template<class Buffers>
void socket_impl::read_some(Buffers const& bufs, ...);

With type erasure, the implementation is fixed:

// With type erasure - single implementation
void socket_impl::read_some(any_bufref& bufs, ...);

The cost is a virtual call when iterating buffers, which is negligible compared to I/O latency.

Custom Buffer Sequences

Any type that satisfies MutableBufferSequence or ConstBufferSequence works with Corosio:

struct my_buffer_sequence
{
    // Buffer type
    using value_type = boost::buffers::mutable_buffer;

    // Iterator support
    auto begin() const { return buffers_.begin(); }
    auto end() const { return buffers_.end(); }

private:
    std::vector<boost::buffers::mutable_buffer> buffers_;
};

Performance Considerations

For maximum performance:

  1. Prefer single buffers when possible—fewer iterations

  2. Reuse buffers to avoid allocation

  3. Size buffers appropriately—too small means more syscalls, too large wastes memory

Typical buffer sizes:

  • Small messages: 1-4 KB

  • Bulk transfers: 64 KB - 1 MB

  • High-throughput: Match TCP window size (typically 64 KB default)

Next Steps

  • Sockets — Socket read/write operations