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