libs/capy/include/boost/capy/buffers/slice.hpp

97.0% Lines (161/166) 100.0% Functions (99/99) 76.2% Branches (48/63)
libs/capy/include/boost/capy/buffers/slice.hpp
Line Branch Hits Source Code
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 <array>
16 #include <cassert>
17 #include <iterator>
18 #include <type_traits>
19
20 namespace boost {
21 namespace capy {
22
23 template<class T> class slice_of;
24
25 namespace detail {
26
27 template<class T, class = void>
28 struct has_tag_invoke : std::false_type {};
29
30 template<class T>
31 struct has_tag_invoke<T, decltype(tag_invoke(
32 std::declval<slice_tag const&>(),
33 std::declval<T&>(),
34 std::declval<slice_how>(),
35 std::declval<std::size_t>()))>
36 : std::true_type {};
37
38 } // detail
39
40 /** Alias for the type representing a slice of T
41 */
42 template<class T>
43 using slice_type = std::conditional_t<
44 detail::has_tag_invoke<T>::value,
45 T, slice_of<T>>;
46
47 //------------------------------------------------
48
49 /** A wrapper enabling a buffer sequence to be consumed
50 */
51 template<ConstBufferSequence BufferSequence>
52 class slice_of<BufferSequence>
53 {
54 static_assert(!std::is_const_v<BufferSequence>,
55 "BufferSequence can't be const");
56
57 static_assert(!std::is_reference_v<BufferSequence>,
58 "BufferSequence can't be a reference");
59
60 using iter_type = decltype(
61 std::declval<BufferSequence const&>().begin());
62
63 using difference_type =
64 typename std::iterator_traits<iter_type>::difference_type;
65
66 BufferSequence bs_;
67 difference_type begin_ = 0; // index of first buffer in sequence
68 difference_type end_ = 0; // 1 + index of last buffer in sequence
69 std::size_t len_ = 0; // length of bs_
70 std::size_t size_ = 0; // total bytes
71 std::size_t prefix_ = 0; // used prefix bytes
72 std::size_t suffix_ = 0; // used suffix bytes
73
74 public:
75 /** The type of values returned by iterators
76 */
77 using value_type = std::conditional_t<
78 MutableBufferSequence<BufferSequence>,
79 mutable_buffer, const_buffer>;
80
81 /** The type of returned iterators
82 */
83 class const_iterator
84 {
85 iter_type it_;
86 // VFALCO we could just point back to
87 // the original sequence to save size
88 std::size_t prefix_ = 0;
89 std::size_t suffix_ = 0;
90 std::size_t i_ = 0;
91 std::size_t n_ = 0;
92
93 friend class slice_of<BufferSequence>;
94
95 6652 const_iterator(
96 iter_type it,
97 std::size_t prefix__,
98 std::size_t suffix__,
99 std::size_t i,
100 std::size_t n) noexcept
101 6652 : it_(it)
102 6652 , prefix_(prefix__)
103 6652 , suffix_(suffix__)
104 6652 , i_(i)
105 6652 , n_(n)
106 {
107 // n_ is the index of the end iterator
108 6652 }
109
110 public:
111 using value_type = typename slice_of::value_type;
112 using reference = value_type;
113 using pointer = void;
114 using difference_type = std::ptrdiff_t;
115 using iterator_category =
116 std::bidirectional_iterator_tag;
117 using iterator_concept = std::bidirectional_iterator_tag;
118
119 const_iterator() = default;
120
121 bool
122 9116 operator==(
123 const_iterator const& other) const noexcept
124 {
125 return
126 9144 it_ == other.it_ &&
127
1/2
✓ Branch 0 taken 3326 times.
✗ Branch 1 not taken.
3326 prefix_ == other.prefix_ &&
128
1/2
✓ Branch 0 taken 3326 times.
✗ Branch 1 not taken.
3326 suffix_ == other.suffix_ &&
129
4/6
✓ Branch 0 taken 3326 times.
✓ Branch 1 taken 5790 times.
✓ Branch 2 taken 3298 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✗ Branch 3 not taken.
15768 i_ == other.i_ &&
130
1/2
✓ Branch 0 taken 3326 times.
✗ Branch 1 not taken.
12442 n_ == other.n_;
131 }
132
133 bool
134 9116 operator!=(
135 const_iterator const& other) const noexcept
136 {
137 9116 return !(*this == other);
138 }
139
140 reference
141 5790 operator*() const noexcept
142 {
143 5790 value_type v = *it_;
144 using P = std::conditional_t<
145 MutableBufferSequence<BufferSequence>,
146 char*, char const*>;
147 5790 auto p = reinterpret_cast<P>(v.data());
148 5790 auto n = v.size();
149
2/2
✓ Branch 0 taken 2943 times.
✓ Branch 1 taken 2847 times.
5790 if(i_ == 0)
150 {
151 2943 p += prefix_;
152 2943 n -= prefix_;
153 }
154
2/2
✓ Branch 0 taken 2943 times.
✓ Branch 1 taken 2847 times.
5790 if(i_ == n_ - 1)
155 2943 n -= suffix_;
156 5790 return value_type(p, n);
157 }
158
159 const_iterator&
160 4502 operator++() noexcept
161 {
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4502 times.
4502 BOOST_CAPY_ASSERT(i_ < n_);
163 4502 ++it_;
164 4502 ++i_;
165 4502 return *this;
166 }
167
168 const_iterator
169 644 operator++(int) noexcept
170 {
171 644 auto temp = *this;
172 644 ++(*this);
173 644 return temp;
174 }
175
176 const_iterator&
177 1288 operator--() noexcept
178 {
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1288 times.
1288 BOOST_CAPY_ASSERT(i_ > 0);
180 1288 --it_;
181 1288 --i_;
182 1288 return *this;
183 }
184
185 const_iterator
186 644 operator--(int) noexcept
187 {
188 644 auto temp = *this;
189 644 --(*this);
190 644 return temp;
191 }
192 };
193
194 /** Constructor
195 */
196 slice_of() = default;
197
198 /** Constructor
199 */
200 194 slice_of(
201 BufferSequence const& bs)
202 194 : bs_(bs)
203 {
204 194 iter_type it = capy::begin(bs_);
205 194 iter_type eit = capy::end(bs_);
206 194 begin_ = 0;
207 194 end_ = std::distance(it, eit);
208
3/3
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 554 times.
✓ Branch 2 taken 180 times.
776 while(it != eit)
209 {
210 582 value_type b(*it);
211 582 size_ += b.size();
212 582 ++len_;
213 582 ++it;
214 }
215 194 }
216
217 /** Return an iterator to the beginning of the sequence
218 */
219 const_iterator
220 3326 begin() const noexcept
221 {
222 return const_iterator(
223 3326 begin_iter_impl(), prefix_, suffix_, 0, len_);
224 }
225
226 /** Return an iterator to the end of the sequence
227 */
228 const_iterator
229 3326 end() const noexcept
230 {
231 return const_iterator(
232 3326 end_iter_impl(), prefix_, suffix_, len_, len_);
233 }
234
235 friend
236 void
237 671 tag_invoke(
238 slice_tag const&,
239 slice_of<BufferSequence>& bs,
240 slice_how how,
241 std::size_t n)
242 {
243 671 bs.slice_impl(how, n);
244 671 }
245
246 private:
247 iter_type
248 3938 begin_iter_impl() const noexcept
249 {
250 3938 iter_type it = capy::begin(bs_);
251 3938 std::advance(it, begin_);
252 3938 return it;
253 }
254
255 iter_type
256 3591 end_iter_impl() const noexcept
257 {
258 3591 iter_type it = capy::begin(bs_);
259 3591 std::advance(it, end_);
260 3591 return it;
261 }
262
263 void
264 347 remove_prefix_impl(
265 std::size_t n)
266 {
267
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 322 times.
347 if(n > size_)
268 25 n = size_;
269
270 // nice hack to simplify the loop (M. Nejati)
271 347 n += prefix_;
272 347 size_ += prefix_;
273 347 prefix_ = 0;
274
275 347 iter_type it = begin_iter_impl();
276
277
3/4
✓ Branch 0 taken 612 times.
✓ Branch 1 taken 97 times.
✓ Branch 2 taken 612 times.
✗ Branch 3 not taken.
709 while(n > 0 && begin_ != end_)
278 {
279 612 value_type b = *it;
280
2/2
✓ Branch 1 taken 250 times.
✓ Branch 2 taken 362 times.
612 if(n < b.size())
281 {
282 250 prefix_ = n;
283 250 size_ -= n;
284 250 break;
285 }
286 362 n -= b.size();
287 362 size_ -= b.size();
288 362 ++begin_;
289 362 ++it;
290 362 --len_;
291 }
292 347 }
293
294 void
295 265 remove_suffix_impl(
296 std::size_t n)
297 {
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 265 times.
265 if(size_ == 0)
299 {
300 BOOST_CAPY_ASSERT(begin_ == end_);
301 265 return;
302 }
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 265 times.
265 BOOST_CAPY_ASSERT(begin_ != end_);
304
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 265 times.
265 if(n > size_)
306 n = size_;
307
308 265 n += suffix_;
309 265 size_ += suffix_;
310 265 suffix_ = 0;
311
312 265 iter_type bit = begin_iter_impl();
313 265 iter_type it = end_iter_impl();
314 265 it--;
315
316
3/3
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 384 times.
✓ Branch 2 taken 123 times.
517 while(it != bit)
317 {
318 391 value_type b = *it;
319
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 252 times.
391 if(n < b.size())
320 {
321 139 suffix_ = n;
322 139 size_ -= n;
323 139 return;
324 }
325 252 n -= b.size();
326 252 size_ -= b.size();
327 252 --it;
328 252 --end_;
329 252 --len_;
330 }
331 126 value_type b = *it;
332 126 auto m = b.size() - prefix_;
333
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 if(n < m)
334 {
335 126 suffix_ = n;
336 126 size_ -= n;
337 126 return;
338 }
339 end_ = begin_;
340 len_ = 0;
341 size_ = 0;
342 }
343
344 void
345 324 keep_prefix_impl(
346 std::size_t n)
347 {
348
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 315 times.
324 if(n >= size_)
349 9 return;
350
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 265 times.
315 if(n == 0)
351 {
352 50 end_ = begin_;
353 50 len_ = 0;
354 50 size_ = 0;
355 50 return;
356 }
357 265 remove_suffix_impl(size_ - n);
358 }
359
360 void
361 keep_suffix_impl(
362 std::size_t n)
363 {
364 if(n >= size_)
365 return;
366 if(n == 0)
367 {
368 begin_ = end_;
369 len_ = 0;
370 size_ = 0;
371 return;
372 }
373 remove_prefix_impl(size_ - n);
374 }
375
376 void
377 671 slice_impl(
378 slice_how how,
379 std::size_t n)
380 {
381
2/3
✓ Branch 0 taken 347 times.
✓ Branch 1 taken 324 times.
✗ Branch 2 not taken.
671 switch(how)
382 {
383 347 case slice_how::remove_prefix:
384 {
385 347 remove_prefix_impl(n);
386 347 break;
387 }
388 324 case slice_how::keep_prefix:
389 {
390 324 keep_prefix_impl(n);
391 324 break;
392 }
393 }
394 671 }
395 };
396
397 //------------------------------------------------
398
399 // in-place modify return value
400 // -----------------------------
401 // keep_prefix* prefix
402 // keep_suffix suffix
403 // remove_prefix* sans_prefix
404 // remove_suffix sans_suffix
405
406 /** Remove all but the first `n` bytes from a buffer sequence
407 */
408 constexpr struct keep_prefix_mrdocs_workaround_t
409 {
410 template<ConstBufferSequence BufferSequence>
411 requires detail::has_tag_invoke<BufferSequence>::value
412 2598 void operator()(
413 BufferSequence& bs,
414 std::size_t n) const
415 {
416
1/1
✓ Branch 1 taken 51 times.
2598 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
417 2598 }
418 } const keep_prefix{};
419
420 /** Remove all but the last `n` bytes from a buffer sequence
421 */
422 constexpr struct keep_suffix_mrdocs_workaround_t
423 {
424 template<ConstBufferSequence BufferSequence>
425 requires detail::has_tag_invoke<BufferSequence>::value
426 588 void operator()(
427 BufferSequence& bs,
428 std::size_t n) const
429 {
430 588 auto n0 = buffer_size(bs);
431
2/2
✓ Branch 0 taken 518 times.
✓ Branch 1 taken 70 times.
588 if(n < n0)
432
1/1
✓ Branch 1 taken 38 times.
518 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
433 588 }
434 } const keep_suffix{};
435
436 /** Remove `n` bytes from the beginning of a buffer sequence
437 */
438 constexpr struct remove_prefix_mrdocs_workaround_t
439 {
440 template<ConstBufferSequence BufferSequence>
441 requires detail::has_tag_invoke<BufferSequence>::value
442 2825 void operator()(
443 BufferSequence& bs,
444 std::size_t n) const
445 {
446
1/1
✓ Branch 1 taken 309 times.
2825 tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
447 2825 }
448 } const remove_prefix{};
449
450 /** Remove `n` bytes from the end of a buffer sequence
451 */
452 constexpr struct remove_suffix_mrdocs_workaround_t
453 {
454 template<ConstBufferSequence BufferSequence>
455 requires detail::has_tag_invoke<BufferSequence>::value
456 842 void operator()(
457 BufferSequence& bs,
458 std::size_t n) const
459 {
460 842 auto n0 = buffer_size(bs);
461
2/2
✓ Branch 0 taken 785 times.
✓ Branch 1 taken 57 times.
842 if(n > 0)
462 {
463
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 728 times.
785 if( n > n0)
464 57 n = n0;
465
1/1
✓ Branch 1 taken 273 times.
785 tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
466 }
467 842 }
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<ConstBufferSequence BufferSequence>
477 944 slice_type<BufferSequence> operator()(
478 BufferSequence const& bs,
479 std::size_t n) const noexcept
480 {
481 944 slice_type<BufferSequence> result(bs);
482 944 keep_prefix(result, n);
483 944 return result;
484 }
485 } prefix{};
486
487 /** Return a sequence representing the last `n` bytes of a buffer sequence
488 */
489 constexpr struct suffix_mrdocs_workaround_t
490 {
491 template<ConstBufferSequence BufferSequence>
492 slice_type<BufferSequence> operator()(
493 BufferSequence const& bs,
494 std::size_t n) const noexcept
495 {
496 slice_type<BufferSequence> result(bs);
497 keep_suffix(result, n);
498 return result;
499 }
500 } suffix{};
501
502 /** Return a sequence representing all but the first `n` bytes of a buffer sequence
503 */
504 constexpr struct sans_prefix_mrdocs_workaround_t
505 {
506 template<ConstBufferSequence BufferSequence>
507 959 slice_type<BufferSequence> operator()(
508 BufferSequence const& bs,
509 std::size_t n) const noexcept
510 {
511 959 slice_type<BufferSequence> result(bs);
512 959 remove_prefix(result, n);
513 959 return result;
514 }
515 } sans_prefix{};
516
517 /** Return a sequence representing all but the last `n` bytes of a buffer sequence
518 */
519 constexpr struct sans_suffix_mrdocs_workaround_t
520 {
521 template<ConstBufferSequence BufferSequence>
522 slice_type<BufferSequence> operator()(
523 BufferSequence const& bs,
524 std::size_t n) const noexcept
525 {
526 slice_type<BufferSequence> result(bs);
527 remove_suffix(result, n);
528 return result;
529 }
530 } sans_suffix{};
531
532 } // capy
533 } // boost
534
535 #endif
536