GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/slice.hpp
Date: 2025-12-06 02:12:43
Exec Total Coverage
Lines: 146 149 98.0%
Functions: 83 83 100.0%
Branches: 43 55 78.2%

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 278809 slice_of(
93 BufferSequence const& bs)
94 278809 : bs_(bs)
95 278809 , begin_(buffers::begin(bs_))
96 278809 , end_(buffers::end(bs_))
97 {
98 278809 auto it = begin_;
99
2/2
✓ Branch 0 taken 557906 times.
✓ Branch 1 taken 278721 times.
836979 while(it != end_)
100 {
101 558170 value_type b(*it);
102 558170 size_ += b.size();
103 558170 ++len_;
104 558170 ++it;
105 }
106 278809 }
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 266505 tag_invoke(
121 slice_tag const&,
122 slice_of<BufferSequence>& bs,
123 slice_how how,
124 std::size_t n)
125 {
126 266505 bs.slice_impl(how, n);
127 266505 }
128
129 private:
130 void
131 131194 remove_prefix_impl(
132 std::size_t n)
133 {
134 // nice hack to simplify the loop (M. Nejati)
135 131194 n += prefix_;
136 131194 size_ += prefix_;
137 131194 prefix_ = 0;
138
139
4/4
✓ Branch 0 taken 182372 times.
✓ Branch 1 taken 18581 times.
✓ Branch 2 taken 178273 times.
✓ Branch 3 taken 4099 times.
201047 while(n > 0 && begin_ != end_)
140 {
141 178351 value_type b = *begin_;
142
2/2
✓ Branch 1 taken 108474 times.
✓ Branch 2 taken 69799 times.
178351 if(n < b.size())
143 {
144 108498 prefix_ = n;
145 108498 size_ -= n;
146 108498 break;
147 }
148 69853 n -= b.size();
149 69853 size_ -= b.size();
150 69853 ++begin_;
151 69853 --len_;
152 }
153 131194 }
154
155 void
156 114791 remove_suffix_impl(
157 std::size_t n)
158 {
159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114759 times.
114791 if(size_ == 0)
160 {
161 BOOST_ASSERT(begin_ == end_);
162 114791 return;
163 }
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114759 times.
114791 BOOST_ASSERT(begin_ != end_);
165 114791 n += suffix_;
166 114791 size_ += suffix_;
167 114791 suffix_ = 0;
168 114791 iter_type it = end_;
169 114791 --it;
170
2/2
✓ Branch 0 taken 114829 times.
✓ Branch 1 taken 73945 times.
188842 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 73957 value_type b = *it;
186 73957 auto m = b.size() - prefix_;
187
1/2
✓ Branch 0 taken 73945 times.
✗ Branch 1 not taken.
73957 if(n < m)
188 {
189 73957 suffix_ = n;
190 73957 size_ -= n;
191 73957 return;
192 }
193 end_ = begin_;
194 len_ = 0;
195 }
196
197 void
198 135311 keep_prefix_impl(
199 std::size_t n)
200 {
201
2/2
✓ Branch 0 taken 8213 times.
✓ Branch 1 taken 127056 times.
135311 if(n >= size_)
202 8217 return;
203
2/2
✓ Branch 0 taken 12297 times.
✓ Branch 1 taken 114759 times.
127094 if(n == 0)
204 {
205 12303 end_ = begin_;
206 12303 len_ = 0;
207 12303 size_ = 0;
208 12303 return;
209 }
210 114791 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 266505 slice_impl(
231 slice_how how,
232 std::size_t n)
233 {
234
2/3
✓ Branch 0 taken 131154 times.
✓ Branch 1 taken 135269 times.
✗ Branch 2 not taken.
266505 switch(how)
235 {
236 131194 case slice_how::remove_prefix:
237 {
238 131194 remove_prefix_impl(n);
239 131194 break;
240 }
241 135311 case slice_how::keep_prefix:
242 {
243 135311 keep_prefix_impl(n);
244 135311 break;
245 }
246 }
247 266505 }
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 6412252 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 6412252 : it_(it)
276 6412252 , prefix_(prefix__)
277 6412252 , suffix_(suffix__)
278 6412252 , i_(i)
279 6412252 , n_(n)
280 {
281 // n_ is the index of the end iterator
282 6412252 }
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 7768114 operator==(
299 const_iterator const& other) const noexcept
300 {
301 return
302 10974239 it_ == other.it_ &&
303
1/2
✓ Branch 0 taken 3205113 times.
✗ Branch 1 not taken.
3206125 prefix_ == other.prefix_ &&
304
1/2
✓ Branch 0 taken 3205113 times.
✗ Branch 1 not taken.
3206125 suffix_ == other.suffix_ &&
305
3/4
✓ Branch 0 taken 3205113 times.
✓ Branch 1 taken 4560141 times.
✓ Branch 2 taken 3205113 times.
✗ Branch 3 not taken.
14180364 i_ == other.i_ &&
306
1/2
✓ Branch 0 taken 3205113 times.
✗ Branch 1 not taken.
10974239 n_ == other.n_;
307 }
308
309 bool
310 7768114 operator!=(
311 const_iterator const& other) const noexcept
312 {
313 7768114 return !(*this == other);
314 }
315
316 reference
317 4561989 operator*() const noexcept
318 {
319 4561989 value_type v = *it_;
320 using P = typename std::conditional<
321 is_mutable_buffer_sequence<BufferSequence>::value,
322 char*, char const*>::type;
323 4561989 auto p = reinterpret_cast<P>(v.data());
324 4561989 auto n = v.size();
325
2/2
✓ Branch 0 taken 2955699 times.
✓ Branch 1 taken 1604442 times.
4561989 if(i_ == 0)
326 {
327 2956579 p += prefix_;
328 2956579 n -= prefix_;
329 }
330
2/2
✓ Branch 0 taken 2955699 times.
✓ Branch 1 taken 1604442 times.
4561989 if(i_ == n_ - 1)
331 2956579 n -= suffix_;
332 4561989 return value_type(p, n);
333 }
334
335 const_iterator&
336 3004622 operator++() noexcept
337 {
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3003398 times.
3004622 BOOST_ASSERT(i_ < n_);
339 3004622 ++it_;
340 3004622 ++i_;
341 3004622 return *this;
342 }
343
344 const_iterator
345 778684 operator++(int) noexcept
346 {
347 778684 auto temp = *this;
348 778684 ++(*this);
349 778684 return temp;
350 }
351
352 const_iterator&
353 1557368 operator--() noexcept
354 {
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1556744 times.
1557368 BOOST_ASSERT(i_ > 0);
356 1557368 --it_;
357 1557368 --i_;
358 1557368 return *this;
359 }
360
361 const_iterator
362 778684 operator--(int) noexcept
363 {
364 778684 auto temp = *this;
365 778684 --(*this);
366 778684 return temp;
367 }
368 };
369
370 //------------------------------------------------
371
372 template<class BufferSequence>
373 auto
374 3206127 slice_of<BufferSequence>::
375 begin() const noexcept ->
376 const_iterator
377 {
378 return const_iterator(
379 3206127 this->begin_, prefix_, suffix_, 0, len_);
380 }
381
382 template<class BufferSequence>
383 auto
384 3206125 slice_of<BufferSequence>::
385 end() const noexcept ->
386 const_iterator
387 {
388 return const_iterator(
389 3206125 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 412994 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 69692 times.
412994 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
414 412994 }
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 140418 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 140418 auto n0 = size(bs);
429
2/2
✓ Branch 0 taken 123398 times.
✓ Branch 1 taken 16454 times.
140418 if(n < n0)
430
1/1
✓ Branch 1 taken 61478 times.
123896 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
431 140418 }
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 404679 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 69676 times.
404679 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
446 404679 }
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 140418 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 140418 auto n0 = size(bs);
461
2/2
✓ Branch 0 taken 131625 times.
✓ Branch 1 taken 8227 times.
140418 if(n > 0)
462 {
463
2/2
✓ Branch 0 taken 8227 times.
✓ Branch 1 taken 123398 times.
132157 if( n > n0)
464 8261 n = n0;
465
1/1
✓ Branch 1 taken 65577 times.
132157 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
466 }
467 140418 }
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 69 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 69 slice_type<BufferSequence> result(bs);
518 69 remove_prefix(result, n);
519 69 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