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