GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/slice.hpp
Date: 2025-12-09 13:02:49
Exec Total Coverage
Lines: 163 168 97.0%
Functions: 136 138 98.6%
Branches: 47 61 77.0%

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_SLICE_HPP
11 #define BOOST_BUFFERS_SLICE_HPP
12
13 #include <boost/buffers/detail/config.hpp>
14 #include <boost/buffers/buffer.hpp>
15 #include <boost/buffers/range.hpp>
16 #include <boost/assert.hpp>
17 #include <array>
18 #include <iterator>
19 #include <type_traits>
20
21 namespace boost {
22 namespace buffers {
23
24 template<class T> class slice_of;
25
26 namespace detail {
27
28 template<class T, class = void>
29 struct has_tag_invoke : std::false_type {};
30
31 template<class T>
32 struct has_tag_invoke<T, decltype(tag_invoke(
33 std::declval<slice_tag const&>(),
34 std::declval<T&>(),
35 std::declval<slice_how>(),
36 std::declval<std::size_t>()))>
37 : std::true_type {};
38
39 } // detail
40
41 /** Alias for the type representing a slice of T
42 */
43 template<class T>
44 using slice_type = typename std::conditional<
45 detail::has_tag_invoke<T>::value,
46 T, slice_of<T> >::type;
47
48 //------------------------------------------------
49
50 /** A wrapper enabling a buffer sequence to be consumed
51 */
52 template<class BufferSequence>
53 class slice_of
54 {
55 static_assert(! std::is_const<BufferSequence>::value,
56 "BufferSequence can't be const");
57
58 static_assert(! std::is_reference<BufferSequence>::value,
59 "BufferSequence can't be a reference");
60
61 static_assert(is_const_buffer_sequence<BufferSequence>::value,
62 "BufferSequence does not meet type requirements");
63
64 using iter_type = decltype(
65 std::declval<BufferSequence const&>().begin());
66
67 using difference_type =
68 typename std::iterator_traits<iter_type>::difference_type;
69
70 BufferSequence bs_;
71 difference_type begin_ = 0; // index of first buffer in sequence
72 difference_type end_ = 0; // 1 + index of last buffer in sequence
73 std::size_t len_ = 0; // length of bs_
74 std::size_t size_ = 0; // total bytes
75 std::size_t prefix_ = 0; // used prefix bytes
76 std::size_t suffix_ = 0; // used suffix bytes
77
78 public:
79 /** The type of values returned by iterators
80 */
81 using value_type = typename std::conditional<
82 is_mutable_buffer_sequence<BufferSequence>::value,
83 mutable_buffer, const_buffer>::type;
84
85 /** The type of returned iterators
86 */
87 class const_iterator;
88
89 /** Constructor
90 */
91 44 slice_of() = default;
92
93 /** Constructor
94 */
95 279218 slice_of(
96 BufferSequence const& bs)
97 279218 : bs_(bs)
98 {
99 279218 iter_type it = buffers::begin(bs_);
100 279218 iter_type eit = buffers::end(bs_);
101 279218 begin_ = 0;
102 279626 end_ = std::distance(it, eit);
103
3/3
✓ Branch 1 taken 279077 times.
✓ Branch 2 taken 292 times.
✓ Branch 0 taken 557642 times.
837750 while(it != eit)
104 {
105 558532 value_type b(*it);
106 558532 size_ += b.size();
107 558532 ++len_;
108 558532 ++it;
109 }
110 279218 }
111
112 /** Return an iterator to the beginning of the sequence
113 */
114 const_iterator
115 begin() const noexcept;
116
117 /** Return an iterator to the end of the sequence
118 */
119 const_iterator
120 end() const noexcept;
121
122 friend
123 void
124 267363 tag_invoke(
125 slice_tag const&,
126 slice_of<BufferSequence>& bs,
127 slice_how how,
128 std::size_t n)
129 {
130 267363 bs.slice_impl(how, n);
131 267363 }
132
133 private:
134 iter_type
135 3458845 begin_iter_impl() const noexcept
136 {
137 3458845 iter_type it = buffers::begin(bs_);
138 3458845 std::advance(it, begin_);
139 3458845 return it;
140 }
141
142 iter_type
143 3327213 end_iter_impl() const noexcept
144 {
145 3327213 iter_type it = buffers::begin(bs_);
146 3327213 std::advance(it, end_);
147 3327213 return it;
148 }
149
150 void
151 131628 remove_prefix_impl(
152 std::size_t n)
153 {
154
2/2
✓ Branch 0 taken 4127 times.
✓ Branch 1 taken 127371 times.
131628 if(n > size_)
155 4135 n = size_;
156
157 // nice hack to simplify the loop (M. Nejati)
158 131628 n += prefix_;
159 131628 size_ += prefix_;
160 131628 prefix_ = 0;
161
162 131628 iter_type it = begin_iter_impl();
163
164
3/4
✓ Branch 0 taken 177829 times.
✓ Branch 1 taken 22765 times.
✓ Branch 2 taken 177829 times.
✗ Branch 3 not taken.
200787 while(n > 0 && begin_ != end_)
165 {
166 177988 value_type b = *it;
167
2/2
✓ Branch 1 taken 108733 times.
✓ Branch 2 taken 69096 times.
177988 if(n < b.size())
168 {
169 108829 prefix_ = n;
170 108829 size_ -= n;
171 108829 break;
172 }
173 69159 n -= b.size();
174 69159 size_ -= b.size();
175 69159 ++begin_;
176 69159 ++it;
177 69159 --len_;
178 }
179 131628 }
180
181 void
182 115125 remove_suffix_impl(
183 std::size_t n)
184 {
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 115021 times.
115125 if(size_ == 0)
186 {
187 BOOST_ASSERT(begin_ == end_);
188 115125 return;
189 }
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 115021 times.
115125 BOOST_ASSERT(begin_ != end_);
191
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 115021 times.
115125 if(n > size_)
193 n = size_;
194
195 115125 n += suffix_;
196 115125 size_ += suffix_;
197 115125 suffix_ = 0;
198
199 115125 iter_type bit = begin_iter_impl();
200 115125 iter_type it = end_iter_impl();
201 115125 it--;
202
203
3/3
✓ Branch 1 taken 74258 times.
✓ Branch 2 taken 183 times.
✓ Branch 0 taken 114763 times.
189344 while(it != bit)
204 {
205 115144 value_type b = *it;
206
2/2
✓ Branch 1 taken 40905 times.
✓ Branch 2 taken 74183 times.
115144 if(n < b.size())
207 {
208 40925 suffix_ = n;
209 40925 size_ -= n;
210 40925 return;
211 }
212 74219 n -= b.size();
213 74219 size_ -= b.size();
214 74219 --it;
215 74219 --end_;
216 74219 --len_;
217 }
218 74200 value_type b = *it;
219 74200 auto m = b.size() - prefix_;
220
1/2
✓ Branch 0 taken 74116 times.
✗ Branch 1 not taken.
74200 if(n < m)
221 {
222 74200 suffix_ = n;
223 74200 size_ -= n;
224 74200 return;
225 }
226 end_ = begin_;
227 len_ = 0;
228 size_ = 0;
229 288 }
230
231 void
232 135735 keep_prefix_impl(
233 std::size_t n)
234 {
235
2/2
✓ Branch 0 taken 8230 times.
✓ Branch 1 taken 127367 times.
135735 if(n >= size_)
236 8249 return;
237
2/2
✓ Branch 0 taken 12346 times.
✓ Branch 1 taken 115021 times.
127486 if(n == 0)
238 {
239 12361 end_ = begin_;
240 12361 len_ = 0;
241 12361 size_ = 0;
242 12361 return;
243 }
244 115125 remove_suffix_impl(size_ - n);
245 }
246
247 void
248 keep_suffix_impl(
249 std::size_t n)
250 {
251 if(n >= size_)
252 return;
253 if(n == 0)
254 {
255 begin_ = end_;
256 len_ = 0;
257 size_ = 0;
258 return;
259 }
260 remove_prefix_impl(size_ - n);
261 }
262
263 void
264 267363 slice_impl(
265 slice_how how,
266 std::size_t n)
267 {
268
2/3
✓ Branch 0 taken 131498 times.
✓ Branch 1 taken 135597 times.
✗ Branch 2 not taken.
267363 switch(how)
269 {
270 131628 case slice_how::remove_prefix:
271 {
272 131628 remove_prefix_impl(n);
273 131628 break;
274 }
275 135735 case slice_how::keep_prefix:
276 {
277 135735 keep_prefix_impl(n);
278 135735 break;
279 }
280 }
281 267363 }
282 };
283
284 //------------------------------------------------
285
286 template<class BufferSequence>
287 class slice_of<BufferSequence>::
288 const_iterator
289 {
290 using iter_type = typename
291 slice_of::iter_type;
292
293 iter_type it_;
294 // VFALCO we could just point back to
295 // the original sequence to save size
296 std::size_t prefix_ = 0;
297 std::size_t suffix_ = 0;
298 std::size_t i_ = 0;
299 std::size_t n_ = 0;
300
301 friend class slice_of<BufferSequence>;
302
303 6424180 const_iterator(
304 iter_type it,
305 std::size_t prefix__,
306 std::size_t suffix__,
307 std::size_t i,
308 std::size_t n) noexcept
309 6424180 : it_(it)
310 6424180 , prefix_(prefix__)
311 6424180 , suffix_(suffix__)
312 6424180 , i_(i)
313 6424180 , n_(n)
314 {
315 // n_ is the index of the end iterator
316 6424180 }
317
318 public:
319 using value_type = typename slice_of::value_type;
320 using reference = value_type;
321 using pointer = void;
322 using difference_type = std::ptrdiff_t;
323 using iterator_category =
324 std::bidirectional_iterator_tag;
325 #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
326 using iterator_concept = std::bidirectional_iterator_tag; // (since C++20)
327 #endif
328
329 const_iterator() = default;
330
331 bool
332 7790311 operator==(
333 const_iterator const& other) const noexcept
334 {
335 return
336 10995425 it_ == other.it_ &&
337
1/2
✓ Branch 0 taken 3208729 times.
✗ Branch 1 not taken.
3212088 prefix_ == other.prefix_ &&
338
1/2
✓ Branch 0 taken 3208729 times.
✗ Branch 1 not taken.
3212088 suffix_ == other.suffix_ &&
339
3/4
✓ Branch 0 taken 3208729 times.
✓ Branch 1 taken 4574501 times.
✓ Branch 2 taken 3208729 times.
✗ Branch 3 not taken.
14214487 i_ == other.i_ &&
340
1/2
✓ Branch 0 taken 3208729 times.
✗ Branch 1 not taken.
11002399 n_ == other.n_;
341 }
342
343 bool
344 7790311 operator!=(
345 const_iterator const& other) const noexcept
346 {
347 7790311 return !(*this == other);
348 }
349
350 reference
351 4578223 operator*() const noexcept
352 {
353 4578223 value_type v = *it_;
354 using P = typename std::conditional<
355 is_mutable_buffer_sequence<BufferSequence>::value,
356 char*, char const*>::type;
357 4578223 auto p = reinterpret_cast<P>(v.data());
358 4578223 auto n = v.size();
359
2/2
✓ Branch 0 taken 2969221 times.
✓ Branch 1 taken 1605280 times.
4578223 if(i_ == 0)
360 {
361 2971974 p += prefix_;
362 2971974 n -= prefix_;
363 }
364
2/2
✓ Branch 0 taken 2969221 times.
✓ Branch 1 taken 1605280 times.
4578223 if(i_ == n_ - 1)
365 2971974 n -= suffix_;
366 4578223 return value_type(p, n);
367 }
368
369 const_iterator&
370 3015721 operator++() noexcept
371 {
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3013270 times.
3015721 BOOST_ASSERT(i_ < n_);
373 3015721 ++it_;
374 3015721 ++i_;
375 3015721 return *this;
376 }
377
378 const_iterator
379 781252 operator++(int) noexcept
380 {
381 781252 auto temp = *this;
382 781252 ++(*this);
383 781252 return temp;
384 }
385
386 const_iterator&
387 1562504 operator--() noexcept
388 {
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1561232 times.
1562504 BOOST_ASSERT(i_ > 0);
390 1562504 --it_;
391 1562504 --i_;
392 1562504 return *this;
393 }
394
395 const_iterator
396 781252 operator--(int) noexcept
397 {
398 781252 auto temp = *this;
399 781252 --(*this);
400 781252 return temp;
401 }
402 };
403
404 //------------------------------------------------
405
406 template<class BufferSequence>
407 auto
408 3212092 slice_of<BufferSequence>::
409 begin() const noexcept ->
410 const_iterator
411 {
412 return const_iterator(
413 3212092 begin_iter_impl(), prefix_, suffix_, 0, len_);
414 }
415
416 template<class BufferSequence>
417 auto
418 3212088 slice_of<BufferSequence>::
419 end() const noexcept ->
420 const_iterator
421 {
422 return const_iterator(
423 3212088 end_iter_impl(), prefix_, suffix_, len_, len_);
424 }
425
426 //------------------------------------------------
427
428 // in-place modify return value
429 // -----------------------------
430 // keep_prefix* prefix
431 // keep_suffix suffix
432 // remove_prefix* sans_prefix
433 // remove_suffix sans_suffix
434
435 /** Remove all but the first `n` bytes from a buffer sequence
436 */
437 constexpr struct keep_prefix_mrdocs_workaround_t
438 {
439 template<class BufferSequence>
440 413096 auto operator()(
441 BufferSequence& bs,
442 std::size_t n) const -> typename std::enable_if<
443 is_const_buffer_sequence<BufferSequence>::value &&
444 detail::has_tag_invoke<BufferSequence>::value>::type
445
446 {
447
1/1
✓ Branch 1 taken 69743 times.
413096 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
448 413096 }
449 } const keep_prefix{};
450
451 /** Remove all but the last `n` bytes from a buffer sequence
452 */
453 constexpr struct keep_suffix_mrdocs_workaround_t
454 {
455 template<class BufferSequence>
456 140520 auto operator()(
457 BufferSequence& bs,
458 std::size_t n) const -> typename std::enable_if<
459 is_const_buffer_sequence<BufferSequence>::value &&
460 detail::has_tag_invoke<BufferSequence>::value>::type
461 {
462 140520 auto n0 = size(bs);
463
2/2
✓ Branch 0 taken 123437 times.
✓ Branch 1 taken 16466 times.
140520 if(n < n0)
464
1/1
✓ Branch 1 taken 61517 times.
123974 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
465 140520 }
466 } const keep_suffix{};
467
468 /** Remove `n` bytes from the beginning of a buffer sequence
469 */
470 constexpr struct remove_prefix_mrdocs_workaround_t
471 {
472 template<class BufferSequence>
473 405083 auto operator()(
474 BufferSequence& bs,
475 std::size_t n) const -> typename std::enable_if<
476 is_const_buffer_sequence<BufferSequence>::value &&
477 detail::has_tag_invoke<BufferSequence>::value>::type
478 {
479
1/1
✓ Branch 1 taken 69981 times.
405083 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
480 405083 }
481 } const remove_prefix{};
482
483 /** Remove `n` bytes from the end of a buffer sequence
484 */
485 constexpr struct remove_suffix_mrdocs_workaround_t
486 {
487 template<class BufferSequence>
488 140774 auto operator()(
489 BufferSequence& bs,
490 std::size_t n) const -> typename std::enable_if<
491 is_const_buffer_sequence<BufferSequence>::value &&
492 detail::has_tag_invoke<BufferSequence>::value>::type
493 {
494 140774 auto n0 = size(bs);
495
2/2
✓ Branch 0 taken 131902 times.
✓ Branch 1 taken 8255 times.
140774 if(n > 0)
496 {
497
2/2
✓ Branch 0 taken 8255 times.
✓ Branch 1 taken 123647 times.
132479 if( n > n0)
498 8295 n = n0;
499
1/1
✓ Branch 1 taken 65854 times.
132479 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
500 }
501 140774 }
502 } const remove_suffix{};
503
504 //------------------------------------------------
505
506 /** Return a sequence representing the first `n` bytes of a buffer sequence
507 */
508 constexpr struct prefix_mrdocs_workaround_t
509 {
510 template<class BufferSequence>
511 108 auto operator()(
512 BufferSequence const& bs,
513 std::size_t n) const noexcept -> typename std::enable_if<
514 is_const_buffer_sequence<BufferSequence>::value,
515 slice_type<BufferSequence>>::type
516 {
517 108 slice_type<BufferSequence> result(bs);
518 108 keep_prefix(result, n);
519 108 return result;
520 }
521 } prefix{};
522
523 /** Return a sequence representing the last `n` bytes of a buffer sequence
524 */
525 constexpr struct suffix_mrdocs_workaround_t
526 {
527 template<class BufferSequence>
528 auto operator()(
529 BufferSequence const& bs,
530 std::size_t n) const noexcept -> typename std::enable_if<
531 is_const_buffer_sequence<BufferSequence>::value,
532 slice_type<BufferSequence>>::type
533 {
534 slice_type<BufferSequence> result(bs);
535 keep_suffix(result, n);
536 return result;
537 }
538 } suffix{};
539
540 /** Return a sequence representing all but the first `n` bytes of a buffer sequence
541 */
542 constexpr struct sans_prefix_mrdocs_workaround_t
543 {
544 template<class BufferSequence>
545 117 auto operator()(
546 BufferSequence const& bs,
547 std::size_t n) const noexcept -> typename std::enable_if<
548 is_const_buffer_sequence<BufferSequence>::value,
549 slice_type<BufferSequence>>::type
550 {
551 117 slice_type<BufferSequence> result(bs);
552 117 remove_prefix(result, n);
553 117 return result;
554 }
555 } sans_prefix{};
556
557 /** Return a sequence representing all but the last `n` bytes of a buffer sequence
558 */
559 constexpr struct sans_suffix_mrdocs_workaround_t
560 {
561 template<class BufferSequence>
562 auto operator()(
563 BufferSequence const& bs,
564 std::size_t n) const noexcept -> typename std::enable_if<
565 is_const_buffer_sequence<BufferSequence>::value,
566 slice_type<BufferSequence>>::type
567 {
568 slice_type<BufferSequence> result(bs);
569 remove_suffix(result, n);
570 return result;
571 }
572 } sans_suffix{};
573
574 } // buffers
575 } // boost
576
577 #endif
578