GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/buffer.hpp
Date: 2025-12-06 02:12:43
Exec Total Coverage
Lines: 88 88 100.0%
Functions: 100 100 100.0%
Branches: 17 19 89.5%

Line Branch Exec Source
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 27697731 data() const noexcept ->
53 typename std::conditional<
54 std::is_const<T>::value,
55 void const*, void*>::type
56 {
57 27697731 return p_;
58 }
59
60 /** Return the number of valid bytes in the referenced memory region
61 */
62 40989903 constexpr std::size_t size() const noexcept
63 {
64 40989903 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 295306 basic_buffer() = default;
73 10615737 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
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1025090 times.
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
2/3
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
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
2/2
✓ Branch 0 taken 476 times.
✓ Branch 1 taken 100 times.
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
2/2
✓ Branch 0 taken 4112 times.
✓ Branch 1 taken 1152135 times.
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
2/3
✓ Branch 0 taken 131653 times.
✓ Branch 1 taken 135696 times.
✗ Branch 2 not taken.
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
2/2
✓ Branch 0 taken 121169 times.
✓ Branch 1 taken 14527 times.
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 1183167 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
442 {
443 1183167 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 8637334 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 8637334 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 3968009 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 3968009 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 1183005 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
504 {
505 1183005 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 8623556 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 8623556 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 3968009 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 3968009 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 2113255 tag_invoke(
599 size_tag const&,
600 ConstBufferSequence const& bs) noexcept
601 {
602 2113255 std::size_t n = 0;
603 2113255 auto const e = end(bs);
604
3/3
✓ Branch 1 taken 1420435 times.
✓ Branch 2 taken 1767272 times.
✓ Branch 3 taken 696765 times.
5667292 for(auto it = begin(bs); it != e; ++it)
605 3554037 n += const_buffer(*it).size();
606 2113265 return n;
607 10 }
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 1406882 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 1406882 return tag_invoke(size_tag{}, bs);
639 }
640 } size {};
641
642 //-----------------------------------------------
643
644 namespace detail {
645
646 template<class It>
647 auto
648 6 length_impl(It first, It last, int)
649 -> decltype(static_cast<std::size_t>(last - first))
650 {
651 6 return static_cast<std::size_t>(last - first);
652 }
653
654 template<class It>
655 std::size_t
656 42 length_impl(It first, It last, long)
657 {
658 42 std::size_t n = 0;
659
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 21 times.
60 while(first != last)
660 {
661 18 ++first;
662 18 ++n;
663 }
664 42 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
683