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