Buffer Sequences
Corosio I/O operations work with buffer sequences from Boost.Capy. This page explains how to use buffers effectively.
| Code snippets assume: |
#include <boost/capy/buffers.hpp>
namespace capy = boost::capy;
Creating Buffers
From Raw Arrays
char data[1024];
capy::mutable_buffer mbuf(data, sizeof(data));
const char* str = "Hello";
capy::const_buffer cbuf(str, 5);
Buffer Sequences
I/O operations accept sequences of buffers for scatter/gather I/O:
Single Buffer
A single buffer is a valid buffer sequence:
capy::mutable_buffer buf(data, size);
co_await sock.read_some(buf); // Works directly
Multiple Buffers
Use arrays or vectors of buffers:
// Array of buffers
std::array<capy::mutable_buffer, 2> bufs = {
capy::mutable_buffer(header, header_size),
capy::mutable_buffer(body, body_size)
};
co_await sock.read_some(bufs);
// Vector of buffers
std::vector<capy::const_buffer> send_bufs;
send_bufs.push_back(capy::const_buffer(header.data(), header.size()));
send_bufs.push_back(capy::const_buffer(body.data(), body.size()));
co_await sock.write_some(send_bufs);
Buffer Sequence Concepts
Corosio uses concepts from Capy:
// Readable buffers (for writing to sockets)
template<capy::const_buffer_sequence ConstBufferSequence>
auto write_some(ConstBufferSequence const& buffers);
// Writable buffers (for reading from sockets)
template<capy::mutable_buffer_sequence MutableBufferSequence>
auto read_some(MutableBufferSequence const& buffers);
A type satisfies these concepts if it’s iterable and yields buffer types.
Buffer Size
Get the total size of a buffer sequence:
std::array<capy::mutable_buffer, 2> bufs = {...};
std::size_t total = capy::buffer_size(bufs);
consuming_buffers
The consuming_buffers wrapper tracks progress through a buffer sequence:
#include <boost/corosio/consuming_buffers.hpp>
std::array<capy::mutable_buffer, 2> bufs = {
capy::mutable_buffer(header, 16),
capy::mutable_buffer(body, 1024)
};
corosio::consuming_buffers<decltype(bufs)> consuming(bufs);
// After reading 20 bytes:
auto [ec, n] = co_await sock.read_some(consuming);
consuming.consume(n); // Advance by bytes read
// Now consuming represents the remaining unread portion
This is used internally by read() and write() but can be used directly.
any_bufref
The any_bufref class type-erases buffer sequences:
#include <boost/corosio/any_bufref.hpp>
void accept_any_buffer(corosio::any_bufref bufref)
{
capy::mutable_buffer temp[8];
std::size_t n = bufref.copy_to(temp, 8);
// Use temp[0..n-1]
}
// Works with any buffer sequence
std::array<capy::mutable_buffer, 2> bufs = {...};
accept_any_buffer(corosio::any_bufref(bufs));
This enables non-templated code to work with any buffer type.
Memory Safety
Lifetime
Buffers don’t own memory. The underlying storage must outlive the I/O operation:
// WRONG: buffer outlives string
capy::task<void> bad_example(corosio::socket& sock)
{
capy::const_buffer buf;
{
std::string temp = "Hello";
buf = capy::const_buffer(temp.data(), temp.size());
} // temp destroyed here!
co_await sock.write_some(buf); // Undefined behavior
}
// CORRECT: keep storage alive
capy::task<void> good_example(corosio::socket& sock)
{
std::string msg = "Hello";
co_await sock.write_some(
capy::const_buffer(msg.data(), msg.size()));
}
String Invalidation
Be careful when using std::string as buffer storage:
std::string s = "Hello";
capy::mutable_buffer buf(s.data(), s.size());
s += " World"; // May reallocate, invalidating buf!
// Use buf here: UNDEFINED BEHAVIOR
Either:
-
Reserve sufficient capacity upfront
-
Don’t modify the string while the buffer is in use
-
Create a new buffer after modification
Scatter/Gather I/O
Multiple buffers can be used for efficient scatter/gather operations:
Reading into Multiple Buffers (Scatter)
struct message_header { ... };
char body[1024];
std::array<capy::mutable_buffer, 2> read_bufs = {
capy::mutable_buffer(&header, sizeof(header)),
capy::mutable_buffer(body, sizeof(body))
};
auto [ec, n] = co_await sock.read_some(read_bufs);
// Data fills header first, then body
Writing from Multiple Buffers (Gather)
std::string header = "HTTP/1.1 200 OK\r\n\r\n";
std::string body = "Hello, World!";
std::array<capy::const_buffer, 2> write_bufs = {
capy::const_buffer(header.data(), header.size()),
capy::const_buffer(body.data(), body.size())
};
auto [ec, n] = co_await sock.write_some(write_bufs);
// Sends header followed by body in a single operation
Example: Reading a Fixed-Size Header
struct packet_header
{
std::uint32_t magic;
std::uint32_t length;
};
capy::task<packet_header> read_header(corosio::io_stream& stream)
{
packet_header header;
auto [ec, n] = co_await corosio::read(
stream, capy::mutable_buffer(&header, sizeof(header)));
if (ec)
throw boost::system::system_error(ec);
return header;
}
Next Steps
-
Composed Operations — Using buffers with read/write
-
Sockets — Socket I/O operations
-
Echo Server Tutorial — Practical usage