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/buffers
8 : //
9 :
10 : #ifndef BOOST_BUFFERS_BUFFER_HPP
11 : #define BOOST_BUFFERS_BUFFER_HPP
12 :
13 : #include <boost/buffers/detail/config.hpp>
14 : #include <boost/buffers/detail/type_traits.hpp>
15 : #include <cstddef>
16 : #include <iterator>
17 : #include <memory>
18 : #include <type_traits>
19 :
20 : // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
21 :
22 : // GCC 6 and earlier bug fixed after PR 42329
23 : // ("deduction of template template argument via base class fails")
24 : #if defined(__GNUC__) && ! defined(__clang__) && (__GNUC__ < 7)
25 : #define BOOST_BUFFERS_GCC6_WORKAROUND
26 : #endif
27 :
28 : namespace boost {
29 :
30 : namespace asio {
31 : class const_buffer;
32 : class mutable_buffer;
33 : } // asio
34 :
35 : namespace buffers {
36 :
37 : #ifndef BOOST_BUFFERS_GCC6_WORKAROUND
38 :
39 : class const_buffer;
40 : class mutable_buffer;
41 :
42 : namespace detail {
43 :
44 : // this is designed to satisfy Asio's buffer constructors
45 : template<class T, std::size_t Extent = (std::size_t)(-1)>
46 : class basic_buffer
47 : {
48 : public:
49 : /** Return a pointer to the beginning of the memory region
50 : */
51 : constexpr auto
52 13848866 : data() const noexcept ->
53 : typename std::conditional<
54 : std::is_const<T>::value,
55 : void const*, void*>::type
56 : {
57 13848866 : return p_;
58 : }
59 :
60 : /** Return the number of valid bytes in the referenced memory region
61 : */
62 20494954 : constexpr std::size_t size() const noexcept
63 : {
64 20494954 : return n_;
65 : }
66 :
67 : private:
68 : friend class buffers::const_buffer;
69 : friend class buffers::mutable_buffer;
70 : friend class asio::const_buffer;
71 : friend class asio::mutable_buffer;
72 147653 : basic_buffer() = default;
73 5307869 : constexpr basic_buffer(T* p, std::size_t n) noexcept : p_(p), n_(n) {}
74 : constexpr basic_buffer<T, (std::size_t)(-1)> subspan(
75 : std::size_t, std::size_t = (std::size_t)(-1)) const noexcept;
76 :
77 : T* p_ = nullptr;
78 : std::size_t n_ = 0;
79 : };
80 :
81 : } // detail
82 :
83 : #endif
84 :
85 : //-----------------------------------------------
86 :
87 : namespace detail {
88 : namespace adl {
89 :
90 : using std::begin;
91 : template<class T>
92 : using begin_type = decltype(begin(std::declval<T&>()));
93 :
94 : using std::end;
95 : template<class T>
96 : using end_type = decltype(end(std::declval<T&>()));
97 :
98 : // determine if T is a bidirectional range
99 : // whose value type is convertible to B
100 : template<class T, class B, class = void>
101 : struct is_bidir_of : std::false_type
102 : {
103 : };
104 :
105 : template<class T, class B>
106 : struct is_bidir_of<T, B, detail::void_t<typename std::enable_if<
107 : std::is_convertible<decltype(*std::declval<begin_type<T const>>()), B>::value &&
108 : std::is_convertible<decltype(*std::declval<end_type<T const>>()), B>::value &&
109 : detail::is_bidirectional_iterator<begin_type<T const>>::value &&
110 : detail::is_bidirectional_iterator<end_type<T const>>::value &&
111 : std::is_same<
112 : decltype(begin(std::declval<T const>())),
113 : decltype(end (std::declval<T const>()))>::value
114 : >::type > >
115 : : std::true_type
116 : {
117 : };
118 :
119 : } // adl
120 : } // detail
121 :
122 : //------------------------------------------------
123 :
124 : /** size tag for `tag_invoke`
125 :
126 : This type is used in overloads of `tag_invoke`
127 : for user-defined types to customize the `size()`
128 : algorithm.
129 : */
130 : struct size_tag {};
131 :
132 : /** slice tag for `tag_invoke`
133 :
134 : This type is used in overloads of `tag_invoke`
135 : for user-defined types to customize the slicing
136 : algorithms.
137 : */
138 : struct slice_tag {};
139 :
140 : /** slice constants for slice customization
141 :
142 : This defines the possible values passed to
143 : overloads of `tag_invoke` for user-defined
144 : types which customize the slicing algorithms.
145 : */
146 : enum class slice_how
147 : {
148 : /// Indicates that the front of the buffer sequence should be trimmed
149 : remove_prefix,
150 :
151 : /// Indicates that the front of the buffer sequence should be preserved
152 : keep_prefix
153 : };
154 :
155 : //------------------------------------------------
156 :
157 : #ifndef BOOST_BUFFERS_GCC6_WORKAROUND
158 :
159 : /** Holds a contiguous range of modifiable bytes
160 : */
161 : class mutable_buffer
162 : : public detail::basic_buffer<unsigned char>
163 : {
164 : public:
165 : /** Constructor.
166 : */
167 578 : mutable_buffer() = default;
168 :
169 : /** Constructor.
170 : */
171 : mutable_buffer(
172 : mutable_buffer const&) = default;
173 :
174 : /** Assignment.
175 : */
176 : mutable_buffer& operator=(
177 : mutable_buffer const&) = default;
178 :
179 : /** Constructor.
180 : */
181 638627 : constexpr mutable_buffer(
182 : void* data, std::size_t size) noexcept
183 638627 : : basic_buffer<unsigned char>(
184 638627 : static_cast<unsigned char*>(data), size)
185 : {
186 638627 : }
187 :
188 : /** Constructor
189 : */
190 : template<class MutableBuffer, class = typename std::enable_if<
191 : std::is_same<MutableBuffer, asio::mutable_buffer>::value>::type>
192 : constexpr mutable_buffer(
193 : MutableBuffer const& b) noexcept
194 : : basic_buffer<unsigned char>(
195 : static_cast<unsigned char*>(
196 : b.data()), b.size())
197 : {
198 : }
199 :
200 : /** Remove a prefix of the memory region
201 :
202 : If the requested number of bytes is larger than the current size,
203 : the resulting buffer will have size 0.
204 :
205 : @param n The number of bytes to remove.
206 : */
207 : mutable_buffer&
208 1025106 : operator+=(std::size_t n) noexcept
209 : {
210 1025106 : if( n > n_)
211 16 : n = n_;
212 1025106 : p_ += n;
213 1025106 : n_ -= n;
214 1025106 : return *this;
215 : }
216 :
217 : /** Remove a slice from the buffer
218 : */
219 : friend
220 : void
221 1088 : tag_invoke(
222 : slice_tag const&,
223 : mutable_buffer& b,
224 : slice_how how,
225 : std::size_t n) noexcept
226 : {
227 1088 : b.do_slice(how, n);
228 1088 : }
229 :
230 : private:
231 1088 : void do_slice(
232 : slice_how how, std::size_t n) noexcept
233 : {
234 1088 : switch(how)
235 : {
236 512 : case slice_how::remove_prefix:
237 512 : *this += n;
238 512 : return;
239 :
240 576 : case slice_how::keep_prefix:
241 576 : if( n < n_)
242 476 : n_ = n;
243 576 : return;
244 : }
245 : }
246 : };
247 :
248 : //------------------------------------------------
249 :
250 : /** Holds a contiguous range of unmodifiable bytes
251 : */
252 : class const_buffer
253 : : public detail::basic_buffer<unsigned char const>
254 : {
255 : public:
256 : /** Constructor
257 : */
258 147075 : const_buffer() = default;
259 :
260 : /** Constructor
261 : */
262 : const_buffer(const_buffer const&) = default;
263 :
264 : /** Assignment
265 :
266 : @par Postconditions
267 : @code
268 : this->data() == other.data() && this->size() == other.size()
269 : @endcode
270 : */
271 : const_buffer& operator=(
272 : const_buffer const& other) = default;
273 :
274 : /** Constructor
275 : */
276 4660271 : constexpr const_buffer(
277 : void const* data, std::size_t size) noexcept
278 4660271 : : basic_buffer<unsigned char const>(
279 4660271 : static_cast<unsigned char const*>(data), size)
280 : {
281 4660271 : }
282 :
283 : /** Constructor
284 : */
285 8971 : constexpr const_buffer(
286 : mutable_buffer const& b) noexcept
287 8971 : : basic_buffer<unsigned char const>(
288 8971 : static_cast<unsigned char const*>(b.data()), b.size())
289 : {
290 8971 : }
291 :
292 : /** Constructor
293 : */
294 : template<class ConstBuffer, class = typename std::enable_if<
295 : std::is_same<ConstBuffer, asio::const_buffer>::value ||
296 : std::is_same<ConstBuffer, asio::mutable_buffer>::value>::type>
297 : constexpr const_buffer(
298 : ConstBuffer const& b) noexcept
299 : : basic_buffer<unsigned char const>(
300 : static_cast<unsigned char const*>(
301 : b.data()), b.size())
302 : {
303 : }
304 :
305 : /** Remove a prefix of the memory region
306 :
307 : If the requested number of bytes is larger than the current size,
308 : the resulting buffer will have size 0.
309 :
310 : @param n The number of bytes to remove.
311 : */
312 : const_buffer&
313 1156247 : operator+=(std::size_t n) noexcept
314 : {
315 1156247 : if( n > n_)
316 4112 : n = n_;
317 1156247 : p_ += n;
318 1156247 : n_ -= n;
319 1156247 : return *this;
320 : }
321 :
322 : /** Remove a slice from the buffer
323 : */
324 : friend
325 : void
326 267349 : tag_invoke(
327 : slice_tag const&,
328 : const_buffer& b,
329 : slice_how how,
330 : std::size_t n) noexcept
331 : {
332 267349 : b.do_slice(how, n);
333 267349 : }
334 :
335 : private:
336 267349 : void do_slice(
337 : slice_how how, std::size_t n) noexcept
338 : {
339 267349 : switch(how)
340 : {
341 131653 : case slice_how::remove_prefix:
342 131653 : *this += n;
343 131653 : return;
344 :
345 135696 : case slice_how::keep_prefix:
346 135696 : if( n < n_)
347 121169 : n_ = n;
348 135696 : return;
349 : }
350 : }
351 : };
352 :
353 : #else
354 :
355 : template<class T, std::size_t Extent = (std::size_t)(-1)>
356 : class basic_buffer
357 : {
358 : using pointer = typename std::conditional<
359 : std::is_const<T>::value, void const*, void*>::type;
360 : public:
361 : basic_buffer() = default;
362 : basic_buffer(basic_buffer const&) = default;
363 : basic_buffer& operator=(basic_buffer const& other) = default;
364 : constexpr auto data() const noexcept -> pointer { return p_; }
365 : constexpr std::size_t size() const noexcept { return n_; }
366 : constexpr basic_buffer(pointer p, std::size_t n) noexcept
367 : : p_(p) , n_(n) {}
368 : template<class U, std::size_t E, class = typename std::enable_if<
369 : std::is_const<T>::value && ! std::is_const<U>::value>::type>
370 : constexpr basic_buffer(basic_buffer<U,E> b) noexcept
371 : : p_(b.data()), n_(b.size()) {}
372 : template<class Buffer, class = typename std::enable_if<
373 : std::is_same<Buffer, asio::mutable_buffer>::value ||
374 : (std::is_same<Buffer, asio::const_buffer>::value &&
375 : std::is_const<T>::value)>::type>
376 : constexpr basic_buffer(Buffer b) noexcept
377 : : p_(b.data()), n_(b.size()) {}
378 : basic_buffer& operator+=(std::size_t n) noexcept
379 : {
380 : if( n > n_)
381 : n = n_;
382 : p_ = static_cast<T*>(p_) + n;
383 : n_ -= n;
384 : return *this;
385 : }
386 : friend void tag_invoke(slice_tag const&, basic_buffer& b,
387 : slice_how how, std::size_t n) noexcept
388 : {
389 : switch(how)
390 : {
391 : case slice_how::remove_prefix:
392 : b += n;
393 : return;
394 :
395 : case slice_how::keep_prefix:
396 : if(n < b.n_)
397 : b.n_ = n;
398 : return;
399 : }
400 : }
401 :
402 : private:
403 : friend class asio::const_buffer;
404 : friend class asio::mutable_buffer;
405 : constexpr basic_buffer<T, (std::size_t)(-1)>
406 : subspan(std::size_t, std::size_t = (std::size_t)(-1)) const noexcept;
407 :
408 : pointer p_ = nullptr;
409 : std::size_t n_ = 0;
410 : };
411 :
412 : using mutable_buffer = basic_buffer<unsigned char>;
413 : using const_buffer = basic_buffer<unsigned char const>;
414 :
415 : #endif
416 :
417 : //------------------------------------------------------------------------------
418 :
419 : /** Return an iterator pointing to the first element of a buffer sequence
420 :
421 : This function returns an iterator to the beginning of the range denoted by
422 : `t`. While this works for any valid range, it is provided for convenience
423 : when using C++17 and earlier, where `std::ranges::begin` is unavailable.
424 :
425 : @par Constraints
426 : @code
427 : requires std::ranges::range<T>
428 : @endcode
429 :
430 : @param t The buffer sequence
431 : */
432 : constexpr struct begin_mrdocs_workaround_t
433 : {
434 : template<
435 : class ConvertibleToBuffer
436 : ,class = typename std::enable_if<
437 : std::is_convertible<ConvertibleToBuffer, const_buffer>::value ||
438 : std::is_convertible<ConvertibleToBuffer, mutable_buffer>::value
439 : >::type
440 : >
441 592836 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
442 : {
443 592836 : return std::addressof(b);
444 : }
445 :
446 : template<
447 : class BufferSequence
448 : ,class = typename std::enable_if<
449 : ! std::is_convertible<BufferSequence const*, const_buffer const*>::value &&
450 : ! std::is_convertible<BufferSequence const*, mutable_buffer const*>::value,
451 : detail::void_t< detail::adl::begin_type<BufferSequence const> > >::type
452 : >
453 4318667 : auto operator()(BufferSequence const& bs) const noexcept
454 : #if ! (__cpp_decltype_auto >= 201304)
455 : -> detail::adl::begin_type<BufferSequence const>
456 : #endif
457 : {
458 : using std::begin;
459 4318667 : return begin(bs);
460 : }
461 :
462 : template<
463 : class BufferSequence
464 : ,class = typename std::enable_if<
465 : ! std::is_convertible<BufferSequence const*, const_buffer const*>::value &&
466 : ! std::is_convertible<BufferSequence const*, mutable_buffer const*>::value,
467 : detail::void_t< detail::adl::begin_type<BufferSequence> > >::type
468 : >
469 2549261 : auto operator()(BufferSequence& bs) const noexcept
470 : #if ! (__cpp_decltype_auto >= 201304)
471 : -> detail::adl::begin_type<BufferSequence>
472 : #endif
473 : {
474 : using std::begin;
475 2549261 : return begin(bs);
476 : }
477 : } begin {};
478 :
479 : //------------------------------------------------------------------------------
480 :
481 : /** Return an iterator to the end of the buffer sequence
482 :
483 : This function returns an iterator to the end of the range denoted by
484 : `t`. While this works for any valid range, it is provided for convenience
485 : when using C++17 and earlier, where `std::ranges::end` is unavailable.
486 :
487 : @par Constraints
488 : @code
489 : requires std::ranges::range<T>
490 : @endcode
491 :
492 : @param t The buffer sequence
493 : */
494 : constexpr struct end_mrdocs_workaround_t
495 : {
496 : template<
497 : class ConvertibleToBuffer
498 : ,class = typename std::enable_if<
499 : std::is_convertible<ConvertibleToBuffer, const_buffer>::value ||
500 : std::is_convertible<ConvertibleToBuffer, mutable_buffer>::value
501 : >::type
502 : >
503 592836 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
504 : {
505 592836 : return std::addressof(b) + 1;
506 : }
507 :
508 : template<
509 : class BufferSequence
510 : ,class = typename std::enable_if<
511 : ! std::is_convertible<BufferSequence const*, const_buffer const*>::value &&
512 : ! std::is_convertible<BufferSequence const*, mutable_buffer const*>::value,
513 : detail::void_t< detail::adl::end_type<BufferSequence const> > >::type
514 : >
515 4318665 : auto operator()(BufferSequence const& bs) const noexcept
516 : #if ! (__cpp_decltype_auto >= 201304)
517 : -> detail::adl::end_type<BufferSequence const>
518 : #endif
519 : {
520 : using std::end;
521 4318665 : return end(bs);
522 : }
523 :
524 : template<
525 : class BufferSequence
526 : ,class = typename std::enable_if<
527 : ! std::is_convertible<BufferSequence const*, const_buffer const*>::value &&
528 : ! std::is_convertible<BufferSequence const*, mutable_buffer const*>::value,
529 : detail::void_t< detail::adl::end_type<BufferSequence> > >::type
530 : >
531 2549261 : auto operator()(BufferSequence& bs) const noexcept
532 : #if ! (__cpp_decltype_auto >= 201304)
533 : -> detail::adl::end_type<BufferSequence>
534 : #endif
535 : {
536 : using std::end;
537 2549261 : return end(bs);
538 : }
539 : } end {};
540 :
541 : //------------------------------------------------
542 :
543 : /** Determine if T is a ConstBufferSequence.
544 : */
545 : /** @{ */
546 : template<class T, class = void>
547 : struct is_const_buffer_sequence
548 : : std::false_type
549 : {
550 : };
551 :
552 : template<class T>
553 : struct is_const_buffer_sequence<T>
554 : : std::integral_constant<bool,
555 : detail::adl::is_bidir_of<typename std::remove_cv<typename
556 : std::remove_reference<T>::type>::type, const_buffer>::value ||
557 : std::is_convertible<T, const_buffer>::value>
558 : {
559 : };
560 :
561 : #if defined(__cpp_variable_templates) && __cpp_variable_templates >= 201304
562 : template<class T>
563 : constexpr bool is_const_buffer_sequence_v =
564 : is_const_buffer_sequence<T>::value;
565 : #endif
566 : /** @} */
567 :
568 : //------------------------------------------------
569 :
570 : /** Determine if T is a MutableBufferSequence.
571 : */
572 : /** @{ */
573 : template<class T, class = void>
574 : struct is_mutable_buffer_sequence : std::false_type
575 : {
576 : };
577 :
578 : template<class T>
579 : struct is_mutable_buffer_sequence<T>
580 : : std::integral_constant<bool,
581 : detail::adl::is_bidir_of<typename std::remove_cv<typename
582 : std::remove_reference<T>::type>::type, mutable_buffer>::value ||
583 : std::is_convertible<T, mutable_buffer>::value>
584 : {
585 : };
586 :
587 : #if defined(__cpp_variable_templates) && __cpp_variable_templates >= 201304
588 : template<class T>
589 : constexpr bool is_mutable_buffer_sequence_v =
590 : is_mutable_buffer_sequence<T>::value;
591 : #endif
592 : /** @} */
593 :
594 : //------------------------------------------------------------------------------
595 :
596 : template<class ConstBufferSequence>
597 : std::size_t
598 1406869 : tag_invoke(
599 : size_tag const&,
600 : ConstBufferSequence const& bs) noexcept
601 : {
602 1406869 : std::size_t n = 0;
603 1406869 : auto const e = end(bs);
604 3884472 : for(auto it = begin(bs); it != e; ++it)
605 2477603 : n += const_buffer(*it).size();
606 1406874 : return n;
607 5 : }
608 :
609 : /** Return the total number of bytes in a buffer sequence
610 :
611 : This function returns the sum of the number of bytes in each contiguous
612 : buffer contained in the range or value. This is different from the length
613 : of the sequence returned by `std::ranges::size(t)`
614 :
615 : @par Constraints
616 : @code
617 : is_const_buffer_sequence_v<T> || std::is_convertible<T,const_buffer>
618 : @endcode
619 :
620 : @par Example
621 : @code
622 : template<class ConstBufferSequence>
623 : bool is_small( ConstBufferSequence const& bs ) noexcept
624 : {
625 : return size(bs) < 100;
626 : }
627 : @endcode
628 : */
629 : constexpr struct size_mrdocs_workaround_t
630 : {
631 : template<class ConstBufferSequence>
632 1406869 : constexpr auto operator()(
633 : ConstBufferSequence const& bs) const noexcept ->
634 : typename std::enable_if<
635 : is_const_buffer_sequence<ConstBufferSequence>::value,
636 : std::size_t>::type
637 : {
638 1406869 : return tag_invoke(size_tag{}, bs);
639 : }
640 : } size {};
641 :
642 : //-----------------------------------------------
643 :
644 : namespace detail {
645 :
646 : template<class It>
647 : auto
648 3 : length_impl(It first, It last, int)
649 : -> decltype(static_cast<std::size_t>(last - first))
650 : {
651 3 : return static_cast<std::size_t>(last - first);
652 : }
653 :
654 : template<class It>
655 : std::size_t
656 21 : length_impl(It first, It last, long)
657 : {
658 21 : std::size_t n = 0;
659 30 : while(first != last)
660 : {
661 9 : ++first;
662 9 : ++n;
663 : }
664 21 : return n;
665 : }
666 :
667 : } // detail
668 :
669 : /** Return the number of elements in a buffer sequence.
670 : */
671 : template<class ConstBufferSequence>
672 : std::size_t
673 24 : length(ConstBufferSequence const& bs)
674 : {
675 44 : return detail::length_impl(
676 24 : buffers::begin(bs), buffers::end(bs), 0);
677 : }
678 :
679 : } // buffers
680 : } // boost
681 :
682 : #endif
|