Sources and Sinks (Complete I/O)

This section explains the ReadSource and WriteSink concepts for complete I/O operations with EOF signaling.

Prerequisites

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(), and n == buffer_size(buffers) (buffer completely filled)

  • On EOF: ec == cond::eof, and n is bytes read before EOF (partial read)

  • On error: ec.failed(), and n is 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.

Use Cases

  • Writing complete messages

  • HTTP body transmission (content-length or chunked)

  • Protocol framing with explicit termination

Example

template<WriteSink Sink>
task<> send_response(Sink& sink, response const& resp)
{
    // Write headers
    auto headers = format_headers(resp);
    co_await sink.write(make_buffer(headers));

    // Write body with EOF
    co_await sink.write(make_buffer(resp.body), true);  // EOF after body
}

Type-Erasing Wrappers

any_read_source

#include <boost/capy/io/any_read_source.hpp>

template<ReadSource S>
any_read_source(S& source);

any_write_sink

#include <boost/capy/io/any_write_sink.hpp>

template<WriteSink S>
any_write_sink(S& sink);

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 (write_eof(), EOF flag)

Use case

Raw I/O, incremental processing

Message-oriented protocols

Abstraction level

Lower (closer to OS)

Higher (application-friendly)

Reference

Header Description

<boost/capy/concept/read_source.hpp>

ReadSource concept definition

<boost/capy/concept/write_sink.hpp>

WriteSink concept definition

<boost/capy/io/any_read_source.hpp>

Type-erased read source wrapper

<boost/capy/io/any_write_sink.hpp>

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.