100.00% Lines (46/46) 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 - @code 55 + @par !example example
56 - void reader(any_read_stream&);  
57 - void writer(any_write_stream&);  
58 -  
59 - // Owning - takes ownership of the stream  
60 - any_stream owning_stream(socket{ioc});  
61 -  
62 - // Reference - wraps without ownership  
63 - socket sock(ioc);  
64 - any_stream ref_stream(&sock);  
65 -  
66 - // Use read_some from the any_read_stream base  
67 - char rdata[1024];  
68 - mutable_buffer rbuf(rdata, sizeof(rdata));  
69 - auto [ec1, n1] = co_await owning_stream.read_some(std::span(&rbuf, 1));  
70 -  
71 - // Use write_some from the any_write_stream base  
72 - char wdata[] = "hello";  
73 - const_buffer wbuf(wdata, sizeof(wdata));  
74 - auto [ec2, n2] = co_await owning_stream.write_some(std::span(&wbuf, 1));  
75 - // Pass to functions expecting one capability  
76 - reader(owning_stream); // Implicit upcast  
77 - writer(owning_stream); // Implicit upcast  
78 - @endcode  
79   56  
80   57  
81   @see any_read_stream, any_write_stream, ReadStream, WriteStream 58   @see any_read_stream, any_write_stream, ReadStream, WriteStream
82   */ 59   */
83   class any_stream 60   class any_stream
84   : public any_read_stream 61   : public any_read_stream
85   , public any_write_stream 62   , public any_write_stream
86   { 63   {
87   void* storage_ = nullptr; 64   void* storage_ = nullptr;
88   void* stream_ptr_ = nullptr; 65   void* stream_ptr_ = nullptr;
89   void (*destroy_)(void*) noexcept = nullptr; 66   void (*destroy_)(void*) noexcept = nullptr;
90   67  
91   public: 68   public:
92   /** Destructor. 69   /** Destructor.
93   70  
94   Destroys the owned stream (if any). Base class destructors 71   Destroys the owned stream (if any). Base class destructors
95   handle their cached awaitable storage. 72   handle their cached awaitable storage.
96   */ 73   */
HITCBC 97   39 ~any_stream() 74   39 ~any_stream()
98   { 75   {
HITCBC 99   39 if(storage_) 76   39 if(storage_)
100   { 77   {
HITCBC 101   3 destroy_(stream_ptr_); 78   3 destroy_(stream_ptr_);
HITCBC 102   3 ::operator delete(storage_); 79   3 ::operator delete(storage_);
103   } 80   }
HITCBC 104   39 } 81   39 }
105   82  
106   /** Construct a default instance. 83   /** Construct a default instance.
107   84  
108   Constructs an empty wrapper. @ref has_value and `operator bool` 85   Constructs an empty wrapper. @ref has_value and `operator bool`
109   report the empty state; calling `read_some` or `write_some` 86   report the empty state; calling `read_some` or `write_some`
110   before the wrapper holds a stream is undefined behavior. 87   before the wrapper holds a stream is undefined behavior.
111   */ 88   */
112   any_stream() = default; 89   any_stream() = default;
113   90  
114   /** Non-copyable. 91   /** Non-copyable.
115   92  
116   The awaitable caches are per-instance and cannot be shared. 93   The awaitable caches are per-instance and cannot be shared.
117   94  
118   @param other The wrapper that would be copied. 95   @param other The wrapper that would be copied.
119   */ 96   */
120   any_stream(any_stream const& other) = delete; 97   any_stream(any_stream const& other) = delete;
121   98  
122   /** Copy assignment is disabled. 99   /** Copy assignment is disabled.
123   100  
124   The awaitable caches are per-instance and cannot be shared. 101   The awaitable caches are per-instance and cannot be shared.
125   102  
126   @param other The wrapper that would be assigned from. 103   @param other The wrapper that would be assigned from.
127   104  
128   @return A reference to `*this`. 105   @return A reference to `*this`.
129   */ 106   */
130   any_stream& operator=(any_stream const& other) = delete; 107   any_stream& operator=(any_stream const& other) = delete;
131   108  
132   /** Construct by moving. 109   /** Construct by moving.
133   110  
134   Transfers ownership from both bases and the owned stream (if any). 111   Transfers ownership from both bases and the owned stream (if any).
135   112  
136   @param other The wrapper to move from. 113   @param other The wrapper to move from.
137   */ 114   */
HITCBC 138   1 any_stream(any_stream&& other) noexcept 115   1 any_stream(any_stream&& other) noexcept
HITCBC 139   1 : any_read_stream(std::move(static_cast<any_read_stream&>(other))) 116   1 : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
HITCBC 140   1 , any_write_stream(std::move(static_cast<any_write_stream&>(other))) 117   1 , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
HITCBC 141   1 , storage_(std::exchange(other.storage_, nullptr)) 118   1 , storage_(std::exchange(other.storage_, nullptr))
HITCBC 142   1 , stream_ptr_(std::exchange(other.stream_ptr_, nullptr)) 119   1 , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
HITCBC 143   2 , destroy_(std::exchange(other.destroy_, nullptr)) 120   2 , destroy_(std::exchange(other.destroy_, nullptr))
144   { 121   {
HITCBC 145   1 } 122   1 }
146   123  
147   /** Assign by moving. 124   /** Assign by moving.
148   125  
149   Destroys any owned stream and releases existing resources, 126   Destroys any owned stream and releases existing resources,
150   then transfers ownership from `other`. 127   then transfers ownership from `other`.
151   128  
152   @param other The wrapper to move from. 129   @param other The wrapper to move from.
153   @return Reference to this wrapper. 130   @return Reference to this wrapper.
154   */ 131   */
155   any_stream& 132   any_stream&
HITCBC 156   2 operator=(any_stream&& other) noexcept 133   2 operator=(any_stream&& other) noexcept
157   { 134   {
HITCBC 158   2 if(this != &other) 135   2 if(this != &other)
159   { 136   {
HITCBC 160   2 if(storage_) 137   2 if(storage_)
161   { 138   {
HITCBC 162   1 destroy_(stream_ptr_); 139   1 destroy_(stream_ptr_);
HITCBC 163   1 ::operator delete(storage_); 140   1 ::operator delete(storage_);
164   } 141   }
165   static_cast<any_read_stream&>(*this) = 142   static_cast<any_read_stream&>(*this) =
HITCBC 166   2 std::move(static_cast<any_read_stream&>(other)); 143   2 std::move(static_cast<any_read_stream&>(other));
167   static_cast<any_write_stream&>(*this) = 144   static_cast<any_write_stream&>(*this) =
HITCBC 168   2 std::move(static_cast<any_write_stream&>(other)); 145   2 std::move(static_cast<any_write_stream&>(other));
HITCBC 169   2 storage_ = std::exchange(other.storage_, nullptr); 146   2 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 170   2 stream_ptr_ = std::exchange(other.stream_ptr_, nullptr); 147   2 stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
HITCBC 171   2 destroy_ = std::exchange(other.destroy_, nullptr); 148   2 destroy_ = std::exchange(other.destroy_, nullptr);
172   } 149   }
HITCBC 173   2 return *this; 150   2 return *this;
174   } 151   }
175   152  
176   /** Construct by taking ownership of a bidirectional stream. 153   /** Construct by taking ownership of a bidirectional stream.
177   154  
178   Allocates storage and moves the stream into this wrapper. 155   Allocates storage and moves the stream into this wrapper.
179   The wrapper owns the stream and destroys it. 156   The wrapper owns the stream and destroys it.
180   157  
181   @param s The stream to take ownership of. Must satisfy both 158   @param s The stream to take ownership of. Must satisfy both
182   ReadStream and WriteStream concepts. 159   ReadStream and WriteStream concepts.
183   */ 160   */
184   template<class S> 161   template<class S>
185   requires ReadStream<S> && WriteStream<S> && 162   requires ReadStream<S> && WriteStream<S> &&
186   (!std::same_as<std::decay_t<S>, any_stream>) 163   (!std::same_as<std::decay_t<S>, any_stream>)
HITCBC 187   4 any_stream(S s) 164   4 any_stream(S s)
HITCBC 188   4 { 165   4 {
189   struct guard { 166   struct guard {
190   any_stream* self; 167   any_stream* self;
191   void* ptr = nullptr; 168   void* ptr = nullptr;
192   bool committed = false; 169   bool committed = false;
HITCBC 193   4 ~guard() { 170   4 ~guard() {
HITCBC 194   4 if(!committed && ptr) { 171   4 if(!committed && ptr) {
195   static_cast<S*>(ptr)->~S(); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws 172   static_cast<S*>(ptr)->~S(); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
196   ::operator delete(self->storage_); // 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
197   self->storage_ = nullptr; // 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
198   } 175   }
HITCBC 199   4 } 176   4 }
HITCBC 200   4 } g{this}; 177   4 } g{this};
201   178  
HITCBC 202   4 storage_ = ::operator new(sizeof(S)); 179   4 storage_ = ::operator new(sizeof(S));
HITCBC 203   4 S* ptr = ::new(storage_) S(std::move(s)); 180   4 S* ptr = ::new(storage_) S(std::move(s));
HITCBC 204   4 g.ptr = ptr; 181   4 g.ptr = ptr;
HITCBC 205   4 stream_ptr_ = ptr; 182   4 stream_ptr_ = ptr;
HITCBC 206   8 destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); }; 183   8 destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); };
207   184  
208   // Initialize bases with pointer (reference semantics) 185   // Initialize bases with pointer (reference semantics)
HITCBC 209   4 static_cast<any_read_stream&>(*this) = any_read_stream(ptr); 186   4 static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
HITCBC 210   4 static_cast<any_write_stream&>(*this) = any_write_stream(ptr); 187   4 static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
211   188  
HITCBC 212   4 g.committed = true; 189   4 g.committed = true;
HITCBC 213   4 } 190   4 }
214   191  
215   /** Construct by wrapping a bidirectional stream without ownership. 192   /** Construct by wrapping a bidirectional stream without ownership.
216   193  
217   Wraps the given stream by pointer. The stream must remain 194   Wraps the given stream by pointer. The stream must remain
218   valid for the lifetime of this wrapper. 195   valid for the lifetime of this wrapper.
219   196  
220   @param s Pointer to the stream to wrap. Must satisfy both 197   @param s Pointer to the stream to wrap. Must satisfy both
221   ReadStream and WriteStream concepts. 198   ReadStream and WriteStream concepts.
222   */ 199   */
223   template<class S> 200   template<class S>
224   requires ReadStream<S> && WriteStream<S> 201   requires ReadStream<S> && WriteStream<S>
HITCBC 225   32 any_stream(S* s) 202   32 any_stream(S* s)
226   : any_read_stream(s) 203   : any_read_stream(s)
HITCBC 227   32 , any_write_stream(s) 204   32 , any_write_stream(s)
228   { 205   {
229   // storage_ remains nullptr - no ownership 206   // storage_ remains nullptr - no ownership
HITCBC 230   32 } 207   32 }
231   208  
232   /** Check if the wrapper contains a valid stream. 209   /** Check if the wrapper contains a valid stream.
233   210  
234   Both bases must be valid for the wrapper to be valid. 211   Both bases must be valid for the wrapper to be valid.
235   212  
236   @return `true` if wrapping a stream, `false` if default-constructed 213   @return `true` if wrapping a stream, `false` if default-constructed
237   or moved-from. 214   or moved-from.
238   */ 215   */
239   bool 216   bool
HITCBC 240   12 has_value() const noexcept 217   12 has_value() const noexcept
241   { 218   {
HITCBC 242   19 return any_read_stream::has_value() && 219   19 return any_read_stream::has_value() &&
HITCBC 243   19 any_write_stream::has_value(); 220   19 any_write_stream::has_value();
244   } 221   }
245   222  
246   /** Check if the wrapper contains a valid stream. 223   /** Check if the wrapper contains a valid stream.
247   224  
248   Both bases must be valid for the wrapper to be valid. 225   Both bases must be valid for the wrapper to be valid.
249   226  
250   @return `true` if wrapping a stream, `false` if default-constructed 227   @return `true` if wrapping a stream, `false` if default-constructed
251   or moved-from. 228   or moved-from.
252   */ 229   */
253   explicit 230   explicit
HITCBC 254   2 operator bool() const noexcept 231   2 operator bool() const noexcept
255   { 232   {
HITCBC 256   2 return has_value(); 233   2 return has_value();
257   } 234   }
258   }; 235   };
259   236  
260   } // namespace capy 237   } // namespace capy
261   } // namespace boost 238   } // namespace boost
262   239  
263   #endif 240   #endif