100.00% Lines (30/30) 100.00% Functions (12/12)
TLA Baseline Branch
Line Hits Code Line Hits Code
  1 + //
  2 + // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
  3 + //
  4 + // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5 + // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6 + //
  7 + // Official repository: https://github.com/cppalliance/capy
  8 + //
  9 + #ifndef BOOST_CAPY_ASIO_STREAM_HPP
  10 + #define BOOST_CAPY_ASIO_STREAM_HPP
  11 +
  12 + #include <boost/capy/asio/as_io_awaitable.hpp>
  13 + #include <boost/capy/asio/buffers.hpp>
  14 + #include <boost/capy/asio/executor_adapter.hpp>
  15 + #include <boost/capy/asio/spawn.hpp>
  16 + #include <boost/capy/concept/stream.hpp>
  17 + #include <boost/capy/concept/executor.hpp>
  18 + #include <boost/capy/ex/any_executor.hpp>
  19 + #include <boost/capy/io/any_stream.hpp>
  20 + #include <boost/capy/io/any_read_stream.hpp>
  21 + #include <boost/capy/io/any_write_stream.hpp>
  22 +
  23 + namespace boost::capy
  24 + {
  25 +
  26 + /** Wraps a capy ReadStream for use with Asio async operations.
  27 +
  28 + Adapts a capy `ReadStream` to provide Asio-style `async_read_some`
  29 + operations with completion token support.
  30 +
  31 + @tparam Stream The underlying capy ReadStream type
  32 + @tparam Exec The executor type
  33 +
  34 + @par Example
  35 + @code
  36 + capy::any_read_stream stream = ...;
  37 + auto exec = capy::wrap_asio_executor(io.get_executor());
  38 + capy::async_read_stream reader(std::move(stream), exec);
  39 +
  40 + auto [ec, n] = co_await reader.async_read_some(buffer, capy::as_io_awaitable);
  41 + @endcode
  42 + */
  43 + template<ReadStream Stream = any_stream, Executor Exec = any_executor>
  44 + struct async_read_stream
  45 + {
  46 + using executor_type = asio_executor_adapter<Exec>;
  47 + using next_layer_type = Stream;
  48 +
  49 + /// Default constructor.
  50 + async_read_stream() = default;
  51 +
  52 + /// Construct from a stream and executor.
HITGNC   53 + 1 async_read_stream(Stream stream, Exec executor)
  54 + noexcept(std::is_nothrow_move_constructible_v<Stream> &&
  55 + std::is_nothrow_move_constructible_v<Exec>)
HITGNC   56 + 1 : stream_(std::move(stream))
HITGNC   57 + 1 , executor_(std::move(executor))
  58 + {
HITGNC   59 + 1 }
  60 +
  61 + async_read_stream(const async_read_stream&)
  62 + noexcept(std::is_nothrow_copy_constructible_v<Stream> &&
  63 + std::is_nothrow_copy_constructible_v<Exec>) = default;
  64 + async_read_stream(async_read_stream&&)
  65 + noexcept(std::is_nothrow_move_constructible_v<Stream> &&
  66 + std::is_nothrow_move_constructible_v<Exec>) = default;
  67 + async_read_stream& operator=(const async_read_stream&)
  68 + noexcept(std::is_nothrow_copy_assignable_v<Stream> &&
  69 + std::is_nothrow_copy_assignable_v<Exec>) = default;
  70 + async_read_stream& operator=(async_read_stream&&)
  71 + noexcept(std::is_nothrow_move_assignable_v<Stream> &&
  72 + std::is_nothrow_move_assignable_v<Exec>) = default;
  73 +
  74 + /// Returns the executor adapter for Asio compatibility.
  75 + executor_type get_executor() const { return executor_; }
  76 +
  77 + /// Initiates an async read, returning a spawn handle.
  78 + template<typename Buffer>
  79 + auto async_read_some(Buffer && buffer)
  80 + {
  81 + return asio_spawn(
  82 + executor_,
  83 + stream_.read_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))));
  84 + }
  85 +
  86 + /// Initiates an async read with a completion token.
  87 + template<typename Buffer, typename CompletionToken>
HITGNC   88 + 2 auto async_read_some(Buffer && buffer, CompletionToken && token)
  89 + {
HITGNC   90 + 4 return asio_spawn(
  91 + executor_,
HITGNC   92 + 2 stream_.read_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))),
HITGNC   93 + 2 std::forward<CompletionToken>(token));
  94 + }
  95 +
  96 + /// Returns a reference inter to the underlying stream.
HITGNC   97 + 1 next_layer_type& next_layer() { return stream_; }
  98 + /// Returns a const reference to the underlying stream.
  99 + const next_layer_type& next_layer() const { return stream_; }
  100 +
  101 + private:
  102 + Stream stream_;
  103 + Exec executor_;
  104 + };
  105 +
  106 + // Deduction guides
  107 + template<ReadStream Stream, Executor Exec>
  108 + async_read_stream(Stream, Exec) -> async_read_stream<Stream, Exec>;
  109 +
  110 +
  111 + /** Wraps a capy WriteStream for use with Asio async operations.
  112 +
  113 + Adapts a capy `WriteStream` to provide Asio-style `async_write_some`
  114 + operations with completion token support.
  115 +
  116 + @tparam Stream The underlying capy WriteStream type
  117 + @tparam Exec The executor type
  118 +
  119 + @par Example
  120 + @code
  121 + capy::any_write_stream stream = ...;
  122 + auto exec = capy::wrap_asio_executor(io.get_executor());
  123 + capy::async_write_stream writer(std::move(stream), exec);
  124 +
  125 + auto [ec, n] = co_await writer.async_write_some(buffer, capy::as_io_awaitable);
  126 + @endcode
  127 + */
  128 + template<WriteStream Stream = any_stream, Executor Exec = any_executor>
  129 + struct async_write_stream
  130 + {
  131 + using executor_type = asio_executor_adapter<Exec>;
  132 + using next_layer_type = Stream;
  133 +
  134 + /// Default constructor.
  135 + async_write_stream() = default;
  136 +
  137 + /// Construct from a stream and executor.
HITGNC   138 + 1 async_write_stream(Stream stream, Exec executor)
  139 + noexcept(std::is_nothrow_move_constructible_v<Stream> &&
  140 + std::is_nothrow_move_constructible_v<Exec>)
HITGNC   141 + 1 : stream_(std::move(stream))
HITGNC   142 + 1 , executor_(std::move(executor))
  143 + {
HITGNC   144 + 1 }
  145 +
  146 + async_write_stream(const async_write_stream&)
  147 + noexcept(std::is_nothrow_copy_constructible_v<Stream> &&
  148 + std::is_nothrow_copy_constructible_v<Exec>) = default;
  149 + async_write_stream(async_write_stream&&)
  150 + noexcept(std::is_nothrow_move_constructible_v<Stream> &&
  151 + std::is_nothrow_move_constructible_v<Exec>) = default;
  152 + async_write_stream& operator=(const async_write_stream&)
  153 + noexcept(std::is_nothrow_copy_assignable_v<Stream> &&
  154 + std::is_nothrow_copy_assignable_v<Exec>) = default;
  155 + async_write_stream& operator=(async_write_stream&&)
  156 + noexcept(std::is_nothrow_move_assignable_v<Stream> &&
  157 + std::is_nothrow_move_assignable_v<Exec>) = default;
  158 +
  159 + /// Returns the executor adapter for Asio compatibility.
  160 + executor_type get_executor() const { return executor_; }
  161 +
  162 + /// Initiates an async write, returning a spawn handle.
  163 + template<typename Buffer>
  164 + auto async_write_some(Buffer && buffer)
  165 + {
  166 +
  167 + return asio_spawn(
  168 + executor_,
  169 + stream_.write_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))));
  170 + }
  171 +
  172 + /// Initiates an async write with a completion token.
  173 + template<typename Buffer, typename CompletionToken>
HITGNC   174 + 1 auto async_write_some(Buffer && buffer, CompletionToken && token)
  175 + {
HITGNC   176 + 2 return asio_spawn(
  177 + executor_,
HITGNC   178 + 1 stream_.write_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))),
HITGNC   179 + 1 std::forward<CompletionToken>(token));
  180 + }
  181 +
  182 + /// Returns a reference to the underlying stream.
HITGNC   183 + 1 next_layer_type& next_layer() { return stream_; }
  184 + /// Returns a const reference to the underlying stream.
  185 + const next_layer_type& next_layer() const { return stream_; }
  186 +
  187 + private:
  188 + Stream stream_;
  189 + Exec executor_;
  190 + };
  191 +
  192 + // Deduction guides
  193 + template<WriteStream Stream, Executor Exec>
  194 + async_write_stream(Stream, Exec) -> async_write_stream<Stream, Exec>;
  195 +
  196 +
  197 + /** Wraps a capy Stream for use with Asio async operations.
  198 +
  199 + Adapts a capy `Stream` (supporting both read and write) to provide
  200 + Asio-style `async_read_some` and `async_write_some` operations
  201 + with completion token support.
  202 +
  203 + @tparam Stream The underlying capy Stream type
  204 + @tparam Exec The executor type
  205 +
  206 + @par Example
  207 + @code
  208 + capy::any_stream stream = ...;
  209 + auto exec = capy::wrap_asio_executor(io.get_executor());
  210 + capy::async_stream sock(std::move(stream), exec);
  211 +
  212 + // Read
  213 + auto [ec1, n1] = co_await sock.async_read_some(read_buf, capy::as_io_awaitable);
  214 +
  215 + // Write
  216 + auto [ec2, n2] = co_await sock.async_write_some(write_buf, capy::as_io_awaitable);
  217 + @endcode
  218 + */
  219 + template<Stream Stream = any_stream, Executor Exec = any_executor>
  220 + struct async_stream
  221 + {
  222 + using executor_type = asio_executor_adapter<Exec>;
  223 + using next_layer_type = Stream;
  224 +
  225 + /// Default constructor.
  226 + async_stream() = default;
  227 +
  228 + /// Construct from a stream and executor.
  229 + async_stream(Stream stream, Exec executor)
  230 + noexcept(std::is_nothrow_move_constructible_v<Stream> &&
  231 + std::is_nothrow_move_constructible_v<Exec>)
  232 + : stream_(std::move(stream))
  233 + , executor_(std::move(executor))
  234 + {
  235 + }
  236 +
  237 + async_stream(const async_stream&)
  238 + noexcept(std::is_nothrow_copy_constructible_v<Stream> &&
  239 + std::is_nothrow_copy_constructible_v<Exec>) = default;
  240 + async_stream(async_stream&&)
  241 + noexcept(std::is_nothrow_move_constructible_v<Stream> &&
  242 + std::is_nothrow_move_constructible_v<Exec>) = default;
  243 + async_stream& operator=(const async_stream&)
  244 + noexcept(std::is_nothrow_copy_assignable_v<Stream> &&
  245 + std::is_nothrow_copy_assignable_v<Exec>) = default;
  246 + async_stream& operator=(async_stream&&)
  247 + noexcept(std::is_nothrow_move_assignable_v<Stream> &&
  248 + std::is_nothrow_move_assignable_v<Exec>) = default;
  249 +
  250 + /// Returns the executor adapter for Asio compatibility.
  251 + executor_type get_executor() const { return executor_; }
  252 +
  253 + /// Initiates an async read, returning a spawn handle.
  254 + template<typename Buffer>
  255 + auto async_read_some(Buffer && buffer)
  256 + {
  257 + return asio_spawn(
  258 + executor_,
  259 + stream_.read_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))));
  260 + }
  261 +
  262 + /// Initiates an async read with a completion token.
  263 + template<typename Buffer, typename CompletionToken>
  264 + auto async_read_some(Buffer && buffer, CompletionToken && token)
  265 + {
  266 + return asio_spawn(
  267 + executor_,
  268 + stream_.read_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))),
  269 + std::forward<CompletionToken>(token));
  270 + }
  271 +
  272 + /// Initiates an async write, returning a spawn handle.
  273 + template<typename Buffer>
  274 + auto async_write_some(Buffer && buffer)
  275 + {
  276 + return asio_spawn(
  277 + executor_,
  278 + stream_.write_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))));
  279 + }
  280 +
  281 + /// Initiates an async write with a completion token.
  282 + template<typename Buffer, typename CompletionToken>
  283 + auto async_write_some(Buffer && buffer, CompletionToken && token)
  284 + {
  285 + return asio_spawn(
  286 + executor_,
  287 + stream_.write_some(as_asio_buffer_sequence(std::forward<Buffer>(buffer))),
  288 + std::forward<CompletionToken>(token));
  289 + }
  290 +
  291 + /// Returns a reference to the underlying stream.
  292 + next_layer_type& next_layer() { return stream_; }
  293 + /// Returns a const reference to the underlying stream.
  294 + const next_layer_type& next_layer() const { return stream_; }
  295 +
  296 + private:
  297 + Stream stream_;
  298 + Exec executor_;
  299 + };
  300 +
  301 + // Deduction guides
  302 + template<Stream StreamT, Executor Exec>
  303 + async_stream(StreamT, Exec) -> async_stream<StreamT, Exec>;
  304 +
  305 + /** Wraps an Asio AsyncReadStream for use with capy's ReadStream concept.
  306 +
  307 + Adapts an Asio `AsyncReadStream` to provide capy-style `read_some`
  308 + operations that return an IoAwaitable.
  309 +
  310 + @tparam AsyncReadStream The underlying Asio AsyncReadStream type
  311 +
  312 + @par Example
  313 + @code
  314 + boost::asio::ip::tcp::socket socket(io);
  315 + capy::asio_read_stream reader(std::move(socket));
  316 +
  317 + auto result = co_await reader.read_some(buffer);
  318 + @endcode
  319 + */
  320 + template<typename AsyncReadStream>
  321 + struct asio_read_stream
  322 + {
  323 + using executor_type = typename AsyncReadStream::executor_type;
  324 + using next_layer_type = AsyncReadStream;
  325 +
  326 + /// Default constructor.
  327 + asio_read_stream() = default;
  328 +
  329 + /// Construct from an Asio stream.
HITGNC   330 + 1 explicit asio_read_stream(AsyncReadStream stream)
  331 + noexcept(std::is_nothrow_move_constructible_v<AsyncReadStream>)
HITGNC   332 + 1 : stream_(std::move(stream))
  333 + {
HITGNC   334 + 1 }
  335 +
  336 + asio_read_stream(const asio_read_stream&)
  337 + noexcept(std::is_nothrow_copy_constructible_v<AsyncReadStream>) = default;
HITGNC   338 + 1 asio_read_stream(asio_read_stream&&)
  339 + noexcept(std::is_nothrow_move_constructible_v<AsyncReadStream>) = default;
  340 + asio_read_stream& operator=(const asio_read_stream&)
  341 + noexcept(std::is_nothrow_copy_assignable_v<AsyncReadStream>) = default;
  342 + asio_read_stream& operator=(asio_read_stream&&)
  343 + noexcept(std::is_nothrow_move_assignable_v<AsyncReadStream>) = default;
  344 +
  345 + /// Returns the executor from the underlying stream.
  346 + executor_type get_executor() const { return stream_.get_executor(); }
  347 +
  348 + /// Initiates an async read, returning an IoAwaitable.
  349 + template<MutableBufferSequence Seq>
HITGNC   350 + 1 auto read_some(Seq buffers)
  351 + {
HITGNC   352 + 2 return stream_.async_read_some(as_asio_buffer_sequence(buffers), as_io_awaitable);
  353 + }
  354 +
  355 + /// Returns a reference to the underlying stream.
  356 + next_layer_type& next_layer() { return stream_; }
  357 + /// Returns a const reference to the underlying stream.
  358 + const next_layer_type& next_layer() const { return stream_; }
  359 +
  360 + private:
  361 + AsyncReadStream stream_;
  362 + };
  363 +
  364 + // Deduction guide
  365 + template<typename AsyncReadStream>
  366 + asio_read_stream(AsyncReadStream) -> asio_read_stream<AsyncReadStream>;
  367 +
  368 +
  369 + /** Wraps an Asio AsyncWriteStream for use with capy's WriteStream concept.
  370 +
  371 + Adapts an Asio `AsyncWriteStream` to provide capy-style `write_some`
  372 + operations that return an IoAwaitable.
  373 +
  374 + @tparam AsyncWriteStream The underlying Asio AsyncWriteStream type
  375 +
  376 + @par Example
  377 + @code
  378 + boost::asio::ip::tcp::socket socket(io);
  379 + capy::asio_write_stream writer(std::move(socket));
  380 +
  381 + auto result = co_await writer.write_some(buffer);
  382 + @endcode
  383 + */
  384 + template<typename AsyncWriteStream>
  385 + struct asio_write_stream
  386 + {
  387 + using executor_type = typename AsyncWriteStream::executor_type;
  388 + using next_layer_type = AsyncWriteStream;
  389 +
  390 + /// Default constructor.
  391 + asio_write_stream() = default;
  392 +
  393 + /// Construct from an Asio stream.
HITGNC   394 + 1 explicit asio_write_stream(AsyncWriteStream stream)
  395 + noexcept(std::is_nothrow_move_constructible_v<AsyncWriteStream>)
HITGNC   396 + 1 : stream_(std::move(stream))
  397 + {
HITGNC   398 + 1 }
  399 +
  400 + asio_write_stream(const asio_write_stream&)
  401 + noexcept(std::is_nothrow_copy_constructible_v<AsyncWriteStream>) = default;
HITGNC   402 + 1 asio_write_stream(asio_write_stream&&)
  403 + noexcept(std::is_nothrow_move_constructible_v<AsyncWriteStream>) = default;
  404 + asio_write_stream& operator=(const asio_write_stream&)
  405 + noexcept(std::is_nothrow_copy_assignable_v<AsyncWriteStream>) = default;
  406 + asio_write_stream& operator=(asio_write_stream&&)
  407 + noexcept(std::is_nothrow_move_assignable_v<AsyncWriteStream>) = default;
  408 +
  409 + /// Returns the executor from the underlying stream.
  410 + executor_type get_executor() const { return stream_.get_executor(); }
  411 +
  412 + /// Initiates an async write, returning an IoAwaitable.
  413 + template<ConstBufferSequence Seq>
HITGNC   414 + 1 auto write_some(Seq buffers)
  415 + {
HITGNC   416 + 2 return stream_.async_write_some(as_asio_buffer_sequence(buffers), as_io_awaitable);
  417 + }
  418 +
  419 + /// Returns a reference to the underlying stream.
  420 + next_layer_type& next_layer() { return stream_; }
  421 + /// Returns a const reference to the underlying stream.
  422 + const next_layer_type& next_layer() const { return stream_; }
  423 +
  424 + private:
  425 + AsyncWriteStream stream_;
  426 + };
  427 +
  428 + // Deduction guide
  429 + template<typename AsyncWriteStream>
  430 + asio_write_stream(AsyncWriteStream) -> asio_write_stream<AsyncWriteStream>;
  431 +
  432 +
  433 + /** Wraps an Asio AsyncStream for use with capy's Stream concept.
  434 +
  435 + Adapts an Asio stream (supporting both read and write) to provide
  436 + capy-style `read_some` and `write_some` operations that return
  437 + an IoAwaitable.
  438 +
  439 + @tparam AsyncStream The underlying Asio stream type
  440 +
  441 + @par Example
  442 + @code
  443 + boost::asio::ip::tcp::socket socket(io);
  444 + capy::asio_stream stream(std::move(socket));
  445 +
  446 + // Read
  447 + auto read_result = co_await stream.read_some(read_buf);
  448 +
  449 + // Write
  450 + auto write_result = co_await stream.write_some(write_buf);
  451 + @endcode
  452 + */
  453 + template<typename AsyncStream>
  454 + struct asio_stream
  455 + {
  456 + using executor_type = typename AsyncStream::executor_type;
  457 + using next_layer_type = AsyncStream;
  458 +
  459 + /// Default constructor.
  460 + asio_stream() = default;
  461 +
  462 + /// Construct from an Asio stream.
  463 + explicit asio_stream(AsyncStream stream)
  464 + noexcept(std::is_nothrow_move_constructible_v<AsyncStream>)
  465 + : stream_(std::move(stream))
  466 + {
  467 + }
  468 +
  469 + asio_stream(const asio_stream&)
  470 + noexcept(std::is_nothrow_copy_constructible_v<AsyncStream>) = default;
  471 + asio_stream(asio_stream&&)
  472 + noexcept(std::is_nothrow_move_constructible_v<AsyncStream>) = default;
  473 + asio_stream& operator=(const asio_stream&)
  474 + noexcept(std::is_nothrow_copy_assignable_v<AsyncStream>) = default;
  475 + asio_stream& operator=(asio_stream&&)
  476 + noexcept(std::is_nothrow_move_assignable_v<AsyncStream>) = default;
  477 +
  478 + /// Returns the executor from the underlying stream.
  479 + executor_type get_executor() const { return stream_.get_executor(); }
  480 +
  481 + /// Initiates an async read, returning an IoAwaitable.
  482 + template<MutableBufferSequence Seq>
  483 + auto read_some(Seq buffers)
  484 + {
  485 + return stream_.async_read_some(as_asio_buffer_sequence(buffers), as_io_awaitable);
  486 + }
  487 +
  488 + /// Initiates an async write, returning an IoAwaitable.
  489 + template<ConstBufferSequence Seq>
  490 + auto write_some(Seq buffers)
  491 + {
  492 + return stream_.async_write_some(as_asio_buffer_sequence(buffers), as_io_awaitable);
  493 + }
  494 +
  495 + /// Returns a reference to the underlying stream.
  496 + next_layer_type& next_layer() { return stream_; }
  497 + /// Returns a const reference to the underlying stream.
  498 + const next_layer_type& next_layer() const { return stream_; }
  499 +
  500 + private:
  501 + AsyncStream stream_;
  502 + };
  503 +
  504 + // Deduction guide
  505 + template<typename AsyncStream>
  506 + asio_stream(AsyncStream) -> asio_stream<AsyncStream>;
  507 +
  508 + }
  509 +
  510 + #endif //BOOST_CAPY_ASIO_STREAM_HPP
  511 +