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