LCOV - code coverage report
Current view: top level - capy/io - any_stream.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 46 46
Test Date: 2026-08-21 22:12:46 Functions: 77.8 % 18 14 4

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/capy
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_CAPY_IO_ANY_STREAM_HPP
      12                 : #define BOOST_CAPY_IO_ANY_STREAM_HPP
      13                 : 
      14                 : #include <boost/capy/detail/config.hpp>
      15                 : #include <boost/capy/concept/read_stream.hpp>
      16                 : #include <boost/capy/concept/write_stream.hpp>
      17                 : #include <boost/capy/io/any_read_stream.hpp>
      18                 : #include <boost/capy/io/any_write_stream.hpp>
      19                 : 
      20                 : #include <concepts>
      21                 : 
      22                 : namespace boost {
      23                 : namespace capy {
      24                 : 
      25                 : /** Dispatches `read_some` and `write_some` through independent type-erased vtables.
      26                 : 
      27                 :     This class provides type erasure for any type satisfying both
      28                 :     the @ref ReadStream and @ref WriteStream concepts, enabling
      29                 :     runtime polymorphism for bidirectional I/O operations.
      30                 : 
      31                 :     Inherits from both @ref any_read_stream and @ref any_write_stream,
      32                 :     providing `read_some` and `write_some` operations. Each base
      33                 :     maintains its own cached awaitable storage, allowing concurrent
      34                 :     read and write operations.
      35                 : 
      36                 :     The wrapper supports two construction modes:
      37                 :     - **Owning**: Pass by value to transfer ownership. The wrapper
      38                 :       allocates storage and owns the stream.
      39                 :     - **Reference**: Pass a pointer to wrap without ownership. The
      40                 :       pointed-to stream must outlive this wrapper.
      41                 : 
      42                 :     @par Implicit Conversion
      43                 :     This class implicitly converts to `any_read_stream&` or
      44                 :     `any_write_stream&`, allowing it to be passed to functions
      45                 :     that accept only one capability. However, do not move through
      46                 :     a base reference as this would leave the other base in an
      47                 :     invalid state.
      48                 : 
      49                 :     @par Thread Safety
      50                 :     Not thread-safe. Concurrent operations of the same type
      51                 :     (two reads or two writes) are undefined behavior. One read
      52                 :     and one write may be in flight simultaneously.
      53                 : 
      54                 :     @par Example
      55                 :     @par !example example
      56                 : 
      57                 : 
      58                 :     @see any_read_stream, any_write_stream, ReadStream, WriteStream
      59                 : */
      60                 : class any_stream
      61                 :     : public any_read_stream
      62                 :     , public any_write_stream
      63                 : {
      64                 :     void* storage_ = nullptr;
      65                 :     void* stream_ptr_ = nullptr;
      66                 :     void (*destroy_)(void*) noexcept = nullptr;
      67                 : 
      68                 : public:
      69                 :     /** Destructor.
      70                 : 
      71                 :         Destroys the owned stream (if any). Base class destructors
      72                 :         handle their cached awaitable storage.
      73                 :     */
      74 HIT          39 :     ~any_stream()
      75                 :     {
      76              39 :         if(storage_)
      77                 :         {
      78               3 :             destroy_(stream_ptr_);
      79               3 :             ::operator delete(storage_);
      80                 :         }
      81              39 :     }
      82                 : 
      83                 :     /** Construct a default instance.
      84                 : 
      85                 :         Constructs an empty wrapper. @ref has_value and `operator bool`
      86                 :         report the empty state; calling `read_some` or `write_some`
      87                 :         before the wrapper holds a stream is undefined behavior.
      88                 :     */
      89                 :     any_stream() = default;
      90                 : 
      91                 :     /** Non-copyable.
      92                 : 
      93                 :         The awaitable caches are per-instance and cannot be shared.
      94                 : 
      95                 :         @param other The wrapper that would be copied.
      96                 :     */
      97                 :     any_stream(any_stream const& other) = delete;
      98                 : 
      99                 :     /** Copy assignment is disabled.
     100                 : 
     101                 :         The awaitable caches are per-instance and cannot be shared.
     102                 : 
     103                 :         @param other The wrapper that would be assigned from.
     104                 : 
     105                 :         @return A reference to `*this`.
     106                 :     */
     107                 :     any_stream& operator=(any_stream const& other) = delete;
     108                 : 
     109                 :     /** Construct by moving.
     110                 : 
     111                 :         Transfers ownership from both bases and the owned stream (if any).
     112                 : 
     113                 :         @param other The wrapper to move from.
     114                 :     */
     115               1 :     any_stream(any_stream&& other) noexcept
     116               1 :         : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
     117               1 :         , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
     118               1 :         , storage_(std::exchange(other.storage_, nullptr))
     119               1 :         , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
     120               2 :         , destroy_(std::exchange(other.destroy_, nullptr))
     121                 :     {
     122               1 :     }
     123                 : 
     124                 :     /** Assign by moving.
     125                 : 
     126                 :         Destroys any owned stream and releases existing resources,
     127                 :         then transfers ownership from `other`.
     128                 : 
     129                 :         @param other The wrapper to move from.
     130                 :         @return Reference to this wrapper.
     131                 :     */
     132                 :     any_stream&
     133               2 :     operator=(any_stream&& other) noexcept
     134                 :     {
     135               2 :         if(this != &other)
     136                 :         {
     137               2 :             if(storage_)
     138                 :             {
     139               1 :                 destroy_(stream_ptr_);
     140               1 :                 ::operator delete(storage_);
     141                 :             }
     142                 :             static_cast<any_read_stream&>(*this) =
     143               2 :                 std::move(static_cast<any_read_stream&>(other));
     144                 :             static_cast<any_write_stream&>(*this) =
     145               2 :                 std::move(static_cast<any_write_stream&>(other));
     146               2 :             storage_ = std::exchange(other.storage_, nullptr);
     147               2 :             stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
     148               2 :             destroy_ = std::exchange(other.destroy_, nullptr);
     149                 :         }
     150               2 :         return *this;
     151                 :     }
     152                 : 
     153                 :     /** Construct by taking ownership of a bidirectional stream.
     154                 : 
     155                 :         Allocates storage and moves the stream into this wrapper.
     156                 :         The wrapper owns the stream and destroys it.
     157                 : 
     158                 :         @param s The stream to take ownership of. Must satisfy both
     159                 :             ReadStream and WriteStream concepts.
     160                 :     */
     161                 :     template<class S>
     162                 :         requires ReadStream<S> && WriteStream<S> &&
     163                 :             (!std::same_as<std::decay_t<S>, any_stream>)
     164               4 :     any_stream(S s)
     165               4 :     {
     166                 :         struct guard {
     167                 :             any_stream* self;
     168                 :             void* ptr = nullptr;
     169                 :             bool committed = false;
     170               4 :             ~guard() {
     171               4 :                 if(!committed && ptr) {
     172                 :                     static_cast<S*>(ptr)->~S(); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
     173                 :                     ::operator delete(self->storage_); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
     174                 :                     self->storage_ = nullptr; // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
     175                 :                 }
     176               4 :             }
     177               4 :         } g{this};
     178                 : 
     179               4 :         storage_ = ::operator new(sizeof(S));
     180               4 :         S* ptr = ::new(storage_) S(std::move(s));
     181               4 :         g.ptr = ptr;
     182               4 :         stream_ptr_ = ptr;
     183               8 :         destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); };
     184                 : 
     185                 :         // Initialize bases with pointer (reference semantics)
     186               4 :         static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
     187               4 :         static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
     188                 : 
     189               4 :         g.committed = true;
     190               4 :     }
     191                 : 
     192                 :     /** Construct by wrapping a bidirectional stream without ownership.
     193                 : 
     194                 :         Wraps the given stream by pointer. The stream must remain
     195                 :         valid for the lifetime of this wrapper.
     196                 : 
     197                 :         @param s Pointer to the stream to wrap. Must satisfy both
     198                 :             ReadStream and WriteStream concepts.
     199                 :     */
     200                 :     template<class S>
     201                 :         requires ReadStream<S> && WriteStream<S>
     202              32 :     any_stream(S* s)
     203                 :         : any_read_stream(s)
     204              32 :         , any_write_stream(s)
     205                 :     {
     206                 :         // storage_ remains nullptr - no ownership
     207              32 :     }
     208                 : 
     209                 :     /** Check if the wrapper contains a valid stream.
     210                 : 
     211                 :         Both bases must be valid for the wrapper to be valid.
     212                 : 
     213                 :         @return `true` if wrapping a stream, `false` if default-constructed
     214                 :             or moved-from.
     215                 :     */
     216                 :     bool
     217              12 :     has_value() const noexcept
     218                 :     {
     219              19 :         return any_read_stream::has_value() &&
     220              19 :                any_write_stream::has_value();
     221                 :     }
     222                 : 
     223                 :     /** Check if the wrapper contains a valid stream.
     224                 : 
     225                 :         Both bases must be valid for the wrapper to be valid.
     226                 : 
     227                 :         @return `true` if wrapping a stream, `false` if default-constructed
     228                 :             or moved-from.
     229                 :     */
     230                 :     explicit
     231               2 :     operator bool() const noexcept
     232                 :     {
     233               2 :         return has_value();
     234                 :     }
     235                 : };
     236                 : 
     237                 : } // namespace capy
     238                 : } // namespace boost
     239                 : 
     240                 : #endif
        

Generated by: LCOV version 2.3