TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_ASIO_BUFFERS_HPP
11 : #define BOOST_CAPY_ASIO_BUFFERS_HPP
12 :
13 : #include <boost/capy/buffers.hpp>
14 : #include <array>
15 : #include <concepts>
16 : #include <utility>
17 :
18 : namespace asio
19 : {
20 :
21 : class mutable_buffer;
22 : class const_buffer;
23 :
24 : }
25 :
26 : namespace boost::asio
27 : {
28 :
29 : class mutable_buffer;
30 : class const_buffer;
31 :
32 : }
33 :
34 : namespace boost::capy
35 : {
36 :
37 : /** A const buffer type compatible with both capy and Asio.
38 :
39 : Extends capy's `const_buffer` with implicit conversion operators
40 : to both standalone Asio (`::asio::const_buffer`) and Boost.Asio
41 : (`boost::asio::const_buffer`) buffer types.
42 :
43 : This allows seamless use of capy buffers with Asio I/O operations
44 : without explicit conversion.
45 :
46 : @par Example
47 : @code
48 : capy::asio_const_buffer buf(data, size);
49 :
50 : // Works directly with Asio operations
51 : co_await socket.async_write_some(buf, capy::as_io_awaitable);
52 : @endcode
53 :
54 : @see asio_mutable_buffer, as_asio_buffer_sequence
55 : */
56 : struct asio_const_buffer : const_buffer
57 : {
58 : using const_buffer::const_buffer;
59 : using const_buffer::operator=;
60 :
61 : /// Construct from a capy const_buffer.
62 HIT 2 : asio_const_buffer(const_buffer cb) : const_buffer(cb) {}
63 :
64 : /// Convert to standalone Asio const_buffer.
65 : inline
66 : operator ::asio::const_buffer() const;
67 :
68 : /// Convert to Boost.Asio const_buffer.
69 : inline
70 : operator boost::asio::const_buffer() const;
71 : };
72 :
73 : /** A mutable buffer type compatible with both capy and Asio.
74 :
75 : Extends capy's `mutable_buffer` with implicit conversion operators
76 : to both standalone Asio and Boost.Asio buffer types. Supports
77 : conversion to both mutable and const buffer types.
78 :
79 : This allows seamless use of capy buffers with Asio I/O operations
80 : without explicit conversion.
81 :
82 : @par Example
83 : @code
84 : char data[1024];
85 : capy::asio_mutable_buffer buf(data, sizeof(data));
86 :
87 : // Works directly with Asio read operations
88 : auto [ec, n] = co_await socket.async_read_some(buf, capy::as_io_awaitable);
89 : @endcode
90 :
91 : @see asio_const_buffer, as_asio_buffer_sequence
92 : */
93 : struct asio_mutable_buffer : mutable_buffer
94 : {
95 : using mutable_buffer::mutable_buffer;
96 : using mutable_buffer::operator=;
97 :
98 : /// Construct from a capy mutable_buffer.
99 2 : asio_mutable_buffer(mutable_buffer mb) : mutable_buffer(mb) {}
100 :
101 : /// Convert to standalone Asio mutable_buffer.
102 : inline
103 : operator ::asio::mutable_buffer() const;
104 :
105 : /// Convert to Boost.Asio mutable_buffer.
106 : inline
107 : operator boost::asio::mutable_buffer() const;
108 :
109 : /// Convert to standalone Asio const_buffer.
110 : inline
111 : operator ::asio::const_buffer() const;
112 :
113 : /// Convert to Boost.Asio const_buffer.
114 : inline
115 : operator boost::asio::const_buffer() const;
116 : };
117 :
118 :
119 : namespace detail
120 : {
121 :
122 : struct asio_buffer_transformer_t
123 : {
124 : inline
125 : asio_mutable_buffer operator()(const boost::asio::mutable_buffer &mb) const noexcept;
126 :
127 : inline
128 : asio_const_buffer operator()(const boost::asio::const_buffer &cb) const noexcept;
129 :
130 : inline
131 : asio_mutable_buffer operator()(const ::asio::mutable_buffer &mb) const noexcept;
132 :
133 : inline
134 : asio_const_buffer operator()(const ::asio::const_buffer &cb) const noexcept;
135 :
136 :
137 2 : asio_mutable_buffer operator()(const mutable_buffer &mb) const noexcept
138 : {
139 2 : return mb;
140 : }
141 :
142 2 : asio_const_buffer operator()(const const_buffer &cb) const noexcept
143 : {
144 2 : return cb;
145 : }
146 :
147 : asio_buffer_transformer_t() noexcept = default;
148 : asio_buffer_transformer_t(const asio_buffer_transformer_t &) noexcept = default;
149 : };
150 :
151 : constexpr static asio_buffer_transformer_t asio_buffer_transformer;
152 :
153 : /** A bidirectional iterator that transforms buffer types using asio_buffer_transformer.
154 : *
155 : * Wraps an underlying bidirectional iterator and applies asio_buffer_transformer
156 : * to each dereferenced value, converting between Asio buffer types and capy
157 : * asio_const_buffer/asio_mutable_buffer types.
158 : *
159 : * @tparam Iterator The underlying bidirectional iterator type
160 : */
161 : template<typename Iterator>
162 : class asio_buffer_iterator
163 : {
164 : public:
165 : using iterator_type = Iterator;
166 : using iterator_category = std::bidirectional_iterator_tag;
167 : using difference_type = typename std::iterator_traits<Iterator>::difference_type;
168 : using underlying_value = typename std::iterator_traits<Iterator>::value_type;
169 : using value_type = decltype(asio_buffer_transformer(std::declval<underlying_value>()));
170 : using reference = value_type;
171 : using pointer = void;
172 :
173 : asio_buffer_iterator() = default;
174 :
175 20 : explicit asio_buffer_iterator(Iterator it)
176 : noexcept(std::is_nothrow_move_constructible_v<Iterator>)
177 20 : : it_(std::move(it))
178 : {
179 20 : }
180 :
181 : asio_buffer_iterator(const asio_buffer_iterator&) = default;
182 : asio_buffer_iterator(asio_buffer_iterator&&) = default;
183 : asio_buffer_iterator& operator=(const asio_buffer_iterator&) = default;
184 : asio_buffer_iterator& operator=(asio_buffer_iterator&&) = default;
185 :
186 10 : reference operator*() const
187 : noexcept(noexcept(asio_buffer_transformer(*std::declval<Iterator>())))
188 : {
189 10 : return asio_buffer_transformer(*it_);
190 : }
191 :
192 4 : asio_buffer_iterator& operator++()
193 : noexcept(noexcept(++std::declval<Iterator&>()))
194 : {
195 4 : ++it_;
196 4 : return *this;
197 : }
198 :
199 3 : asio_buffer_iterator operator++(int)
200 : noexcept(noexcept(std::declval<Iterator&>()++))
201 : {
202 3 : asio_buffer_iterator tmp(*this);
203 3 : ++it_;
204 3 : return tmp;
205 : }
206 :
207 : asio_buffer_iterator& operator--()
208 : noexcept(noexcept(--std::declval<Iterator&>()))
209 : {
210 : --it_;
211 : return *this;
212 : }
213 :
214 : asio_buffer_iterator operator--(int)
215 : noexcept(noexcept(std::declval<Iterator&>()--))
216 : {
217 : asio_buffer_iterator tmp(*this);
218 : --it_;
219 : return tmp;
220 : }
221 :
222 : bool operator==(const asio_buffer_iterator& other) const
223 : noexcept(noexcept(std::declval<Iterator>() == std::declval<Iterator>()))
224 : {
225 : return it_ == other.it_;
226 : }
227 :
228 13 : bool operator!=(const asio_buffer_iterator& other) const
229 : noexcept(noexcept(std::declval<Iterator>() != std::declval<Iterator>()))
230 : {
231 13 : return it_ != other.it_;
232 : }
233 :
234 : /// Returns the underlying iterator.
235 : Iterator base() const noexcept(std::is_nothrow_copy_constructible_v<Iterator>)
236 : {
237 : return it_;
238 : }
239 :
240 : private:
241 : Iterator it_{};
242 : };
243 :
244 : // Deduction guide
245 : template<typename Iterator>
246 : asio_buffer_iterator(Iterator) -> asio_buffer_iterator<Iterator>;
247 :
248 :
249 : /** A bidirectional range that transforms buffer sequences using asio_buffer_transformer.
250 : *
251 : * Wraps a buffer sequence and provides begin/end iterators that transform
252 : * buffer elements via asio_buffer_transformer. Satisfies the requirements
253 : * of std::ranges::bidirectional_range.
254 : *
255 : * @tparam Sequence The underlying buffer sequence type
256 : */
257 : template<typename Sequence>
258 : class asio_buffer_range
259 : {
260 : public:
261 : using sequence_type = Sequence;
262 : using iterator = asio_buffer_iterator<
263 : decltype(std::ranges::begin(std::declval<const Sequence&>()))>;
264 : using const_iterator = iterator;
265 :
266 : asio_buffer_range() = default;
267 :
268 2 : explicit asio_buffer_range(Sequence seq)
269 : noexcept(std::is_nothrow_move_constructible_v<Sequence>)
270 2 : : seq_(std::move(seq))
271 : {
272 2 : }
273 :
274 : asio_buffer_range(const asio_buffer_range&) = default;
275 : asio_buffer_range(asio_buffer_range&&) = default;
276 : asio_buffer_range& operator=(const asio_buffer_range&) = default;
277 : asio_buffer_range& operator=(asio_buffer_range&&) = default;
278 :
279 4 : iterator begin() const
280 : noexcept(noexcept(std::ranges::begin(std::declval<const Sequence&>())))
281 : {
282 4 : return iterator(std::ranges::begin(seq_));
283 : }
284 :
285 4 : iterator end() const
286 : noexcept(noexcept(std::ranges::end(std::declval<const Sequence&>())))
287 : {
288 4 : return iterator(std::ranges::end(seq_));
289 : }
290 :
291 : /// Returns true if the range is empty.
292 : bool empty() const
293 : noexcept(noexcept(std::ranges::empty(std::declval<const Sequence&>())))
294 : {
295 : return std::ranges::empty(seq_);
296 : }
297 :
298 : /// Returns the underlying sequence.
299 : const Sequence& base() const noexcept { return seq_; }
300 :
301 : private:
302 : Sequence seq_{};
303 : };
304 :
305 : // Deduction guide
306 : template<typename Sequence>
307 : asio_buffer_range(Sequence) -> asio_buffer_range<Sequence>;
308 :
309 : }
310 :
311 :
312 : /** @defgroup as_asio_buffer_sequence as_asio_buffer_sequence
313 :
314 : Bidirectional conversion between Asio and capy buffer sequences.
315 :
316 : The `as_asio_buffer_sequence` function provides seamless conversion
317 : in both directions:
318 :
319 : - **Capy to Asio**: Convert capy buffer sequences for use with Asio
320 : I/O operations. The returned range satisfies Asio's
321 : `MutableBufferSequence` or `ConstBufferSequence` requirements.
322 :
323 : - **Asio to Capy**: Convert Asio buffer sequences for use with capy's
324 : buffer concepts. The returned range satisfies capy's
325 : `MutableBufferSequence` or `ConstBufferSequence` concepts.
326 :
327 : @par Example: Capy to Asio
328 : @code
329 : capy::mutable_buffer_pair buffers = ...;
330 : auto seq = capy::as_asio_buffer_sequence(buffers);
331 : co_await socket.async_read_some(seq, capy::as_io_awaitable);
332 : @endcode
333 :
334 : @par Example: Asio to Capy
335 : @code
336 : std::vector<boost::asio::mutable_buffer> asio_bufs = ...;
337 : auto seq = capy::as_asio_buffer_sequence(asio_bufs);
338 : std::size_t total = capy::buffer_size(seq); // Use capy algorithms
339 : @endcode
340 :
341 : @{
342 : */
343 :
344 : /** Pass through a range already containing asio_const_buffer elements.
345 :
346 : @param rng A bidirectional range of asio_const_buffer
347 : @return The range forwarded unchanged
348 : */
349 : template<std::ranges::bidirectional_range<> Range>
350 : requires std::same_as<std::ranges::range_value_t<Range>, asio_const_buffer>
351 : auto as_asio_buffer_sequence(Range && rng)
352 : {
353 : return std::forward<Range>(rng);
354 : }
355 :
356 : /** Pass through a range already containing asio_mutable_buffer elements.
357 :
358 : @param rng A bidirectional range of asio_mutable_buffer
359 : @return The range forwarded unchanged
360 : */
361 : template<std::ranges::bidirectional_range<> Range>
362 : requires std::same_as<std::ranges::range_value_t<Range>, asio_mutable_buffer>
363 : auto as_asio_buffer_sequence(Range && rng)
364 : {
365 : return std::forward<Range>(rng);
366 : }
367 :
368 : /** Convert a single asio_mutable_buffer to a buffer sequence.
369 :
370 : @param mb The mutable buffer
371 : @return A single-element array containing the buffer
372 : */
373 : inline
374 : auto as_asio_buffer_sequence(asio_mutable_buffer mb)
375 : {
376 : return std::array<asio_mutable_buffer, 1u>{mb};
377 : }
378 :
379 : /** Convert a single asio_const_buffer to a buffer sequence.
380 :
381 : @param cb The const buffer
382 : @return A single-element array containing the buffer
383 : */
384 : inline
385 : auto as_asio_buffer_sequence(asio_const_buffer cb)
386 : {
387 : return std::array<asio_const_buffer, 1u>{cb};
388 : }
389 :
390 : /** Convert a single capy mutable_buffer to a buffer sequence.
391 :
392 : @param mb The mutable buffer
393 : @return A single-element array containing an asio_mutable_buffer
394 : */
395 : inline
396 : auto as_asio_buffer_sequence(mutable_buffer mb)
397 : {
398 : return std::array<asio_mutable_buffer, 1u>{mb};
399 : }
400 :
401 : /** Convert a buffer type that might support both mutable & const buffer
402 : to an asio buffer sequence.
403 :
404 : @param buf The const buffer
405 : @return A single-element array containing an asio_mutable_buffer
406 :
407 : */
408 : template<std::convertible_to<mutable_buffer> Buffer>
409 : inline
410 : auto as_asio_buffer_sequence(const Buffer & buf)
411 : {
412 : return as_asio_buffer_sequence(static_cast<mutable_buffer>(buf));
413 : }
414 :
415 : /** Convert a single capy const_buffer to a buffer sequence.
416 : @param cb The const buffer
417 : @return A single-element array containing an asio_const_buffer
418 :
419 : */
420 : inline
421 : auto as_asio_buffer_sequence(const_buffer cb)
422 : {
423 : return std::array<asio_const_buffer, 1u>{cb};
424 : }
425 :
426 : /** Convert any buffer sequence for bidirectional Asio/capy compatibility.
427 :
428 : Wraps the buffer sequence in a transforming range that converts
429 : each buffer element to asio_const_buffer or asio_mutable_buffer.
430 : The returned range satisfies both Asio's buffer sequence requirements
431 : and capy's buffer sequence concepts.
432 :
433 : This overload handles:
434 : - Capy buffer sequences (for use with Asio operations)
435 : - Asio buffer sequences (for use with capy concepts/algorithms)
436 :
437 : @param seq The buffer sequence to convert
438 : @return A transforming range over the buffer sequence
439 : */
440 : template<ConstBufferSequence Seq>
441 : requires (!std::convertible_to<Seq, const_buffer> && !std::convertible_to<Seq, mutable_buffer>)
442 : inline
443 2 : auto as_asio_buffer_sequence(const Seq & seq)
444 : {
445 2 : return detail::asio_buffer_range(seq);
446 : }
447 :
448 : /** @} */
449 :
450 : }
451 :
452 :
453 : #endif // BOOST_CAPY_ASIO_BUFFERS_HPP
454 :
|