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).

buffer_slice

When transferring data incrementally, buffer_slice returns a slice that tracks progress:

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

template<MutableBufferSequence Buffers>
task<std::size_t> read_all(Stream& stream, Buffers buffers)
{
    auto remaining = buffer_slice(buffers);
    std::size_t const total_size = buffer_size(buffers);
    std::size_t total = 0;

    while (total < total_size)
    {
        auto [ec, n] = co_await stream.read_some(remaining.data());
        remaining.remove_prefix(n);
        total += n;
        if (ec)
            break;
    }

    co_return total;
}

buffer_slice(seq, offset, length) returns an object of unspecified type that satisfies the Slice concept, providing:

  • data() — Buffer sequence view of the slice’s current bytes (pass to read_some/write_some)

  • remove_prefix(n) — Advance the start by n bytes

The offset and length parameters (both optional) make buffer_slice a general byte sub-range primitive, not just an iteration-state holder.

Why Bidirectional?

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

  1. Some algorithms traverse buffers backwards

  2. The buffer sequence view returned by Slice::data() needs to adjust the first and last buffers' bounds

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/buffer_slice.hpp>

Byte sub-range slicing algorithm

<boost/capy/concept/slice.hpp>

Slice concept

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.