LCOV - code coverage report
Current view: top level - capy/io - any_stream.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 50 50
Test Date: 2026-10-17 15:55:14 Functions: 100.0 % 16 16

           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                 :         Ends any operation in flight, then destroys the owned stream
      72                 :         (if any). Base class destructors release their cached awaitable
      73                 :         storage.
      74                 :     */
      75 HIT          39 :     ~any_stream()
      76                 :     {
      77              39 :         any_read_stream::end_operation();
      78              39 :         any_write_stream::end_operation();
      79              39 :         if(storage_)
      80                 :         {
      81               7 :             destroy_(stream_ptr_);
      82               7 :             ::operator delete(storage_);
      83                 :         }
      84              39 :     }
      85                 : 
      86                 :     /** Construct a default instance.
      87                 : 
      88                 :         Constructs an empty wrapper. @ref has_value and `operator bool`
      89                 :         report the empty state; calling `read_some` or `write_some`
      90                 :         before the wrapper holds a stream is undefined behavior.
      91                 :     */
      92                 :     any_stream() = default;
      93                 : 
      94                 :     /** Non-copyable.
      95                 : 
      96                 :         The awaitable caches are per-instance and cannot be shared.
      97                 : 
      98                 :         @param other The wrapper that would be copied.
      99                 :     */
     100                 :     any_stream(any_stream const& other) = delete;
     101                 : 
     102                 :     /** Copy assignment is disabled.
     103                 : 
     104                 :         The awaitable caches are per-instance and cannot be shared.
     105                 : 
     106                 :         @param other The wrapper that would be assigned from.
     107                 : 
     108                 :         @return A reference to `*this`.
     109                 :     */
     110                 :     any_stream& operator=(any_stream const& other) = delete;
     111                 : 
     112                 :     /** Construct by moving.
     113                 : 
     114                 :         Transfers ownership from both bases and the owned stream (if any).
     115                 : 
     116                 :         @param other The wrapper to move from.
     117                 :     */
     118               1 :     any_stream(any_stream&& other) noexcept
     119               1 :         : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
     120               1 :         , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
     121               1 :         , storage_(std::exchange(other.storage_, nullptr))
     122               1 :         , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
     123               2 :         , destroy_(std::exchange(other.destroy_, nullptr))
     124                 :     {
     125               1 :     }
     126                 : 
     127                 :     /** Assign by moving.
     128                 : 
     129                 :         Destroys any owned stream and releases existing resources,
     130                 :         then transfers ownership from `other`.
     131                 : 
     132                 :         @param other The wrapper to move from.
     133                 :         @return Reference to this wrapper.
     134                 :     */
     135                 :     any_stream&
     136               4 :     operator=(any_stream&& other) noexcept
     137                 :     {
     138               4 :         if(this != &other)
     139                 :         {
     140               4 :             any_read_stream::end_operation();
     141               4 :             any_write_stream::end_operation();
     142               4 :             if(storage_)
     143                 :             {
     144               3 :                 destroy_(stream_ptr_);
     145               3 :                 ::operator delete(storage_);
     146                 :             }
     147                 :             static_cast<any_read_stream&>(*this) =
     148               4 :                 std::move(static_cast<any_read_stream&>(other));
     149                 :             static_cast<any_write_stream&>(*this) =
     150               4 :                 std::move(static_cast<any_write_stream&>(other));
     151               4 :             storage_ = std::exchange(other.storage_, nullptr);
     152               4 :             stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
     153               4 :             destroy_ = std::exchange(other.destroy_, nullptr);
     154                 :         }
     155               4 :         return *this;
     156                 :     }
     157                 : 
     158                 :     /** Construct by taking ownership of a bidirectional stream.
     159                 : 
     160                 :         Allocates storage and moves the stream into this wrapper.
     161                 :         The wrapper owns the stream and destroys it.
     162                 : 
     163                 :         @param s The stream to take ownership of. Must satisfy both
     164                 :             ReadStream and WriteStream concepts.
     165                 :     */
     166                 :     template<class S>
     167                 :         requires ReadStream<S> && WriteStream<S> &&
     168                 :             (!std::same_as<std::decay_t<S>, any_stream>)
     169              10 :     any_stream(S s)
     170              10 :     {
     171                 :         struct guard {
     172                 :             any_stream* self;
     173                 :             void* ptr = nullptr;
     174                 :             bool committed = false;
     175              10 :             ~guard() {
     176              10 :                 if(!committed && ptr) {
     177                 :                     static_cast<S*>(ptr)->~S(); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
     178                 :                     ::operator delete(self->storage_); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
     179                 :                     self->storage_ = nullptr; // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
     180                 :                 }
     181              10 :             }
     182              10 :         } g{this};
     183                 : 
     184              10 :         storage_ = ::operator new(sizeof(S));
     185              10 :         S* ptr = ::new(storage_) S(std::move(s));
     186              10 :         g.ptr = ptr;
     187              10 :         stream_ptr_ = ptr;
     188              20 :         destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); };
     189                 : 
     190                 :         // Initialize bases with pointer (reference semantics)
     191              10 :         static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
     192              10 :         static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
     193                 : 
     194              10 :         g.committed = true;
     195              10 :     }
     196                 : 
     197                 :     /** Construct by wrapping a bidirectional stream without ownership.
     198                 : 
     199                 :         Wraps the given stream by pointer. The stream must remain
     200                 :         valid for the lifetime of this wrapper.
     201                 : 
     202                 :         @param s Pointer to the stream to wrap. Must satisfy both
     203                 :             ReadStream and WriteStream concepts.
     204                 :     */
     205                 :     template<class S>
     206                 :         requires ReadStream<S> && WriteStream<S>
     207              26 :     any_stream(S* s)
     208                 :         : any_read_stream(s)
     209              26 :         , any_write_stream(s)
     210                 :     {
     211                 :         // storage_ remains nullptr - no ownership
     212              26 :     }
     213                 : 
     214                 :     /** Check if the wrapper contains a valid stream.
     215                 : 
     216                 :         Both bases must be valid for the wrapper to be valid.
     217                 : 
     218                 :         @return `true` if wrapping a stream, `false` if default-constructed
     219                 :             or moved-from.
     220                 :     */
     221                 :     bool
     222              12 :     has_value() const noexcept
     223                 :     {
     224              19 :         return any_read_stream::has_value() &&
     225              19 :                any_write_stream::has_value();
     226                 :     }
     227                 : 
     228                 :     /** Check if the wrapper contains a valid stream.
     229                 : 
     230                 :         Both bases must be valid for the wrapper to be valid.
     231                 : 
     232                 :         @return `true` if wrapping a stream, `false` if default-constructed
     233                 :             or moved-from.
     234                 :     */
     235                 :     explicit
     236               2 :     operator bool() const noexcept
     237                 :     {
     238               2 :         return has_value();
     239                 :     }
     240                 : };
     241                 : 
     242                 : } // namespace capy
     243                 : } // namespace boost
     244                 : 
     245                 : #endif
        

Generated by: LCOV version 2.3