GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/buffers/slice.hpp
Date: 2026-01-15 20:40:20
Exec Total Coverage
Lines: 161 166 97.0%
Functions: 81 81 100.0%
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/capy
8 //
9
10 #ifndef BOOST_CAPY_BUFFERS_SLICE_HPP
11 #define BOOST_CAPY_BUFFERS_SLICE_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/buffers.hpp>
15 #include <boost/capy/buffers/range.hpp>
16 #include <boost/assert.hpp>
17 #include <array>
18 #include <iterator>
19 #include <type_traits>
20
21 namespace boost {
22 namespace capy {
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 = std::conditional_t<
45 detail::has_tag_invoke<T>::value,
46 T, slice_of<T>>;
47
48 //------------------------------------------------
49
50 /** A wrapper enabling a buffer sequence to be consumed
51 */
52 template<const_buffer_sequence BufferSequence>
53 class slice_of<BufferSequence>
54 {
55 static_assert(!std::is_const_v<BufferSequence>,
56 "BufferSequence can't be const");
57
58 static_assert(!std::is_reference_v<BufferSequence>,
59 "BufferSequence can't be a reference");
60
61 using iter_type = decltype(
62 std::declval<BufferSequence const&>().begin());
63
64 using difference_type =
65 typename std::iterator_traits<iter_type>::difference_type;
66
67 BufferSequence bs_;
68 difference_type begin_ = 0; // index of first buffer in sequence
69 difference_type end_ = 0; // 1 + index of last buffer in sequence
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 = std::conditional_t<
79 mutable_buffer_sequence<BufferSequence>,
80 mutable_buffer, const_buffer>;
81
82 /** The type of returned iterators
83 */
84 class const_iterator
85 {
86 iter_type it_;
87 // VFALCO we could just point back to
88 // the original sequence to save size
89 std::size_t prefix_ = 0;
90 std::size_t suffix_ = 0;
91 std::size_t i_ = 0;
92 std::size_t n_ = 0;
93
94 friend class slice_of<BufferSequence>;
95
96 6414788 const_iterator(
97 iter_type it,
98 std::size_t prefix__,
99 std::size_t suffix__,
100 std::size_t i,
101 std::size_t n) noexcept
102 6414788 : it_(it)
103 6414788 , prefix_(prefix__)
104 6414788 , suffix_(suffix__)
105 6414788 , i_(i)
106 6414788 , n_(n)
107 {
108 // n_ is the index of the end iterator
109 6414788 }
110
111 public:
112 using value_type = typename slice_of::value_type;
113 using reference = value_type;
114 using pointer = void;
115 using difference_type = std::ptrdiff_t;
116 using iterator_category =
117 std::bidirectional_iterator_tag;
118 using iterator_concept = std::bidirectional_iterator_tag;
119
120 const_iterator() = default;
121
122 bool
123 7781869 operator==(
124 const_iterator const& other) const noexcept
125 {
126 return
127 10986981 it_ == other.it_ &&
128
1/2
✓ Branch 0 taken 3206382 times.
✗ Branch 1 not taken.
3207394 prefix_ == other.prefix_ &&
129
1/2
✓ Branch 0 taken 3206382 times.
✗ Branch 1 not taken.
3207394 suffix_ == other.suffix_ &&
130
3/4
✓ Branch 0 taken 3206382 times.
✓ Branch 1 taken 4572627 times.
✓ Branch 2 taken 3206382 times.
✗ Branch 3 not taken.
14196657 i_ == other.i_ &&
131
1/2
✓ Branch 0 taken 3206382 times.
✗ Branch 1 not taken.
10989263 n_ == other.n_;
132 }
133
134 bool
135 7781869 operator!=(
136 const_iterator const& other) const noexcept
137 {
138 7781869 return !(*this == other);
139 }
140
141 reference
142 4574475 operator*() const noexcept
143 {
144 4574475 value_type v = *it_;
145 using P = std::conditional_t<
146 mutable_buffer_sequence<BufferSequence>,
147 char*, char const*>;
148 4574475 auto p = reinterpret_cast<P>(v.data());
149 4574475 auto n = v.size();
150
2/2
✓ Branch 0 taken 2967348 times.
✓ Branch 1 taken 1605279 times.
4574475 if(i_ == 0)
151 {
152 2968228 p += prefix_;
153 2968228 n -= prefix_;
154 }
155
2/2
✓ Branch 0 taken 2967348 times.
✓ Branch 1 taken 1605279 times.
4574475 if(i_ == n_ - 1)
156 2968228 n -= suffix_;
157 4574475 return value_type(p, n);
158 }
159
160 const_iterator&
161 3013267 operator++() noexcept
162 {
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3012043 times.
3013267 BOOST_ASSERT(i_ < n_);
164 3013267 ++it_;
165 3013267 ++i_;
166 3013267 return *this;
167 }
168
169 const_iterator
170 780604 operator++(int) noexcept
171 {
172 780604 auto temp = *this;
173 780604 ++(*this);
174 780604 return temp;
175 }
176
177 const_iterator&
178 1561208 operator--() noexcept
179 {
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1560584 times.
1561208 BOOST_ASSERT(i_ > 0);
181 1561208 --it_;
182 1561208 --i_;
183 1561208 return *this;
184 }
185
186 const_iterator
187 780604 operator--(int) noexcept
188 {
189 780604 auto temp = *this;
190 780604 --(*this);
191 780604 return temp;
192 }
193 };
194
195 /** Constructor
196 */
197 slice_of() = default;
198
199 /** Constructor
200 */
201 278808 slice_of(
202 BufferSequence const& bs)
203 278808 : bs_(bs)
204 {
205 278808 iter_type it = capy::begin(bs_);
206 278808 iter_type eit = capy::end(bs_);
207 278808 begin_ = 0;
208 278808 end_ = std::distance(it, eit);
209
3/3
✓ Branch 0 taken 557640 times.
✓ Branch 1 taken 278896 times.
✓ Branch 2 taken 88 times.
836976 while(it != eit)
210 {
211 558168 value_type b(*it);
212 558168 size_ += b.size();
213 558168 ++len_;
214 558168 ++it;
215 }
216 278808 }
217
218 /** Return an iterator to the beginning of the sequence
219 */
220 const_iterator
221 3207394 begin() const noexcept
222 {
223 return const_iterator(
224 3207394 begin_iter_impl(), prefix_, suffix_, 0, len_);
225 }
226
227 /** Return an iterator to the end of the sequence
228 */
229 const_iterator
230 3207394 end() const noexcept
231 {
232 return const_iterator(
233 3207394 end_iter_impl(), prefix_, suffix_, len_, len_);
234 }
235
236 friend
237 void
238 266991 tag_invoke(
239 slice_tag const&,
240 slice_of<BufferSequence>& bs,
241 slice_how how,
242 std::size_t n)
243 {
244 266991 bs.slice_impl(how, n);
245 266991 }
246
247 private:
248 iter_type
249 3453823 begin_iter_impl() const noexcept
250 {
251 3453823 iter_type it = capy::begin(bs_);
252 3453823 std::advance(it, begin_);
253 3453823 return it;
254 }
255
256 iter_type
257 3322375 end_iter_impl() const noexcept
258 {
259 3322375 iter_type it = capy::begin(bs_);
260 3322375 std::advance(it, end_);
261 3322375 return it;
262 }
263
264 void
265 131448 remove_prefix_impl(
266 std::size_t n)
267 {
268
2/2
✓ Branch 0 taken 4121 times.
✓ Branch 1 taken 127287 times.
131448 if(n > size_)
269 4123 n = size_;
270
271 // nice hack to simplify the loop (M. Nejati)
272 131448 n += prefix_;
273 131448 size_ += prefix_;
274 131448 prefix_ = 0;
275
276 131448 iter_type it = begin_iter_impl();
277
278
3/4
✓ Branch 0 taken 177748 times.
✓ Branch 1 taken 22747 times.
✓ Branch 2 taken 177748 times.
✗ Branch 3 not taken.
200589 while(n > 0 && begin_ != end_)
279 {
280 177826 value_type b = *it;
281
2/2
✓ Branch 1 taken 108661 times.
✓ Branch 2 taken 69087 times.
177826 if(n < b.size())
282 {
283 108685 prefix_ = n;
284 108685 size_ -= n;
285 108685 break;
286 }
287 69141 n -= b.size();
288 69141 size_ -= b.size();
289 69141 ++begin_;
290 69141 ++it;
291 69141 --len_;
292 }
293 131448 }
294
295 void
296 114981 remove_suffix_impl(
297 std::size_t n)
298 {
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114949 times.
114981 if(size_ == 0)
300 {
301 BOOST_ASSERT(begin_ == end_);
302 114981 return;
303 }
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114949 times.
114981 BOOST_ASSERT(begin_ != end_);
305
306
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114949 times.
114981 if(n > size_)
307 n = size_;
308
309 114981 n += suffix_;
310 114981 size_ += suffix_;
311 114981 suffix_ = 0;
312
313 114981 iter_type bit = begin_iter_impl();
314 114981 iter_type it = end_iter_impl();
315 114981 it--;
316
317
3/3
✓ Branch 0 taken 114763 times.
✓ Branch 1 taken 74258 times.
✓ Branch 2 taken 111 times.
189200 while(it != bit)
318 {
319 115144 value_type b = *it;
320
2/2
✓ Branch 1 taken 40905 times.
✓ Branch 2 taken 74183 times.
115144 if(n < b.size())
321 {
322 40925 suffix_ = n;
323 40925 size_ -= n;
324 40925 return;
325 }
326 74219 n -= b.size();
327 74219 size_ -= b.size();
328 74219 --it;
329 74219 --end_;
330 74219 --len_;
331 }
332 74056 value_type b = *it;
333 74056 auto m = b.size() - prefix_;
334
1/2
✓ Branch 0 taken 74044 times.
✗ Branch 1 not taken.
74056 if(n < m)
335 {
336 74056 suffix_ = n;
337 74056 size_ -= n;
338 74056 return;
339 }
340 end_ = begin_;
341 len_ = 0;
342 size_ = 0;
343 }
344
345 void
346 135543 keep_prefix_impl(
347 std::size_t n)
348 {
349
2/2
✓ Branch 0 taken 8215 times.
✓ Branch 1 taken 127286 times.
135543 if(n >= size_)
350 8219 return;
351
2/2
✓ Branch 0 taken 12337 times.
✓ Branch 1 taken 114949 times.
127324 if(n == 0)
352 {
353 12343 end_ = begin_;
354 12343 len_ = 0;
355 12343 size_ = 0;
356 12343 return;
357 }
358 114981 remove_suffix_impl(size_ - n);
359 }
360
361 void
362 keep_suffix_impl(
363 std::size_t n)
364 {
365 if(n >= size_)
366 return;
367 if(n == 0)
368 {
369 begin_ = end_;
370 len_ = 0;
371 size_ = 0;
372 return;
373 }
374 remove_prefix_impl(size_ - n);
375 }
376
377 void
378 266991 slice_impl(
379 slice_how how,
380 std::size_t n)
381 {
382
2/3
✓ Branch 0 taken 131408 times.
✓ Branch 1 taken 135501 times.
✗ Branch 2 not taken.
266991 switch(how)
383 {
384 131448 case slice_how::remove_prefix:
385 {
386 131448 remove_prefix_impl(n);
387 131448 break;
388 }
389 135543 case slice_how::keep_prefix:
390 {
391 135543 keep_prefix_impl(n);
392 135543 break;
393 }
394 }
395 266991 }
396 };
397
398 //------------------------------------------------
399
400 // in-place modify return value
401 // -----------------------------
402 // keep_prefix* prefix
403 // keep_suffix suffix
404 // remove_prefix* sans_prefix
405 // remove_suffix sans_suffix
406
407 /** Remove all but the first `n` bytes from a buffer sequence
408 */
409 constexpr struct keep_prefix_mrdocs_workaround_t
410 {
411 template<const_buffer_sequence BufferSequence>
412 requires detail::has_tag_invoke<BufferSequence>::value
413 412994 void operator()(
414 BufferSequence& bs,
415 std::size_t n) const
416 {
417
1/1
✓ Branch 1 taken 69692 times.
412994 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
418 412994 }
419 } const keep_prefix{};
420
421 /** Remove all but the last `n` bytes from a buffer sequence
422 */
423 constexpr struct keep_suffix_mrdocs_workaround_t
424 {
425 template<const_buffer_sequence BufferSequence>
426 requires detail::has_tag_invoke<BufferSequence>::value
427 140418 void operator()(
428 BufferSequence& bs,
429 std::size_t n) const
430 {
431 140418 auto n0 = buffer_size(bs);
432
2/2
✓ Branch 0 taken 123398 times.
✓ Branch 1 taken 16454 times.
140418 if(n < n0)
433
1/1
✓ Branch 1 taken 61478 times.
123896 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
434 140418 }
435 } const keep_suffix{};
436
437 /** Remove `n` bytes from the beginning of a buffer sequence
438 */
439 constexpr struct remove_prefix_mrdocs_workaround_t
440 {
441 template<const_buffer_sequence BufferSequence>
442 requires detail::has_tag_invoke<BufferSequence>::value
443 404933 void operator()(
444 BufferSequence& bs,
445 std::size_t n) const
446 {
447
1/1
✓ Branch 1 taken 69930 times.
404933 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
448 404933 }
449 } const remove_prefix{};
450
451 /** Remove `n` bytes from the end of a buffer sequence
452 */
453 constexpr struct remove_suffix_mrdocs_workaround_t
454 {
455 template<const_buffer_sequence BufferSequence>
456 requires detail::has_tag_invoke<BufferSequence>::value
457 140672 void operator()(
458 BufferSequence& bs,
459 std::size_t n) const
460 {
461 140672 auto n0 = buffer_size(bs);
462
2/2
✓ Branch 0 taken 131857 times.
✓ Branch 1 taken 8249 times.
140672 if(n > 0)
463 {
464
2/2
✓ Branch 0 taken 8249 times.
✓ Branch 1 taken 123608 times.
132389 if( n > n0)
465 8283 n = n0;
466
1/1
✓ Branch 1 taken 65809 times.
132389 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
467 }
468 140672 }
469 } const remove_suffix{};
470
471 //------------------------------------------------
472
473 /** Return a sequence representing the first `n` bytes of a buffer sequence
474 */
475 constexpr struct prefix_mrdocs_workaround_t
476 {
477 template<const_buffer_sequence BufferSequence>
478 108 slice_type<BufferSequence> operator()(
479 BufferSequence const& bs,
480 std::size_t n) const noexcept
481 {
482 108 slice_type<BufferSequence> result(bs);
483 108 keep_prefix(result, n);
484 108 return result;
485 }
486 } prefix{};
487
488 /** Return a sequence representing the last `n` bytes of a buffer sequence
489 */
490 constexpr struct suffix_mrdocs_workaround_t
491 {
492 template<const_buffer_sequence BufferSequence>
493 slice_type<BufferSequence> operator()(
494 BufferSequence const& bs,
495 std::size_t n) const noexcept
496 {
497 slice_type<BufferSequence> result(bs);
498 keep_suffix(result, n);
499 return result;
500 }
501 } suffix{};
502
503 /** Return a sequence representing all but the first `n` bytes of a buffer sequence
504 */
505 constexpr struct sans_prefix_mrdocs_workaround_t
506 {
507 template<const_buffer_sequence BufferSequence>
508 69 slice_type<BufferSequence> operator()(
509 BufferSequence const& bs,
510 std::size_t n) const noexcept
511 {
512 69 slice_type<BufferSequence> result(bs);
513 69 remove_prefix(result, n);
514 69 return result;
515 }
516 } sans_prefix{};
517
518 /** Return a sequence representing all but the last `n` bytes of a buffer sequence
519 */
520 constexpr struct sans_suffix_mrdocs_workaround_t
521 {
522 template<const_buffer_sequence BufferSequence>
523 slice_type<BufferSequence> operator()(
524 BufferSequence const& bs,
525 std::size_t n) const noexcept
526 {
527 slice_type<BufferSequence> result(bs);
528 remove_suffix(result, n);
529 return result;
530 }
531 } sans_suffix{};
532
533 } // capy
534 } // boost
535
536 #endif
537