Sources and Sinks (Complete I/O)
This section explains the ReadSource and WriteSink concepts for complete I/O operations with EOF signaling.
Prerequisites
-
Completed Streams (Partial I/O)
-
Understanding of partial I/O with
ReadStreamandWriteStream
ReadSource
A ReadSource provides complete read operations that fill buffers entirely or signal EOF:
template<typename T>
concept ReadSource =
requires(T& source, mutable_buffer_archetype buffers) {
{ source.read(buffers) } -> IoAwaitable;
};
read Semantics
template<MutableBufferSequence MB>
IoAwaitable auto read(MB const& buffers);
Returns an awaitable yielding (error_code, std::size_t):
-
On success:
!ec.failed(), andn == buffer_size(buffers)(buffer completely filled) -
On EOF:
ec == cond::eof, andnis bytes read before EOF (partial read) -
On error:
ec.failed(), andnis bytes read before error
The key difference from ReadStream: a successful read fills the buffer completely.
Use Cases
-
Reading fixed-size records
-
Reading message frames with known sizes
-
Filling buffers for batch processing
Example
template<ReadSource Source>
task<std::optional<message>> read_message(Source& source)
{
// Read fixed-size header
message_header header;
auto [ec, n] = co_await source.read(
mutable_buffer(&header, sizeof(header)));
if (ec == cond::eof && n == 0)
co_return std::nullopt; // Clean EOF
if (ec.failed())
throw std::system_error(ec);
// Read variable-size body
std::vector<char> body(header.body_size);
auto [ec2, n2] = co_await source.read(make_buffer(body));
if (ec2.failed())
throw std::system_error(ec2);
co_return message{header, std::move(body)};
}
WriteSink
A WriteSink provides complete write operations with explicit EOF signaling:
template<typename T>
concept WriteSink =
requires(T& sink, const_buffer_archetype buffers) {
{ sink.write(buffers) } -> IoAwaitable;
{ sink.write(buffers, bool{}) } -> IoAwaitable;
{ sink.write_eof() } -> IoAwaitable;
};
write Semantics
// Write data
template<ConstBufferSequence CB>
IoAwaitable auto write(CB const& buffers);
// Write data with optional EOF
template<ConstBufferSequence CB>
IoAwaitable auto write(CB const& buffers, bool eof);
// Signal EOF without data
IoAwaitable auto write_eof();
The eof parameter signals end-of-stream after the data is written.
After calling write_eof() or write(buffers, true), no further writes are permitted.
Example: HTTP Body Handler
The HTTP library uses any_write_sink for body transmission:
// HTTP response handler doesn't know the underlying transport
task<> send_body(any_write_sink& body, std::string_view data)
{
// Works whether body is:
// - Direct socket write (content-length)
// - Chunked encoding wrapper
// - Compressed stream
// - Test mock
co_await body.write(make_buffer(data), true);
}
The caller decides the concrete implementation:
// Content-length mode
content_length_sink cl_sink(socket, data.size());
any_write_sink body{cl_sink};
send_body(body, data);
// Chunked mode
chunked_sink ch_sink(socket);
any_write_sink body{ch_sink};
send_body(body, data);
Same send_body function, different transfer encodings—the library handles the difference.
Streams vs Sources/Sinks
| Aspect | Streams | Sources/Sinks |
|---|---|---|
Transfer amount |
Partial (whatever is available) |
Complete (fill buffer or EOF) |
EOF handling |
Implicit (read returns 0) |
Explicit ( |
Use case |
Raw I/O, incremental processing |
Message-oriented protocols |
Abstraction level |
Lower (closer to OS) |
Higher (application-friendly) |
Reference
| Header | Description |
|---|---|
|
ReadSource concept definition |
|
WriteSink concept definition |
|
Type-erased read source wrapper |
|
Type-erased write sink wrapper |
You have now learned about sources and sinks for complete I/O. Continue to Buffer Sources and Sinks to learn about the callee-owns-buffers pattern.