1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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  
    */
93  
    ~any_stream()
93  
    ~any_stream()
94  
    {
94  
    {
95  
        if(storage_)
95  
        if(storage_)
96  
        {
96  
        {
97  
            destroy_(stream_ptr_);
97  
            destroy_(stream_ptr_);
98  
            ::operator delete(storage_);
98  
            ::operator delete(storage_);
99  
        }
99  
        }
100  
    }
100  
    }
101  

101  

102  
    /** Default constructor.
102  
    /** Default constructor.
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  
    /** Move constructor.
116  
    /** Move constructor.
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  
    */
122  
    any_stream(any_stream&& other) noexcept
122  
    any_stream(any_stream&& other) noexcept
123  
        : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
123  
        : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
124  
        , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
124  
        , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
125  
        , storage_(std::exchange(other.storage_, nullptr))
125  
        , storage_(std::exchange(other.storage_, nullptr))
126  
        , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
126  
        , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
127  
        , destroy_(std::exchange(other.destroy_, nullptr))
127  
        , destroy_(std::exchange(other.destroy_, nullptr))
128  
    {
128  
    {
129  
    }
129  
    }
130  

130  

131  
    /** Move assignment operator.
131  
    /** Move assignment operator.
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&
140  
    operator=(any_stream&& other) noexcept
140  
    operator=(any_stream&& other) noexcept
141  
    {
141  
    {
142  
        if(this != &other)
142  
        if(this != &other)
143  
        {
143  
        {
144  
            if(storage_)
144  
            if(storage_)
145  
            {
145  
            {
146  
                destroy_(stream_ptr_);
146  
                destroy_(stream_ptr_);
147  
                ::operator delete(storage_);
147  
                ::operator delete(storage_);
148  
            }
148  
            }
149  
            static_cast<any_read_stream&>(*this) =
149  
            static_cast<any_read_stream&>(*this) =
150  
                std::move(static_cast<any_read_stream&>(other));
150  
                std::move(static_cast<any_read_stream&>(other));
151  
            static_cast<any_write_stream&>(*this) =
151  
            static_cast<any_write_stream&>(*this) =
152  
                std::move(static_cast<any_write_stream&>(other));
152  
                std::move(static_cast<any_write_stream&>(other));
153  
            storage_ = std::exchange(other.storage_, nullptr);
153  
            storage_ = std::exchange(other.storage_, nullptr);
154  
            stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
154  
            stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
155  
            destroy_ = std::exchange(other.destroy_, nullptr);
155  
            destroy_ = std::exchange(other.destroy_, nullptr);
156  
        }
156  
        }
157  
        return *this;
157  
        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>)
171  
    any_stream(S s)
171  
    any_stream(S s)
172  
    {
172  
    {
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;
177  
            ~guard() {
177  
            ~guard() {
178  
                if(!committed && ptr) {
178  
                if(!committed && ptr) {
179  
                    static_cast<S*>(ptr)->~S();
179  
                    static_cast<S*>(ptr)->~S();
180  
                    ::operator delete(self->storage_);
180  
                    ::operator delete(self->storage_);
181  
                    self->storage_ = nullptr;
181  
                    self->storage_ = nullptr;
182  
                }
182  
                }
183  
            }
183  
            }
184  
        } g{this};
184  
        } g{this};
185  

185  

186  
        storage_ = ::operator new(sizeof(S));
186  
        storage_ = ::operator new(sizeof(S));
187  
        S* ptr = ::new(storage_) S(std::move(s));
187  
        S* ptr = ::new(storage_) S(std::move(s));
188  
        g.ptr = ptr;
188  
        g.ptr = ptr;
189  
        stream_ptr_ = ptr;
189  
        stream_ptr_ = ptr;
190  
        destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); };
190  
        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)
193  
        static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
193  
        static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
194  
        static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
194  
        static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
195  

195  

196  
        g.committed = true;
196  
        g.committed = true;
197  
    }
197  
    }
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>
209  
    any_stream(S* s)
209  
    any_stream(S* s)
210  
        : any_read_stream(s)
210  
        : any_read_stream(s)
211  
        , any_write_stream(s)
211  
        , any_write_stream(s)
212  
    {
212  
    {
213  
        // storage_ remains nullptr - no ownership
213  
        // storage_ remains nullptr - no ownership
214  
    }
214  
    }
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
224  
    has_value() const noexcept
224  
    has_value() const noexcept
225  
    {
225  
    {
226  
        return any_read_stream::has_value() &&
226  
        return any_read_stream::has_value() &&
227  
               any_write_stream::has_value();
227  
               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
238  
    operator bool() const noexcept
238  
    operator bool() const noexcept
239  
    {
239  
    {
240  
        return has_value();
240  
        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