Buffer Composition

Composing buffer sequences without allocation for scatter/gather I/O.

What You Will Learn

  • Creating buffers from different sources

  • Using const_buffer_pair and mutable_buffer_pair for scatter/gather I/O

  • Zero-allocation buffer sequence patterns

Prerequisites

Source Code

#include <boost/capy.hpp>
#include <iostream>
#include <string>
#include <array>
#include <vector>

using namespace boost::capy;

void demonstrate_single_buffers()
{
    std::cout << "=== Single Buffer Examples ===\n\n";

    // Create buffers from various sources
    std::string str = "Hello, World!";
    char arr[] = "Array data";
    std::vector<char> vec = {'V', 'e', 'c', 't', 'o', 'r'};

    // make_buffer creates buffer views (no copies)
    auto str_buf = make_buffer(str);  // mutable_buffer
    auto arr_buf = make_buffer(arr, sizeof(arr) - 1);  // mutable_buffer - Exclude null terminator
    auto vec_buf = make_buffer(vec);  // mutable_buffer

    std::cout << "String buffer: " << str_buf.size() << " bytes\n";
    std::cout << "Array buffer:  " << arr_buf.size() << " bytes\n";
    std::cout << "Vector buffer: " << vec_buf.size() << " bytes\n";
}

void demonstrate_buffer_pair()
{
    std::cout << "\n=== Buffer Pair (Scatter/Gather) ===\n\n";

    // const_buffer_pair is std::array<const_buffer, 2>
    std::string header = "Content-Type: text/plain\r\n\r\n";
    std::string body = "Hello, World!";

    const_buffer_pair message = {{
        make_buffer(header),
        make_buffer(body)
    }};

    // Calculate total size
    std::size_t total = buffer_size(message);
    std::cout << "Total message size: " << total << " bytes\n";
    std::cout << "Buffer count: " << buffer_length(message) << "\n";

    // Iterate through buffers
    std::cout << "\nBuffer contents:\n";
    for (auto const& buf : message)  // const_buffer const&
    {
        std::cout << "  [" << buf.size() << " bytes]: ";
        std::cout.write(static_cast<char const*>(buf.data()), buf.size());
        std::cout << "\n";
    }
}

void demonstrate_buffer_array()
{
    std::cout << "\n=== Multi-Buffer Array ===\n\n";

    // Use std::array for more than 2 buffers
    std::string status = "HTTP/1.1 200 OK\r\n";
    std::string content_type = "Content-Type: application/json\r\n";
    std::string server = "Server: Capy/1.0\r\n";
    std::string empty_line = "\r\n";
    std::string body = R"({"status":"ok"})";

    std::array<const_buffer, 5> http_response = {{
        make_buffer(status),
        make_buffer(content_type),
        make_buffer(server),
        make_buffer(empty_line),
        make_buffer(body)
    }};

    std::size_t total = buffer_size(http_response);
    std::cout << "HTTP response size: " << total << " bytes\n";
    std::cout << "Buffer count: " << buffer_length(http_response) << "\n";

    // In real code with streams:
    // co_await write(stream, http_response);
    // This performs scatter/gather I/O - single syscall for all buffers
}

void demonstrate_mutable_buffers()
{
    std::cout << "\n=== Mutable Buffer Example ===\n\n";

    // Mutable buffers for receiving data
    char buf1[64];
    char buf2[64];

    mutable_buffer_pair recv_buffers = {{
        mutable_buffer(buf1, sizeof(buf1)),
        mutable_buffer(buf2, sizeof(buf2))
    }};

    std::cout << "Prepared " << buffer_length(recv_buffers)
              << " buffers with " << buffer_size(recv_buffers)
              << " bytes total capacity\n";

    // In real code:
    // auto [ec, n] = co_await stream.read_some(recv_buffers);
}

int main()
{
    demonstrate_single_buffers();
    demonstrate_buffer_pair();
    demonstrate_buffer_array();
    demonstrate_mutable_buffers();

    return 0;
}

Build

add_executable(buffer_composition buffer_composition.cpp)
target_link_libraries(buffer_composition PRIVATE capy)

Walkthrough

Creating Buffers

auto str_buf = make_buffer(str);  // mutable_buffer
auto arr_buf = make_buffer(arr, sizeof(arr) - 1);  // mutable_buffer

make_buffer creates buffer views from various sources. No data is copied—the buffers reference the original storage.

Buffer Pairs

const_buffer_pair message = {{
    make_buffer(header),
    make_buffer(body)
}};

const_buffer_pair is std::array<const_buffer, 2> — a fixed-size buffer sequence for scatter/gather I/O. Similarly, mutable_buffer_pair holds two mutable buffers.

Multi-Buffer Arrays

std::array<const_buffer, 5> http_response = {{
    make_buffer(status),
    make_buffer(content_type),
    // ...
}};

For more than two buffers, use std::array directly. Buffer sequences support buffer_size() and buffer_length() for querying total bytes and buffer count.

Scatter/Gather I/O

co_await write(stream, http_response);

When you write a buffer sequence, the OS receives all buffers in a single system call. This is scatter/gather I/O:

  • No intermediate buffer allocation

  • No copying data together

  • Single syscall for multiple buffers

Output

=== Single Buffer Examples ===

String buffer: 13 bytes
Array buffer:  10 bytes
Vector buffer: 6 bytes

=== Buffer Pair (Scatter/Gather) ===

Total message size: 41 bytes
Buffer count: 2

Buffer contents:
  [28 bytes]: Content-Type: text/plain


  [13 bytes]: Hello, World!

=== Multi-Buffer Array ===

HTTP response size: 84 bytes
Buffer count: 5

=== Mutable Buffer Example ===

Prepared 2 buffers with 128 bytes total capacity

Exercises

  1. Create a function that takes any ConstBufferSequence and prints its contents

  2. Measure the difference between copying data into a single buffer vs. using cat()

  3. Implement a simple message framing protocol using buffer composition

Next Steps