GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/buffer.hpp
Date: 2023-02-02 18:17:22
Exec Total Coverage
Lines: 132 140 94.3%
Functions: 45 46 97.8%
Branches: 31 40 77.5%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 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/http_proto
8 //
9
10 #ifndef BOOST_HTTP_PROTO_BUFFER_HPP
11 #define BOOST_HTTP_PROTO_BUFFER_HPP
12
13 #include <boost/http_proto/detail/config.hpp>
14 #include <boost/assert.hpp>
15 #include <boost/config/workaround.hpp>
16 #include <boost/type_traits/make_void.hpp>
17 #include <cstdlib>
18 #include <cstring>
19 #include <iterator>
20 #include <type_traits>
21 #include <utility>
22
23 namespace boost {
24 namespace http_proto {
25
26 /** size tag for tag_invoke.
27 */
28 struct size_tag {};
29
30 /** prefix tag for tag_invoke.
31 */
32 struct prefix_tag {};
33
34 /** suffix tag for tag-invoke.
35 */
36 struct suffix_tag {};
37
38 //------------------------------------------------
39
40 /** Holds a buffer that can be modified.
41 */
42 class mutable_buffer
43 {
44 unsigned char* p_ = nullptr;
45 std::size_t n_ = 0;
46
47 public:
48 using value_type = mutable_buffer;
49 using const_iterator =
50 value_type const*;
51
52 3187 mutable_buffer() = default;
53 mutable_buffer(
54 mutable_buffer const&) = default;
55 mutable_buffer& operator=(
56 mutable_buffer const&) = default;
57
58 8333 mutable_buffer(
59 void* data,
60 std::size_t size) noexcept
61 8333 : p_(static_cast<
62 unsigned char*>(data))
63 8333 , n_(size)
64 {
65 8333 }
66
67 #ifndef BOOST_HTTP_PROTO_DOCS
68 // conversion to boost::asio::mutable_buffer
69 template<
70 class T
71 , class = typename std::enable_if<
72 std::is_constructible<T,
73 void*, std::size_t>::value
74 && ! std::is_same<T, mutable_buffer>::value
75 //&& ! std::is_same<T, const_buffer>::value
76 >::type
77 >
78 operator T() const noexcept
79 {
80 return T{ data(), size() };
81 }
82 #endif
83
84 void*
85 7884 data() const noexcept
86 {
87 7884 return p_;
88 }
89
90 std::size_t
91 23271 size() const noexcept
92 {
93 23271 return n_;
94 }
95
96 const_iterator
97 56 begin() const noexcept
98 {
99 56 return this;
100 }
101
102 const_iterator
103 56 end() const noexcept
104 {
105 56 return this + 1;
106 }
107
108 /** Remove a prefix from the buffer.
109 */
110 mutable_buffer&
111 4848 operator+=(std::size_t n) noexcept
112 {
113
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 4815 times.
4848 if(n >= n_)
114 {
115 33 p_ = p_ + n_;
116 33 n_ = 0;
117 33 return *this;
118 }
119 4815 p_ = p_ + n;
120 4815 n_ -= n;
121 4815 return *this;
122 }
123
124 /** Return the buffer with a prefix removed.
125 */
126 friend
127 mutable_buffer
128 4844 operator+(
129 mutable_buffer b,
130 std::size_t n) noexcept
131 {
132 4844 return b += n;
133 }
134
135 /** Return the buffer with a prefix removed.
136 */
137 friend
138 mutable_buffer
139 1 operator+(
140 std::size_t n,
141 mutable_buffer b) noexcept
142 {
143 1 return b += n;
144 }
145
146 #ifndef BOOST_HTTP_PROTO_DOCS
147 friend
148 mutable_buffer
149 2 tag_invoke(
150 prefix_tag const&,
151 mutable_buffer const& b,
152 std::size_t n) noexcept
153 {
154
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if(n < b.size())
155 2 return { b.p_, n };
156 return b;
157 }
158
159 friend
160 mutable_buffer
161 2 tag_invoke(
162 suffix_tag const&,
163 mutable_buffer const& b,
164 std::size_t n) noexcept
165 {
166
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if(n < b.size())
167 2 return { b.p_ + b.n_ - n, n };
168 return b;
169 }
170 #endif
171 };
172
173 //------------------------------------------------
174
175 /** Holds a buffer that cannot be modified.
176 */
177 class const_buffer
178 {
179 unsigned char const* p_ = nullptr;
180 std::size_t n_ = 0;
181
182 public:
183 using value_type = const_buffer;
184 using const_iterator =
185 value_type const*;
186
187 29 const_buffer() = default;
188 const_buffer(
189 const_buffer const&) = default;
190 const_buffer& operator=(
191 const_buffer const&) = default;
192
193 5326 const_buffer(
194 void const* data,
195 std::size_t size) noexcept
196 5326 : p_(static_cast<
197 unsigned char const*>(data))
198 5326 , n_(size)
199 {
200 5326 }
201
202 14 const_buffer(
203 mutable_buffer const& b) noexcept
204 14 : p_(static_cast<
205 14 unsigned char const*>(b.data()))
206 14 , n_(b.size())
207 {
208 14 }
209
210 #ifndef BOOST_HTTP_PROTO_DOCS
211 // conversion to boost::asio::const_buffer
212 template<
213 class T
214 , class = typename std::enable_if<
215 std::is_constructible<T,
216 void const*, std::size_t>::value &&
217 ! std::is_same<T, mutable_buffer>::value &&
218 ! std::is_same<T, const_buffer>::value
219 >::type
220 >
221 operator T() const noexcept
222 {
223 return T{ data(), size() };
224 }
225 #endif
226
227 void const*
228 4851 data() const noexcept
229 {
230 4851 return p_;
231 }
232
233 std::size_t
234 15078 size() const noexcept
235 {
236 15078 return n_;
237 }
238
239 const_iterator
240 216 begin() const noexcept
241 {
242 216 return this;
243 }
244
245 const_iterator
246 216 end() const noexcept
247 {
248 216 return this + 1;
249 }
250
251 /** Remove a prefix from the buffer.
252 */
253 const_buffer&
254 4847 operator+=(std::size_t n) noexcept
255 {
256
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 4814 times.
4847 if(n >= n_)
257 {
258 33 p_ = p_ + n_;
259 33 n_ = 0;
260 33 return *this;
261 }
262 4814 p_ = p_ + n;
263 4814 n_ -= n;
264 4814 return *this;
265 }
266
267 /** Return the buffer with a prefix removed.
268 */
269 friend
270 const_buffer
271 4844 operator+(
272 const_buffer b,
273 std::size_t n) noexcept
274 {
275 4844 return b += n;
276 }
277
278 /** Return the buffer with a prefix removed.
279 */
280 friend
281 const_buffer
282 1 operator+(
283 std::size_t n,
284 const_buffer b) noexcept
285 {
286 1 return b += n;
287 }
288
289 #ifndef BOOST_HTTP_PROTO_DOCS
290 friend
291 const_buffer
292 2 tag_invoke(
293 prefix_tag const&,
294 const_buffer const& b,
295 std::size_t n) noexcept
296 {
297
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if(n < b.size())
298 2 return { b.p_, n };
299 return b;
300 }
301
302 friend
303 const_buffer
304 2 tag_invoke(
305 suffix_tag const&,
306 const_buffer const& b,
307 std::size_t n) noexcept
308 {
309
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if(n < b.size())
310 2 return { b.p_ + b.n_ - n, n };
311 return b;
312 }
313 #endif
314 };
315
316 //------------------------------------------------
317
318 #ifndef BOOST_HTTP_PROTO_DOCS
319 namespace detail {
320
321 // is bidirectional iterator
322 template<class T, class = void>
323 struct is_bidir_iter : std::false_type
324 {
325 };
326
327 template<class T>
328 struct is_bidir_iter<T, boost::void_t<decltype(
329 // LegacyIterator
330 *std::declval<T&>()
331 ),
332 // LegacyIterator
333 typename std::iterator_traits<T>::value_type,
334 typename std::iterator_traits<T>::difference_type,
335 typename std::iterator_traits<T>::reference,
336 typename std::iterator_traits<T>::pointer,
337 typename std::iterator_traits<T>::iterator_category,
338 typename std::enable_if<
339 // LegacyIterator
340 std::is_copy_constructible<T>::value &&
341 std::is_copy_assignable<T>::value &&
342 std::is_destructible<T>::value &&
343 std::is_same<T&, decltype(
344 ++std::declval<T&>())>::value &&
345 // Swappable
346 // VFALCO TODO
347 // EqualityComparable
348 std::is_convertible<decltype(
349 std::declval<T const&>() ==
350 std::declval<T const&>()),
351 bool>::value &&
352 // LegacyInputIterator
353 std::is_convertible<typename
354 std::iterator_traits<T>::reference, typename
355 std::iterator_traits<T>::value_type>::value &&
356 std::is_same<typename
357 std::iterator_traits<T>::reference,
358 decltype(*std::declval<T const&>())>::value &&
359 std::is_convertible<decltype(
360 std::declval<T const&>() !=
361 std::declval<T const&>()),
362 bool>::value &&
363 std::is_same<T&, decltype(
364 ++std::declval<T&>())>::value &&
365 // VFALCO (void)r++ (void)++r
366 std::is_convertible<decltype(
367 *std::declval<T&>()++), typename
368 std::iterator_traits<T>::value_type>::value &&
369 // LegacyForwardIterator
370 std::is_default_constructible<T>::value &&
371 std::is_same<T, decltype(
372 std::declval<T&>()++)>::value &&
373 std::is_same<typename
374 std::iterator_traits<T>::reference,
375 decltype(*std::declval<T&>()++)
376 >::value &&
377 // LegacyBidirectionalIterator
378 std::is_same<T&, decltype(
379 --std::declval<T&>())>::value &&
380 std::is_convertible<decltype(
381 std::declval<T&>()--),
382 T const&>::value &&
383 std::is_same<typename
384 std::iterator_traits<T>::reference,
385 decltype(*std::declval<T&>()--)>::value
386 >::type >>
387 : std::true_type
388 {
389 };
390
391 } // detail
392 #endif
393
394 //------------------------------------------------
395
396 // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
397
398 /** Determine if T is a ConstBuffers.
399 */
400 #if BOOST_HTTP_PROTO_DOCS
401 template<class T>
402 struct is_const_buffers
403 : std::integral_constant<bool, ...>{};
404 #else
405
406 template<class T, class = void>
407 struct is_const_buffers : std::false_type
408 {
409 };
410
411 template<class T>
412 struct is_const_buffers<T const>
413 : is_const_buffers<typename
414 std::decay<T>::type>
415 {
416 };
417
418 template<class T>
419 struct is_const_buffers<T const&>
420 : is_const_buffers<typename
421 std::decay<T>::type>
422 {
423 };
424
425 template<class T>
426 struct is_const_buffers<T&>
427 : is_const_buffers<typename
428 std::decay<T>::type>
429 {
430 };
431
432 template<class T>
433 struct is_const_buffers<T, boost::void_t<
434 typename std::enable_if<
435 (std::is_same<const_buffer, typename
436 T::value_type>::value
437 || std::is_same<mutable_buffer, typename
438 T::value_type>::value
439 ) &&
440 detail::is_bidir_iter<typename
441 T::const_iterator>::value &&
442 std::is_same<typename
443 T::const_iterator, decltype(
444 std::declval<T const&>().begin())
445 >::value &&
446 std::is_same<typename
447 T::const_iterator, decltype(
448 std::declval<T const&>().end())
449 >::value && (
450 std::is_same<const_buffer, typename
451 std::remove_const<typename
452 std::iterator_traits<
453 typename T::const_iterator
454 >::value_type>::type
455 >::value ||
456 std::is_same<mutable_buffer, typename
457 std::remove_const<typename
458 std::iterator_traits<
459 typename T::const_iterator
460 >::value_type>::type
461 >::value)
462 >::type
463 > > : std::is_move_constructible<T>
464 {
465 };
466
467 #endif
468
469 /** Determine if T is a MutableBuffers.
470 */
471 #if BOOST_HTTP_PROTO_DOCS
472 template<class T>
473 struct is_mutable_buffers
474 : std::integral_constant<bool, ...>{};
475 #else
476
477 template<class T, class = void>
478 struct is_mutable_buffers : std::false_type
479 {
480 };
481
482 template<class T>
483 struct is_mutable_buffers<T const>
484 : is_mutable_buffers<typename
485 std::decay<T>::type>
486 {
487 };
488
489 template<class T>
490 struct is_mutable_buffers<T const&>
491 : is_mutable_buffers<typename
492 std::decay<T>::type>
493 {
494 };
495
496 template<class T>
497 struct is_mutable_buffers<T&>
498 : is_mutable_buffers<typename
499 std::decay<T>::type>
500 {
501 };
502
503 template<class T>
504 struct is_mutable_buffers<T, boost::void_t<
505 typename std::enable_if<
506 detail::is_bidir_iter<typename
507 T::const_iterator>::value &&
508 std::is_same<typename
509 T::const_iterator, decltype(
510 std::declval<T const&>().begin())
511 >::value &&
512 std::is_same<typename
513 T::const_iterator, decltype(
514 std::declval<T const&>().end())
515 >::value &&
516 std::is_same<mutable_buffer, typename
517 std::remove_const<typename
518 std::iterator_traits<
519 typename T::const_iterator
520 >::value_type>::type
521 >::value
522 >::type
523 >> : std::is_move_constructible<T>
524 {
525 };
526
527 #endif
528
529 //------------------------------------------------
530
531 /** Determine if T is a DynamicBuffer
532 */
533 #if BOOST_HTTP_PROTO_DOCS
534 template<class T>
535 struct is_dynamic_buffer
536 : std::integral_constant<bool, ...>{};
537 #else
538
539 template<
540 class T,
541 class = void>
542 struct is_dynamic_buffer : std::false_type {};
543
544 template<class T>
545 struct is_dynamic_buffer<
546 T, boost::void_t<decltype(
547 std::declval<std::size_t&>() =
548 std::declval<T const&>().size()
549 ,std::declval<std::size_t&>() =
550 std::declval<T const&>().max_size()
551 ,std::declval<std::size_t&>() =
552 std::declval<T const&>().capacity()
553 ,std::declval<T&>().commit(
554 std::declval<std::size_t>())
555 ,std::declval<T&>().consume(
556 std::declval<std::size_t>())
557 )
558 ,typename std::enable_if<
559 is_const_buffers<typename
560 T::const_buffers_type>::value
561 && is_mutable_buffers<typename
562 T::mutable_buffers_type>::value
563 >::type
564 ,typename std::enable_if<
565 std::is_same<decltype(
566 std::declval<T const&>().data()),
567 typename T::const_buffers_type>::value
568 && std::is_same<decltype(
569 std::declval<T&>().prepare(
570 std::declval<std::size_t>())),
571 typename T::mutable_buffers_type>::value
572 >::type
573 > > : std::true_type
574 {
575 };
576
577 #endif
578
579 //------------------------------------------------
580
581 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
582 # pragma warning (push)
583 # pragma warning (disable: 4521) // multiple copy constructors specified
584 # pragma warning (disable: 4522) // multiple assignment operators specified
585 #endif
586
587 template<bool isConst>
588 class buffers_pair
589 {
590 public:
591 // VFALCO: This type is public otherwise
592 // asio::buffers_iterator won't compile.
593 using value_type = typename
594 std::conditional<isConst,
595 const_buffer,
596 mutable_buffer>::type;
597
598 using const_iterator = value_type const*;
599
600 buffers_pair() = default;
601
602 #if defined(BOOST_HTTP_PROTO_DOCS) || ( \
603 ! BOOST_WORKAROUND(BOOST_MSVC, < 1910))
604 buffers_pair(
605 buffers_pair const& other) = default;
606 buffers_pair& operator=(
607 buffers_pair const& other) = default;
608
609 #else
610 buffers_pair(
611 buffers_pair const& other) noexcept
612 : buffers_pair(
613 *other.begin(),
614 *(other.begin() + 1))
615 {
616 }
617
618 buffers_pair&
619 operator=(buffers_pair const& other)
620 {
621 b_[0] = other.b_[0];
622 b_[1] = other.b_[1];
623 return *this;
624 }
625 #endif
626
627 // const pair construction
628 // from mutable mutable pair
629 template<
630 bool isConst_ = isConst,
631 class = typename std::enable_if<
632 isConst_>::type>
633 buffers_pair(
634 buffers_pair<false> const& other)
635 : buffers_pair(
636 other.b_[0],
637 other.b_[1])
638 {
639 }
640
641 // const pair assignment
642 // from mutable mutable pair
643 template<
644 bool isConst_ = isConst,
645 class = typename std::enable_if<
646 isConst_>::type>
647 buffers_pair&
648 operator=(
649 buffers_pair<false> const& other)
650 {
651 b_[0] = other.b_[0];
652 b_[1] = other.b_[1];
653 return *this;
654 }
655
656 16534 buffers_pair(
657 value_type b0,
658 value_type b1) noexcept
659 16534 {
660
2/2
✓ Branch 1 taken 7877 times.
✓ Branch 2 taken 390 times.
16534 if(b0.size() > 0)
661 {
662 15754 b_[0] = b0;
663 15754 b_[1] = b1;
664 }
665 else
666 {
667 780 b_[0] = b1;
668 }
669 16534 }
670
671 const_buffer
672 operator[](
673 std::size_t i) const noexcept
674 {
675 BOOST_ASSERT(i < 2);
676 return b_[i];
677 }
678
679 const_iterator
680 13351 begin() const noexcept
681 {
682 13351 return b_;
683 }
684
685 const_iterator
686 10330 end() const noexcept
687 {
688
2/2
✓ Branch 1 taken 4681 times.
✓ Branch 2 taken 568 times.
10330 if(b_[1].size() > 0)
689 9362 return &b_[2];
690
1/2
✓ Branch 1 taken 568 times.
✗ Branch 2 not taken.
968 if(b_[0].size() > 0)
691 968 return &b_[1];
692 return b_;
693 }
694
695 private:
696 value_type b_[2];
697 };
698
699 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
700 # pragma warning (pop)
701 #endif
702
703 /** A mutable buffers pair
704 */
705 using mutable_buffers_pair =
706 buffers_pair<false>;
707
708 /** A const buffers pair
709 */
710 using const_buffers_pair =
711 buffers_pair<true>;
712
713 //------------------------------------------------
714
715 /** Return the total octets in a buffer sequence
716 */
717 template<
718 class ConstBuffers
719 #ifndef BOOST_HTTP_PROTO_DOCS
720 , class = typename std::enable_if<
721 is_const_buffers<ConstBuffers>::value
722 >::type
723 #endif
724 >
725 std::size_t
726 38 buffer_size(
727 ConstBuffers const& buffers) noexcept
728 {
729 38 std::size_t n = 0;
730
2/2
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 25 times.
90 for(const_buffer b : buffers)
731 52 n += b.size();
732 38 return n;
733 }
734
735 //------------------------------------------------
736
737 /** Copy buffer contents
738 */
739 template<
740 class MutableBuffers,
741 class ConstBuffers>
742 std::size_t
743 2754 buffer_copy(
744 MutableBuffers const& to,
745 ConstBuffers const& from,
746 std::size_t at_most =
747 std::size_t(-1)) noexcept
748 {
749 // If you get a compile error here it
750 // means that one or both of your types
751 // do not meet the requirements.
752 static_assert(
753 is_mutable_buffers<MutableBuffers>::value,
754 "Type requirements not met");
755 static_assert(
756 is_const_buffers<ConstBuffers>::value,
757 "Type requirements not met");
758
759 2754 std::size_t total = 0;
760 2754 std::size_t pos0 = 0;
761 2754 std::size_t pos1 = 0;
762 2754 auto const end0 = from.end();
763 2754 auto const end1 = to.end();
764 2754 auto it0 = from.begin();
765 2754 auto it1 = to.begin();
766 4843 while(
767
2/2
✓ Branch 0 taken 4780 times.
✓ Branch 1 taken 333 times.
5231 total < at_most &&
768
3/4
✓ Branch 0 taken 5113 times.
✓ Branch 1 taken 2366 times.
✓ Branch 2 taken 4780 times.
✗ Branch 3 not taken.
12445 it0 != end0 &&
769 it1 != end1)
770 {
771 const_buffer b0 =
772 4843 const_buffer(*it0) + pos0;
773 mutable_buffer b1 =
774 4843 mutable_buffer(*it1) + pos1;
775 std::size_t amount =
776 27673 [&]
777 {
778 4843 std::size_t n = b0.size();
779
3/4
✓ Branch 1 taken 1730 times.
✓ Branch 2 taken 3073 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 40 times.
4843 if( n > b1.size())
780 1730 n = b1.size();
781
3/4
✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 3075 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
4843 if( n > at_most - total)
782 1728 n = at_most - total;
783 4843 std::memcpy(
784 b1.data(),
785 b0.data(),
786 n);
787 4843 return n;
788 4843 }();
789 4843 total += amount;
790
2/2
✓ Branch 1 taken 1664 times.
✓ Branch 2 taken 3116 times.
4843 if(amount == b1.size())
791 {
792 1715 ++it1;
793 1715 pos1 = 0;
794 }
795 else
796 {
797 3128 pos1 += amount;
798 }
799
2/2
✓ Branch 1 taken 1828 times.
✓ Branch 2 taken 2952 times.
4843 if(amount == b0.size())
800 {
801 1891 ++it0;
802 1891 pos0 = 0;
803 }
804 else
805 {
806 2952 pos0 += amount;
807 }
808 }
809 2754 return total;
810 }
811
812 //------------------------------------------------
813
814 //
815 // size
816 //
817
818 //
819 // prefix
820 //
821
822 #ifndef BOOST_HTTP_PROTO_DOCS
823 template<class Buffers>
824 void
825 tag_invoke(
826 prefix_tag const&,
827 Buffers const&,
828 std::size_t) = delete;
829 #endif
830
831 /** Returns the type of a prefix of Buffers
832 */
833 #ifdef BOOST_HTTP_PROTO_DOCS
834 template<class Buffers>
835 using prefix_type = __see_below__;
836 #else
837 template<class Buffers>
838 using prefix_type = decltype(
839 tag_invoke(
840 prefix_tag{},
841 std::declval<Buffers const&>(),
842 std::size_t{}));
843 #endif
844
845 /** Return a prefix of the buffers.
846 */
847 template<class Buffers>
848 auto
849 8 prefix(
850 Buffers const& b,
851 std::size_t n) ->
852 prefix_type<Buffers>
853 {
854 static_assert(
855 is_const_buffers<Buffers>::value,
856 "Type requirements not met");
857
858 16 return tag_invoke(
859 8 prefix_tag{}, b, n);
860 }
861
862 /** Return a prefix of the buffers.
863 */
864 template<class Buffers>
865 auto
866 4 sans_suffix(
867 Buffers const& b,
868 std::size_t n) ->
869 prefix_type<Buffers>
870 {
871 4 auto const n0 = buffer_size(b);
872
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if( n > n0)
873 n = n0;
874 4 return prefix(b, n0 - n);
875 }
876
877 //
878 // suffix
879 //
880
881 #ifndef BOOST_HTTP_PROTO_DOCS
882 template<class Buffers>
883 void
884 tag_invoke(
885 suffix_tag const&,
886 Buffers const&,
887 std::size_t) = delete;
888 #endif
889
890 /** Returns the type of a suffix of Buffers.
891 */
892 #ifdef BOOST_HTTP_PROTO_DOCS
893 template<class Buffers>
894 using suffix_type = __see_below__;
895 #else
896 template<class Buffers>
897 using suffix_type = decltype(
898 tag_invoke(
899 suffix_tag{},
900 std::declval<Buffers const&>(),
901 std::size_t{}));
902 #endif
903
904 /** Return a suffix of the buffers.
905 */
906 template<class Buffers>
907 auto
908 8 suffix(
909 Buffers const& b,
910 std::size_t n) ->
911 suffix_type<Buffers>
912 {
913 static_assert(
914 is_const_buffers<Buffers>::value,
915 "Type requirements not met");
916
917 16 return tag_invoke(
918 8 suffix_tag{}, b, n);
919 }
920
921 /** Return a suffix of the buffers.
922 */
923 template<class Buffers>
924 auto
925 2 sans_prefix(
926 Buffers const& b,
927 std::size_t n) ->
928 suffix_type<Buffers>
929 {
930 static_assert(
931 is_const_buffers<Buffers>::value,
932 "Type requirements not met");
933
934 2 auto const n0 = buffer_size(b);
935 2 if( n > n0)
936 n = n0;
937 2 return suffix(b, n0 - n);
938 }
939
940 } // http_proto
941 } // boost
942
943 #endif
944