Buffer Sequences

This section explains buffer sequences—the concept that enables zero-allocation composition of buffers.

Prerequisites

  • Completed Buffer Types

  • Understanding of const_buffer and mutable_buffer

What Is a Buffer Sequence?

A buffer sequence is any type that can produce an iteration of buffers. Formally:

  • A single buffer (like const_buffer) is a sequence of one element

  • A range of buffers (like vector<const_buffer>) is a multi-element sequence

  • Any bidirectional range with buffer-convertible values qualifies

The Concepts

ConstBufferSequence

template<typename T>
concept ConstBufferSequence =
    std::is_convertible_v<T, const_buffer> || (
        std::ranges::bidirectional_range<T> &&
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);

A type satisfies ConstBufferSequence if:

  • It converts to const_buffer directly (single buffer), OR

  • It is a bidirectional range whose elements convert to const_buffer

MutableBufferSequence

template<typename T>
concept MutableBufferSequence =
    std::is_convertible_v<T, mutable_buffer> || (
        std::ranges::bidirectional_range<T> &&
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);

Same pattern, but for mutable_buffer.

Satisfying the Concepts

Many common types satisfy these concepts:

// Single buffers
const_buffer cb;                    // ConstBufferSequence
mutable_buffer mb;                  // MutableBufferSequence (and ConstBufferSequence)

// Standard containers of buffers
std::vector<const_buffer> v;        // ConstBufferSequence
std::array<mutable_buffer, 3> a;    // MutableBufferSequence

// String types (convert to single buffer)
std::string str;                    // ConstBufferSequence (via make_buffer)
std::string_view sv;                // ConstBufferSequence

Heterogeneous Composition

Because the concept accepts anything convertible to buffer, you can mix types:

template<ConstBufferSequence Buffers>
void send(Buffers const& bufs);

// All of these work:
send(make_buffer("Hello"));                    // string literal
send(std::string_view{"Hello"});               // string_view
send(std::array{buf1, buf2});                  // array of buffers
send(my_custom_buffer_sequence);               // custom type

Iterating Buffer Sequences

Use begin() and end() from <boost/capy/buffers.hpp>:

template<ConstBufferSequence Buffers>
void process(Buffers const& bufs)
{
    for (auto it = begin(bufs); it != end(bufs); ++it)
    {
        const_buffer buf = *it;
        // Process buf.data(), buf.size()
    }
}

These functions handle both single buffers (returning pointer-to-self) and ranges (returning standard iterators).

consuming_buffers

When transferring data incrementally, consuming_buffers tracks progress:

#include <boost/capy/buffers/consuming_buffers.hpp>

template<MutableBufferSequence Buffers>
task<std::size_t> read_all(Stream& stream, Buffers buffers)
{
    consuming_buffers<Buffers> remaining(buffers);
    std::size_t total = 0;

    while (buffer_size(remaining) > 0)
    {
        auto [ec, n] = co_await stream.read_some(remaining);
        remaining.consume(n);
        total += n;
        if (ec)
            break;
    }

    co_return total;
}

consuming_buffers wraps a buffer sequence and provides:

  • consume(n) — Mark n bytes as consumed (remove from front)

  • Iteration over unconsumed buffers

  • buffer_size() of remaining bytes

Why Bidirectional?

The concepts require bidirectional ranges (not just forward ranges) for two reasons:

  1. Some algorithms traverse buffers backwards

  2. consuming_buffers needs to adjust the first buffer’s start position

If your custom buffer sequence only provides forward iteration, wrap it in a type that provides bidirectional access.

Reference

Header Description

<boost/capy/buffers.hpp>

Concepts and iteration functions

<boost/capy/buffers/consuming_buffers.hpp>

Incremental consumption wrapper

You have now learned how buffer sequences enable zero-allocation composition. Continue to System I/O Integration to see how buffer sequences interface with operating system I/O.