GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/buffers/slice.hpp
Date: 2026-01-18 18:26:31
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<ConstBufferSequence 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 MutableBufferSequence<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 8644 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 8644 : it_(it)
103 8644 , prefix_(prefix__)
104 8644 , suffix_(suffix__)
105 8644 , i_(i)
106 8644 , n_(n)
107 {
108 // n_ is the index of the end iterator
109 8644 }
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 12205 operator==(
124 const_iterator const& other) const noexcept
125 {
126 return
127 14245 it_ == other.it_ &&
128
1/2
✓ Branch 0 taken 3310 times.
✗ Branch 1 not taken.
4322 prefix_ == other.prefix_ &&
129
1/2
✓ Branch 0 taken 3310 times.
✗ Branch 1 not taken.
4322 suffix_ == other.suffix_ &&
130
3/4
✓ Branch 0 taken 3310 times.
✓ Branch 1 taken 6035 times.
✓ Branch 2 taken 3310 times.
✗ Branch 3 not taken.
20849 i_ == other.i_ &&
131
1/2
✓ Branch 0 taken 3310 times.
✗ Branch 1 not taken.
16527 n_ == other.n_;
132 }
133
134 bool
135 12205 operator!=(
136 const_iterator const& other) const noexcept
137 {
138 12205 return !(*this == other);
139 }
140
141 reference
142 7883 operator*() const noexcept
143 {
144 7883 value_type v = *it_;
145 using P = std::conditional_t<
146 MutableBufferSequence<BufferSequence>,
147 char*, char const*>;
148 7883 auto p = reinterpret_cast<P>(v.data());
149 7883 auto n = v.size();
150
2/2
✓ Branch 0 taken 2932 times.
✓ Branch 1 taken 3103 times.
7883 if(i_ == 0)
151 {
152 3812 p += prefix_;
153 3812 n -= prefix_;
154 }
155
2/2
✓ Branch 0 taken 2932 times.
✓ Branch 1 taken 3103 times.
7883 if(i_ == n_ - 1)
156 3812 n -= suffix_;
157 7883 return value_type(p, n);
158 }
159
160 const_iterator&
161 5971 operator++() noexcept
162 {
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4747 times.
5971 BOOST_ASSERT(i_ < n_);
164 5971 ++it_;
165 5971 ++i_;
166 5971 return *this;
167 }
168
169 const_iterator
170 956 operator++(int) noexcept
171 {
172 956 auto temp = *this;
173 956 ++(*this);
174 956 return temp;
175 }
176
177 const_iterator&
178 1912 operator--() noexcept
179 {
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1288 times.
1912 BOOST_ASSERT(i_ > 0);
181 1912 --it_;
182 1912 --i_;
183 1912 return *this;
184 }
185
186 const_iterator
187 956 operator--(int) noexcept
188 {
189 956 auto temp = *this;
190 956 --(*this);
191 956 return temp;
192 }
193 };
194
195 /** Constructor
196 */
197 slice_of() = default;
198
199 /** Constructor
200 */
201 280 slice_of(
202 BufferSequence const& bs)
203 280 : bs_(bs)
204 {
205 280 iter_type it = capy::begin(bs_);
206 280 iter_type eit = capy::end(bs_);
207 280 begin_ = 0;
208 280 end_ = std::distance(it, eit);
209
3/3
✓ Branch 0 taken 584 times.
✓ Branch 1 taken 368 times.
✓ Branch 2 taken 88 times.
1392 while(it != eit)
210 {
211 1112 value_type b(*it);
212 1112 size_ += b.size();
213 1112 ++len_;
214 1112 ++it;
215 }
216 280 }
217
218 /** Return an iterator to the beginning of the sequence
219 */
220 const_iterator
221 4322 begin() const noexcept
222 {
223 return const_iterator(
224 4322 begin_iter_impl(), prefix_, suffix_, 0, len_);
225 }
226
227 /** Return an iterator to the end of the sequence
228 */
229 const_iterator
230 4322 end() const noexcept
231 {
232 return const_iterator(
233 4322 end_iter_impl(), prefix_, suffix_, len_, len_);
234 }
235
236 friend
237 void
238 751 tag_invoke(
239 slice_tag const&,
240 slice_of<BufferSequence>& bs,
241 slice_how how,
242 std::size_t n)
243 {
244 751 bs.slice_impl(how, n);
245 751 }
246
247 private:
248 iter_type
249 4991 begin_iter_impl() const noexcept
250 {
251 4991 iter_type it = capy::begin(bs_);
252 4991 std::advance(it, begin_);
253 4991 return it;
254 }
255
256 iter_type
257 4615 end_iter_impl() const noexcept
258 {
259 4615 iter_type it = capy::begin(bs_);
260 4615 std::advance(it, end_);
261 4615 return it;
262 }
263
264 void
265 376 remove_prefix_impl(
266 std::size_t n)
267 {
268
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 311 times.
376 if(n > size_)
269 27 n = size_;
270
271 // nice hack to simplify the loop (M. Nejati)
272 376 n += prefix_;
273 376 size_ += prefix_;
274 376 prefix_ = 0;
275
276 376 iter_type it = begin_iter_impl();
277
278
3/4
✓ Branch 0 taken 596 times.
✓ Branch 1 taken 91 times.
✓ Branch 2 taken 596 times.
✗ Branch 3 not taken.
781 while(n > 0 && begin_ != end_)
279 {
280 674 value_type b = *it;
281
2/2
✓ Branch 1 taken 245 times.
✓ Branch 2 taken 351 times.
674 if(n < b.size())
282 {
283 269 prefix_ = n;
284 269 size_ -= n;
285 269 break;
286 }
287 405 n -= b.size();
288 405 size_ -= b.size();
289 405 ++begin_;
290 405 ++it;
291 405 --len_;
292 }
293 376 }
294
295 void
296 293 remove_suffix_impl(
297 std::size_t n)
298 {
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
293 if(size_ == 0)
300 {
301 BOOST_ASSERT(begin_ == end_);
302 293 return;
303 }
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
293 BOOST_ASSERT(begin_ != end_);
305
306
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
293 if(n > size_)
307 n = size_;
308
309 293 n += suffix_;
310 293 size_ += suffix_;
311 293 suffix_ = 0;
312
313 293 iter_type bit = begin_iter_impl();
314 293 iter_type it = end_iter_impl();
315 293 it--;
316
317
3/3
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 338 times.
✓ Branch 2 taken 111 times.
592 while(it != bit)
318 {
319 456 value_type b = *it;
320
2/2
✓ Branch 1 taken 137 times.
✓ Branch 2 taken 263 times.
456 if(n < b.size())
321 {
322 157 suffix_ = n;
323 157 size_ -= n;
324 157 return;
325 }
326 299 n -= b.size();
327 299 size_ -= b.size();
328 299 --it;
329 299 --end_;
330 299 --len_;
331 }
332 136 value_type b = *it;
333 136 auto m = b.size() - prefix_;
334
1/2
✓ Branch 0 taken 124 times.
✗ Branch 1 not taken.
136 if(n < m)
335 {
336 136 suffix_ = n;
337 136 size_ -= n;
338 136 return;
339 }
340 end_ = begin_;
341 len_ = 0;
342 size_ = 0;
343 }
344
345 void
346 375 keep_prefix_impl(
347 std::size_t n)
348 {
349
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 310 times.
375 if(n >= size_)
350 27 return;
351
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 261 times.
348 if(n == 0)
352 {
353 55 end_ = begin_;
354 55 len_ = 0;
355 55 size_ = 0;
356 55 return;
357 }
358 293 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 751 slice_impl(
379 slice_how how,
380 std::size_t n)
381 {
382
2/3
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 333 times.
✗ Branch 2 not taken.
751 switch(how)
383 {
384 376 case slice_how::remove_prefix:
385 {
386 376 remove_prefix_impl(n);
387 376 break;
388 }
389 375 case slice_how::keep_prefix:
390 {
391 375 keep_prefix_impl(n);
392 375 break;
393 }
394 }
395 751 }
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<ConstBufferSequence BufferSequence>
412 requires detail::has_tag_invoke<BufferSequence>::value
413 343362 void operator()(
414 BufferSequence& bs,
415 std::size_t n) const
416 {
417
1/1
✓ Branch 1 taken 60 times.
343362 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
418 343362 }
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<ConstBufferSequence BufferSequence>
426 requires detail::has_tag_invoke<BufferSequence>::value
427 70786 void operator()(
428 BufferSequence& bs,
429 std::size_t n) const
430 {
431 70786 auto n0 = buffer_size(bs);
432
2/2
✓ Branch 0 taken 61958 times.
✓ Branch 1 taken 8262 times.
70786 if(n < n0)
433
1/1
✓ Branch 1 taken 38 times.
62456 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
434 70786 }
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<ConstBufferSequence BufferSequence>
442 requires detail::has_tag_invoke<BufferSequence>::value
443 335301 void operator()(
444 BufferSequence& bs,
445 std::size_t n) const
446 {
447
1/1
✓ Branch 1 taken 298 times.
335301 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
448 335301 }
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<ConstBufferSequence BufferSequence>
456 requires detail::has_tag_invoke<BufferSequence>::value
457 71040 void operator()(
458 BufferSequence& bs,
459 std::size_t n) const
460 {
461 71040 auto n0 = buffer_size(bs);
462
2/2
✓ Branch 0 taken 66321 times.
✓ Branch 1 taken 4153 times.
71040 if(n > 0)
463 {
464
2/2
✓ Branch 0 taken 4153 times.
✓ Branch 1 taken 62168 times.
66853 if( n > n0)
465 4187 n = n0;
466
1/1
✓ Branch 1 taken 273 times.
66853 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
467 }
468 71040 }
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<ConstBufferSequence 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<ConstBufferSequence 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<ConstBufferSequence 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<ConstBufferSequence 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