100.00% Lines (29/29) 100.00% Functions (13/13)
TLA Baseline Branch
Line Hits Code Line Hits Code
  1 + //
  2 + // Copyright (c) 2025 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 +
  10 + #ifndef BOOST_CAPY_ASIO_BUFFERS_HPP
  11 + #define BOOST_CAPY_ASIO_BUFFERS_HPP
  12 +
  13 + #include <boost/capy/buffers.hpp>
  14 + #include <array>
  15 + #include <concepts>
  16 + #include <utility>
  17 +
  18 + namespace asio
  19 + {
  20 +
  21 + class mutable_buffer;
  22 + class const_buffer;
  23 +
  24 + }
  25 +
  26 + namespace boost::asio
  27 + {
  28 +
  29 + class mutable_buffer;
  30 + class const_buffer;
  31 +
  32 + }
  33 +
  34 + namespace boost::capy
  35 + {
  36 +
  37 + /** A const buffer type compatible with both capy and Asio.
  38 +
  39 + Extends capy's `const_buffer` with implicit conversion operators
  40 + to both standalone Asio (`::asio::const_buffer`) and Boost.Asio
  41 + (`boost::asio::const_buffer`) buffer types.
  42 +
  43 + This allows seamless use of capy buffers with Asio I/O operations
  44 + without explicit conversion.
  45 +
  46 + @par Example
  47 + @code
  48 + capy::asio_const_buffer buf(data, size);
  49 +
  50 + // Works directly with Asio operations
  51 + co_await socket.async_write_some(buf, capy::as_io_awaitable);
  52 + @endcode
  53 +
  54 + @see asio_mutable_buffer, as_asio_buffer_sequence
  55 + */
  56 + struct asio_const_buffer : const_buffer
  57 + {
  58 + using const_buffer::const_buffer;
  59 + using const_buffer::operator=;
  60 +
  61 + /// Construct from a capy const_buffer.
HITGNC   62 + 2 asio_const_buffer(const_buffer cb) : const_buffer(cb) {}
  63 +
  64 + /// Convert to standalone Asio const_buffer.
  65 + inline
  66 + operator ::asio::const_buffer() const;
  67 +
  68 + /// Convert to Boost.Asio const_buffer.
  69 + inline
  70 + operator boost::asio::const_buffer() const;
  71 + };
  72 +
  73 + /** A mutable buffer type compatible with both capy and Asio.
  74 +
  75 + Extends capy's `mutable_buffer` with implicit conversion operators
  76 + to both standalone Asio and Boost.Asio buffer types. Supports
  77 + conversion to both mutable and const buffer types.
  78 +
  79 + This allows seamless use of capy buffers with Asio I/O operations
  80 + without explicit conversion.
  81 +
  82 + @par Example
  83 + @code
  84 + char data[1024];
  85 + capy::asio_mutable_buffer buf(data, sizeof(data));
  86 +
  87 + // Works directly with Asio read operations
  88 + auto [ec, n] = co_await socket.async_read_some(buf, capy::as_io_awaitable);
  89 + @endcode
  90 +
  91 + @see asio_const_buffer, as_asio_buffer_sequence
  92 + */
  93 + struct asio_mutable_buffer : mutable_buffer
  94 + {
  95 + using mutable_buffer::mutable_buffer;
  96 + using mutable_buffer::operator=;
  97 +
  98 + /// Construct from a capy mutable_buffer.
HITGNC   99 + 2 asio_mutable_buffer(mutable_buffer mb) : mutable_buffer(mb) {}
  100 +
  101 + /// Convert to standalone Asio mutable_buffer.
  102 + inline
  103 + operator ::asio::mutable_buffer() const;
  104 +
  105 + /// Convert to Boost.Asio mutable_buffer.
  106 + inline
  107 + operator boost::asio::mutable_buffer() const;
  108 +
  109 + /// Convert to standalone Asio const_buffer.
  110 + inline
  111 + operator ::asio::const_buffer() const;
  112 +
  113 + /// Convert to Boost.Asio const_buffer.
  114 + inline
  115 + operator boost::asio::const_buffer() const;
  116 + };
  117 +
  118 +
  119 + namespace detail
  120 + {
  121 +
  122 + struct asio_buffer_transformer_t
  123 + {
  124 + inline
  125 + asio_mutable_buffer operator()(const boost::asio::mutable_buffer &mb) const noexcept;
  126 +
  127 + inline
  128 + asio_const_buffer operator()(const boost::asio::const_buffer &cb) const noexcept;
  129 +
  130 + inline
  131 + asio_mutable_buffer operator()(const ::asio::mutable_buffer &mb) const noexcept;
  132 +
  133 + inline
  134 + asio_const_buffer operator()(const ::asio::const_buffer &cb) const noexcept;
  135 +
  136 +
HITGNC   137 + 2 asio_mutable_buffer operator()(const mutable_buffer &mb) const noexcept
  138 + {
HITGNC   139 + 2 return mb;
  140 + }
  141 +
HITGNC   142 + 2 asio_const_buffer operator()(const const_buffer &cb) const noexcept
  143 + {
HITGNC   144 + 2 return cb;
  145 + }
  146 +
  147 + asio_buffer_transformer_t() noexcept = default;
  148 + asio_buffer_transformer_t(const asio_buffer_transformer_t &) noexcept = default;
  149 + };
  150 +
  151 + constexpr static asio_buffer_transformer_t asio_buffer_transformer;
  152 +
  153 + /** A bidirectional iterator that transforms buffer types using asio_buffer_transformer.
  154 + *
  155 + * Wraps an underlying bidirectional iterator and applies asio_buffer_transformer
  156 + * to each dereferenced value, converting between Asio buffer types and capy
  157 + * asio_const_buffer/asio_mutable_buffer types.
  158 + *
  159 + * @tparam Iterator The underlying bidirectional iterator type
  160 + */
  161 + template<typename Iterator>
  162 + class asio_buffer_iterator
  163 + {
  164 + public:
  165 + using iterator_type = Iterator;
  166 + using iterator_category = std::bidirectional_iterator_tag;
  167 + using difference_type = typename std::iterator_traits<Iterator>::difference_type;
  168 + using underlying_value = typename std::iterator_traits<Iterator>::value_type;
  169 + using value_type = decltype(asio_buffer_transformer(std::declval<underlying_value>()));
  170 + using reference = value_type;
  171 + using pointer = void;
  172 +
  173 + asio_buffer_iterator() = default;
  174 +
HITGNC   175 + 20 explicit asio_buffer_iterator(Iterator it)
  176 + noexcept(std::is_nothrow_move_constructible_v<Iterator>)
HITGNC   177 + 20 : it_(std::move(it))
  178 + {
HITGNC   179 + 20 }
  180 +
  181 + asio_buffer_iterator(const asio_buffer_iterator&) = default;
  182 + asio_buffer_iterator(asio_buffer_iterator&&) = default;
  183 + asio_buffer_iterator& operator=(const asio_buffer_iterator&) = default;
  184 + asio_buffer_iterator& operator=(asio_buffer_iterator&&) = default;
  185 +
HITGNC   186 + 10 reference operator*() const
  187 + noexcept(noexcept(asio_buffer_transformer(*std::declval<Iterator>())))
  188 + {
HITGNC   189 + 10 return asio_buffer_transformer(*it_);
  190 + }
  191 +
HITGNC   192 + 4 asio_buffer_iterator& operator++()
  193 + noexcept(noexcept(++std::declval<Iterator&>()))
  194 + {
HITGNC   195 + 4 ++it_;
HITGNC   196 + 4 return *this;
  197 + }
  198 +
HITGNC   199 + 3 asio_buffer_iterator operator++(int)
  200 + noexcept(noexcept(std::declval<Iterator&>()++))
  201 + {
HITGNC   202 + 3 asio_buffer_iterator tmp(*this);
HITGNC   203 + 3 ++it_;
HITGNC   204 + 3 return tmp;
  205 + }
  206 +
  207 + asio_buffer_iterator& operator--()
  208 + noexcept(noexcept(--std::declval<Iterator&>()))
  209 + {
  210 + --it_;
  211 + return *this;
  212 + }
  213 +
  214 + asio_buffer_iterator operator--(int)
  215 + noexcept(noexcept(std::declval<Iterator&>()--))
  216 + {
  217 + asio_buffer_iterator tmp(*this);
  218 + --it_;
  219 + return tmp;
  220 + }
  221 +
  222 + bool operator==(const asio_buffer_iterator& other) const
  223 + noexcept(noexcept(std::declval<Iterator>() == std::declval<Iterator>()))
  224 + {
  225 + return it_ == other.it_;
  226 + }
  227 +
HITGNC   228 + 13 bool operator!=(const asio_buffer_iterator& other) const
  229 + noexcept(noexcept(std::declval<Iterator>() != std::declval<Iterator>()))
  230 + {
HITGNC   231 + 13 return it_ != other.it_;
  232 + }
  233 +
  234 + /// Returns the underlying iterator.
  235 + Iterator base() const noexcept(std::is_nothrow_copy_constructible_v<Iterator>)
  236 + {
  237 + return it_;
  238 + }
  239 +
  240 + private:
  241 + Iterator it_{};
  242 + };
  243 +
  244 + // Deduction guide
  245 + template<typename Iterator>
  246 + asio_buffer_iterator(Iterator) -> asio_buffer_iterator<Iterator>;
  247 +
  248 +
  249 + /** A bidirectional range that transforms buffer sequences using asio_buffer_transformer.
  250 + *
  251 + * Wraps a buffer sequence and provides begin/end iterators that transform
  252 + * buffer elements via asio_buffer_transformer. Satisfies the requirements
  253 + * of std::ranges::bidirectional_range.
  254 + *
  255 + * @tparam Sequence The underlying buffer sequence type
  256 + */
  257 + template<typename Sequence>
  258 + class asio_buffer_range
  259 + {
  260 + public:
  261 + using sequence_type = Sequence;
  262 + using iterator = asio_buffer_iterator<
  263 + decltype(std::ranges::begin(std::declval<const Sequence&>()))>;
  264 + using const_iterator = iterator;
  265 +
  266 + asio_buffer_range() = default;
  267 +
HITGNC   268 + 2 explicit asio_buffer_range(Sequence seq)
  269 + noexcept(std::is_nothrow_move_constructible_v<Sequence>)
HITGNC   270 + 2 : seq_(std::move(seq))
  271 + {
HITGNC   272 + 2 }
  273 +
  274 + asio_buffer_range(const asio_buffer_range&) = default;
  275 + asio_buffer_range(asio_buffer_range&&) = default;
  276 + asio_buffer_range& operator=(const asio_buffer_range&) = default;
  277 + asio_buffer_range& operator=(asio_buffer_range&&) = default;
  278 +
HITGNC   279 + 4 iterator begin() const
  280 + noexcept(noexcept(std::ranges::begin(std::declval<const Sequence&>())))
  281 + {
HITGNC   282 + 4 return iterator(std::ranges::begin(seq_));
  283 + }
  284 +
HITGNC   285 + 4 iterator end() const
  286 + noexcept(noexcept(std::ranges::end(std::declval<const Sequence&>())))
  287 + {
HITGNC   288 + 4 return iterator(std::ranges::end(seq_));
  289 + }
  290 +
  291 + /// Returns true if the range is empty.
  292 + bool empty() const
  293 + noexcept(noexcept(std::ranges::empty(std::declval<const Sequence&>())))
  294 + {
  295 + return std::ranges::empty(seq_);
  296 + }
  297 +
  298 + /// Returns the underlying sequence.
  299 + const Sequence& base() const noexcept { return seq_; }
  300 +
  301 + private:
  302 + Sequence seq_{};
  303 + };
  304 +
  305 + // Deduction guide
  306 + template<typename Sequence>
  307 + asio_buffer_range(Sequence) -> asio_buffer_range<Sequence>;
  308 +
  309 + }
  310 +
  311 +
  312 + /** @defgroup as_asio_buffer_sequence as_asio_buffer_sequence
  313 +
  314 + Bidirectional conversion between Asio and capy buffer sequences.
  315 +
  316 + The `as_asio_buffer_sequence` function provides seamless conversion
  317 + in both directions:
  318 +
  319 + - **Capy to Asio**: Convert capy buffer sequences for use with Asio
  320 + I/O operations. The returned range satisfies Asio's
  321 + `MutableBufferSequence` or `ConstBufferSequence` requirements.
  322 +
  323 + - **Asio to Capy**: Convert Asio buffer sequences for use with capy's
  324 + buffer concepts. The returned range satisfies capy's
  325 + `MutableBufferSequence` or `ConstBufferSequence` concepts.
  326 +
  327 + @par Example: Capy to Asio
  328 + @code
  329 + capy::mutable_buffer_pair buffers = ...;
  330 + auto seq = capy::as_asio_buffer_sequence(buffers);
  331 + co_await socket.async_read_some(seq, capy::as_io_awaitable);
  332 + @endcode
  333 +
  334 + @par Example: Asio to Capy
  335 + @code
  336 + std::vector<boost::asio::mutable_buffer> asio_bufs = ...;
  337 + auto seq = capy::as_asio_buffer_sequence(asio_bufs);
  338 + std::size_t total = capy::buffer_size(seq); // Use capy algorithms
  339 + @endcode
  340 +
  341 + @{
  342 + */
  343 +
  344 + /** Pass through a range already containing asio_const_buffer elements.
  345 +
  346 + @param rng A bidirectional range of asio_const_buffer
  347 + @return The range forwarded unchanged
  348 + */
  349 + template<std::ranges::bidirectional_range<> Range>
  350 + requires std::same_as<std::ranges::range_value_t<Range>, asio_const_buffer>
  351 + auto as_asio_buffer_sequence(Range && rng)
  352 + {
  353 + return std::forward<Range>(rng);
  354 + }
  355 +
  356 + /** Pass through a range already containing asio_mutable_buffer elements.
  357 +
  358 + @param rng A bidirectional range of asio_mutable_buffer
  359 + @return The range forwarded unchanged
  360 + */
  361 + template<std::ranges::bidirectional_range<> Range>
  362 + requires std::same_as<std::ranges::range_value_t<Range>, asio_mutable_buffer>
  363 + auto as_asio_buffer_sequence(Range && rng)
  364 + {
  365 + return std::forward<Range>(rng);
  366 + }
  367 +
  368 + /** Convert a single asio_mutable_buffer to a buffer sequence.
  369 +
  370 + @param mb The mutable buffer
  371 + @return A single-element array containing the buffer
  372 + */
  373 + inline
  374 + auto as_asio_buffer_sequence(asio_mutable_buffer mb)
  375 + {
  376 + return std::array<asio_mutable_buffer, 1u>{mb};
  377 + }
  378 +
  379 + /** Convert a single asio_const_buffer to a buffer sequence.
  380 +
  381 + @param cb The const buffer
  382 + @return A single-element array containing the buffer
  383 + */
  384 + inline
  385 + auto as_asio_buffer_sequence(asio_const_buffer cb)
  386 + {
  387 + return std::array<asio_const_buffer, 1u>{cb};
  388 + }
  389 +
  390 + /** Convert a single capy mutable_buffer to a buffer sequence.
  391 +
  392 + @param mb The mutable buffer
  393 + @return A single-element array containing an asio_mutable_buffer
  394 + */
  395 + inline
  396 + auto as_asio_buffer_sequence(mutable_buffer mb)
  397 + {
  398 + return std::array<asio_mutable_buffer, 1u>{mb};
  399 + }
  400 +
  401 + /** Convert a buffer type that might support both mutable & const buffer
  402 + to an asio buffer sequence.
  403 +
  404 + @param buf The const buffer
  405 + @return A single-element array containing an asio_mutable_buffer
  406 +
  407 + */
  408 + template<std::convertible_to<mutable_buffer> Buffer>
  409 + inline
  410 + auto as_asio_buffer_sequence(const Buffer & buf)
  411 + {
  412 + return as_asio_buffer_sequence(static_cast<mutable_buffer>(buf));
  413 + }
  414 +
  415 + /** Convert a single capy const_buffer to a buffer sequence.
  416 + @param cb The const buffer
  417 + @return A single-element array containing an asio_const_buffer
  418 +
  419 + */
  420 + inline
  421 + auto as_asio_buffer_sequence(const_buffer cb)
  422 + {
  423 + return std::array<asio_const_buffer, 1u>{cb};
  424 + }
  425 +
  426 + /** Convert any buffer sequence for bidirectional Asio/capy compatibility.
  427 +
  428 + Wraps the buffer sequence in a transforming range that converts
  429 + each buffer element to asio_const_buffer or asio_mutable_buffer.
  430 + The returned range satisfies both Asio's buffer sequence requirements
  431 + and capy's buffer sequence concepts.
  432 +
  433 + This overload handles:
  434 + - Capy buffer sequences (for use with Asio operations)
  435 + - Asio buffer sequences (for use with capy concepts/algorithms)
  436 +
  437 + @param seq The buffer sequence to convert
  438 + @return A transforming range over the buffer sequence
  439 + */
  440 + template<ConstBufferSequence Seq>
  441 + requires (!std::convertible_to<Seq, const_buffer> && !std::convertible_to<Seq, mutable_buffer>)
  442 + inline
HITGNC   443 + 2 auto as_asio_buffer_sequence(const Seq & seq)
  444 + {
HITGNC   445 + 2 return detail::asio_buffer_range(seq);
  446 + }
  447 +
  448 + /** @} */
  449 +
  450 + }
  451 +
  452 +
  453 + #endif // BOOST_CAPY_ASIO_BUFFERS_HPP
  454 +