GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/slice.hpp
Date: 2025-12-08 14:54:08
Exec Total Coverage
Lines: 147 150 98.0%
Functions: 123 123 100.0%
Branches: 49 62 79.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 BufferSequence bs_;
68 iter_type begin_;
69 iter_type end_;
70 std::size_t len_ = 0; // length of bs_
71 std::size_t size_ = 0; // total bytes
72 std::size_t prefix_ = 0; // used prefix bytes
73 std::size_t suffix_ = 0; // used suffix bytes
74
75 public:
76 /** The type of values returned by iterators
77 */
78 using value_type = typename std::conditional<
79 is_mutable_buffer_sequence<BufferSequence>::value,
80 mutable_buffer, const_buffer>::type;
81
82 /** The type of returned iterators
83 */
84 class const_iterator;
85
86 /** Constructor
87 */
88 slice_of() = default;
89
90 /** Constructor
91 */
92 279218 slice_of(
93 BufferSequence const& bs)
94 279218 : bs_(bs)
95 279218 , begin_(buffers::begin(bs_))
96 279218 , end_(buffers::end(bs_))
97 {
98 279218 auto it = begin_;
99
3/3
✓ Branch 1 taken 278901 times.
✓ Branch 2 taken 204 times.
✓ Branch 0 taken 557906 times.
837750 while(it != end_)
100 {
101 558532 value_type b(*it);
102 558532 size_ += b.size();
103 558532 ++len_;
104 558532 ++it;
105 }
106 279218 }
107
108 /** Return an iterator to the beginning of the sequence
109 */
110 const_iterator
111 begin() const noexcept;
112
113 /** Return an iterator to the end of the sequence
114 */
115 const_iterator
116 end() const noexcept;
117
118 friend
119 void
120 266877 tag_invoke(
121 slice_tag const&,
122 slice_of<BufferSequence>& bs,
123 slice_how how,
124 std::size_t n)
125 {
126 266877 bs.slice_impl(how, n);
127 266877 }
128
129 private:
130 void
131 131374 remove_prefix_impl(
132 std::size_t n)
133 {
134 // nice hack to simplify the loop (M. Nejati)
135 131374 n += prefix_;
136 131374 size_ += prefix_;
137 131374 prefix_ = 0;
138
139
7/7
✓ Branch 0 taken 182459 times.
✓ Branch 1 taken 18593 times.
✓ Branch 3 taken 4180 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 81 times.
✓ Branch 6 taken 18 times.
✓ Branch 2 taken 178273 times.
201245 while(n > 0 && begin_ != end_)
140 {
141 178513 value_type b = *begin_;
142
2/2
✓ Branch 1 taken 108546 times.
✓ Branch 2 taken 69808 times.
178513 if(n < b.size())
143 {
144 108642 prefix_ = n;
145 108642 size_ -= n;
146 108642 break;
147 }
148 69871 n -= b.size();
149 69871 size_ -= b.size();
150 69871 ++begin_;
151 69871 --len_;
152 }
153 131374 }
154
155 void
156 114935 remove_suffix_impl(
157 std::size_t n)
158 {
159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114831 times.
114935 if(size_ == 0)
160 {
161 BOOST_ASSERT(begin_ == end_);
162 114935 return;
163 }
164
2/3
✓ Branch 1 taken 114759 times.
✓ Branch 2 taken 72 times.
✗ Branch 0 not taken.
114935 BOOST_ASSERT(begin_ != end_);
165 114935 n += suffix_;
166 114935 size_ += suffix_;
167 114935 suffix_ = 0;
168 114935 iter_type it = end_;
169 114935 --it;
170
3/3
✓ Branch 1 taken 73945 times.
✓ Branch 2 taken 72 times.
✓ Branch 0 taken 114829 times.
188986 while(it != begin_)
171 {
172 114885 value_type b = *it;
173
2/2
✓ Branch 1 taken 40814 times.
✓ Branch 2 taken 74015 times.
114885 if(n < b.size())
174 {
175 40834 suffix_ = n;
176 40834 size_ -= n;
177 40834 return;
178 }
179 74051 n -= b.size();
180 74051 size_ -= b.size();
181 74051 --it;
182 74051 --end_;
183 74051 --len_;
184 }
185 74101 value_type b = *it;
186 74101 auto m = b.size() - prefix_;
187
1/2
✓ Branch 0 taken 74017 times.
✗ Branch 1 not taken.
74101 if(n < m)
188 {
189 74101 suffix_ = n;
190 74101 size_ -= n;
191 74101 return;
192 }
193 end_ = begin_;
194 len_ = 0;
195 144 }
196
197 void
198 135503 keep_prefix_impl(
199 std::size_t n)
200 {
201
2/2
✓ Branch 0 taken 8228 times.
✓ Branch 1 taken 127137 times.
135503 if(n >= size_)
202 8247 return;
203
2/2
✓ Branch 0 taken 12306 times.
✓ Branch 1 taken 114831 times.
127256 if(n == 0)
204 {
205 12321 end_ = begin_;
206 12321 len_ = 0;
207 12321 size_ = 0;
208 12321 return;
209 }
210 114935 remove_suffix_impl(size_ - n);
211 }
212
213 void
214 keep_suffix_impl(
215 std::size_t n)
216 {
217 if(n >= size_)
218 return;
219 if(n == 0)
220 {
221 begin_ = end_;
222 len_ = 0;
223 size_ = 0;
224 return;
225 }
226 remove_prefix_impl(size_ - n);
227 }
228
229 void
230 266877 slice_impl(
231 slice_how how,
232 std::size_t n)
233 {
234
2/3
✓ Branch 0 taken 131244 times.
✓ Branch 1 taken 135365 times.
✗ Branch 2 not taken.
266877 switch(how)
235 {
236 131374 case slice_how::remove_prefix:
237 {
238 131374 remove_prefix_impl(n);
239 131374 break;
240 }
241 135503 case slice_how::keep_prefix:
242 {
243 135503 keep_prefix_impl(n);
244 135503 break;
245 }
246 }
247 266877 }
248 };
249
250 //------------------------------------------------
251
252 template<class BufferSequence>
253 class slice_of<BufferSequence>::
254 const_iterator
255 {
256 using iter_type = typename
257 slice_of::iter_type;
258
259 iter_type it_;
260 // VFALCO we could just point back to
261 // the original sequence to save size
262 std::size_t prefix_ = 0;
263 std::size_t suffix_ = 0;
264 std::size_t i_ = 0;
265 std::size_t n_ = 0;
266
267 friend class slice_of<BufferSequence>;
268
269 6421640 const_iterator(
270 iter_type it,
271 std::size_t prefix__,
272 std::size_t suffix__,
273 std::size_t i,
274 std::size_t n) noexcept
275 6421640 : it_(it)
276 6421640 , prefix_(prefix__)
277 6421640 , suffix_(suffix__)
278 6421640 , i_(i)
279 6421640 , n_(n)
280 {
281 // n_ is the index of the end iterator
282 6421640 }
283
284 public:
285 using value_type = typename slice_of::value_type;
286 using reference = value_type;
287 using pointer = void;
288 using difference_type = std::ptrdiff_t;
289 using iterator_category =
290 std::bidirectional_iterator_tag;
291 #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
292 using iterator_concept = std::bidirectional_iterator_tag; // (since C++20)
293 #endif
294
295 const_iterator() = default;
296
297 bool
298 7776553 operator==(
299 const_iterator const& other) const noexcept
300 {
301 return
302 10982679 it_ == other.it_ &&
303
1/2
✓ Branch 0 taken 3207459 times.
✗ Branch 1 not taken.
3210818 prefix_ == other.prefix_ &&
304
1/2
✓ Branch 0 taken 3207459 times.
✗ Branch 1 not taken.
3210818 suffix_ == other.suffix_ &&
305
3/4
✓ Branch 0 taken 3207459 times.
✓ Branch 1 taken 4562013 times.
✓ Branch 2 taken 3207459 times.
✗ Branch 3 not taken.
14198189 i_ == other.i_ &&
306
1/2
✓ Branch 0 taken 3207459 times.
✗ Branch 1 not taken.
10987371 n_ == other.n_;
307 }
308
309 bool
310 7776553 operator!=(
311 const_iterator const& other) const noexcept
312 {
313 7776553 return !(*this == other);
314 }
315
316 reference
317 4565735 operator*() const noexcept
318 {
319 4565735 value_type v = *it_;
320 using P = typename std::conditional<
321 is_mutable_buffer_sequence<BufferSequence>::value,
322 char*, char const*>::type;
323 4565735 auto p = reinterpret_cast<P>(v.data());
324 4565735 auto n = v.size();
325
2/2
✓ Branch 0 taken 2957571 times.
✓ Branch 1 taken 1604442 times.
4565735 if(i_ == 0)
326 {
327 2960324 p += prefix_;
328 2960324 n -= prefix_;
329 }
330
2/2
✓ Branch 0 taken 2957571 times.
✓ Branch 1 taken 1604442 times.
4565735 if(i_ == n_ - 1)
331 2960324 n -= suffix_;
332 4565735 return value_type(p, n);
333 }
334
335 const_iterator&
336 3007073 operator++() noexcept
337 {
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3004622 times.
3007073 BOOST_ASSERT(i_ < n_);
339 3007073 ++it_;
340 3007073 ++i_;
341 3007073 return *this;
342 }
343
344 const_iterator
345 779332 operator++(int) noexcept
346 {
347 779332 auto temp = *this;
348 779332 ++(*this);
349 779332 return temp;
350 }
351
352 const_iterator&
353 1558664 operator--() noexcept
354 {
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1557392 times.
1558664 BOOST_ASSERT(i_ > 0);
356 1558664 --it_;
357 1558664 --i_;
358 1558664 return *this;
359 }
360
361 const_iterator
362 779332 operator--(int) noexcept
363 {
364 779332 auto temp = *this;
365 779332 --(*this);
366 779332 return temp;
367 }
368 };
369
370 //------------------------------------------------
371
372 template<class BufferSequence>
373 auto
374 3210822 slice_of<BufferSequence>::
375 begin() const noexcept ->
376 const_iterator
377 {
378 return const_iterator(
379 3210822 this->begin_, prefix_, suffix_, 0, len_);
380 }
381
382 template<class BufferSequence>
383 auto
384 3210818 slice_of<BufferSequence>::
385 end() const noexcept ->
386 const_iterator
387 {
388 return const_iterator(
389 3210818 this->end_, prefix_, suffix_, len_, len_);
390 }
391
392 //------------------------------------------------
393
394 // in-place modify return value
395 // -----------------------------
396 // keep_prefix* prefix
397 // keep_suffix suffix
398 // remove_prefix* sans_prefix
399 // remove_suffix sans_suffix
400
401 /** Remove all but the first `n` bytes from a buffer sequence
402 */
403 constexpr struct keep_prefix_mrdocs_workaround_t
404 {
405 template<class BufferSequence>
406 413096 auto operator()(
407 BufferSequence& bs,
408 std::size_t n) const -> typename std::enable_if<
409 is_const_buffer_sequence<BufferSequence>::value &&
410 detail::has_tag_invoke<BufferSequence>::value>::type
411
412 {
413
1/1
✓ Branch 1 taken 69743 times.
413096 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
414 413096 }
415 } const keep_prefix{};
416
417 /** Remove all but the last `n` bytes from a buffer sequence
418 */
419 constexpr struct keep_suffix_mrdocs_workaround_t
420 {
421 template<class BufferSequence>
422 140520 auto operator()(
423 BufferSequence& bs,
424 std::size_t n) const -> typename std::enable_if<
425 is_const_buffer_sequence<BufferSequence>::value &&
426 detail::has_tag_invoke<BufferSequence>::value>::type
427 {
428 140520 auto n0 = size(bs);
429
2/2
✓ Branch 0 taken 123437 times.
✓ Branch 1 taken 16466 times.
140520 if(n < n0)
430
1/1
✓ Branch 1 taken 61517 times.
123974 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
431 140520 }
432 } const keep_suffix{};
433
434 /** Remove `n` bytes from the beginning of a buffer sequence
435 */
436 constexpr struct remove_prefix_mrdocs_workaround_t
437 {
438 template<class BufferSequence>
439 404907 auto operator()(
440 BufferSequence& bs,
441 std::size_t n) const -> typename std::enable_if<
442 is_const_buffer_sequence<BufferSequence>::value &&
443 detail::has_tag_invoke<BufferSequence>::value>::type
444 {
445
1/1
✓ Branch 1 taken 69727 times.
404907 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
446 404907 }
447 } const remove_prefix{};
448
449 /** Remove `n` bytes from the end of a buffer sequence
450 */
451 constexpr struct remove_suffix_mrdocs_workaround_t
452 {
453 template<class BufferSequence>
454 140520 auto operator()(
455 BufferSequence& bs,
456 std::size_t n) const -> typename std::enable_if<
457 is_const_buffer_sequence<BufferSequence>::value &&
458 detail::has_tag_invoke<BufferSequence>::value>::type
459 {
460 140520 auto n0 = size(bs);
461
2/2
✓ Branch 0 taken 131670 times.
✓ Branch 1 taken 8233 times.
140520 if(n > 0)
462 {
463
2/2
✓ Branch 0 taken 8233 times.
✓ Branch 1 taken 123437 times.
132247 if( n > n0)
464 8273 n = n0;
465
1/1
✓ Branch 1 taken 65622 times.
132247 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
466 }
467 140520 }
468 } const remove_suffix{};
469
470 //------------------------------------------------
471
472 /** Return a sequence representing the first `n` bytes of a buffer sequence
473 */
474 constexpr struct prefix_mrdocs_workaround_t
475 {
476 template<class BufferSequence>
477 108 auto operator()(
478 BufferSequence const& bs,
479 std::size_t n) const noexcept -> typename std::enable_if<
480 is_const_buffer_sequence<BufferSequence>::value,
481 slice_type<BufferSequence>>::type
482 {
483 108 slice_type<BufferSequence> result(bs);
484 108 keep_prefix(result, n);
485 108 return result;
486 }
487 } prefix{};
488
489 /** Return a sequence representing the last `n` bytes of a buffer sequence
490 */
491 constexpr struct suffix_mrdocs_workaround_t
492 {
493 template<class BufferSequence>
494 auto operator()(
495 BufferSequence const& bs,
496 std::size_t n) const noexcept -> typename std::enable_if<
497 is_const_buffer_sequence<BufferSequence>::value,
498 slice_type<BufferSequence>>::type
499 {
500 slice_type<BufferSequence> result(bs);
501 keep_suffix(result, n);
502 return result;
503 }
504 } suffix{};
505
506 /** Return a sequence representing all but the first `n` bytes of a buffer sequence
507 */
508 constexpr struct sans_prefix_mrdocs_workaround_t
509 {
510 template<class BufferSequence>
511 195 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 195 slice_type<BufferSequence> result(bs);
518 195 remove_prefix(result, n);
519 195 return result;
520 }
521 } sans_prefix{};
522
523 /** Return a sequence representing all but the last `n` bytes of a buffer sequence
524 */
525 constexpr struct sans_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 remove_suffix(result, n);
536 return result;
537 }
538 } sans_suffix{};
539
540 } // buffers
541 } // boost
542
543 #endif
544