include/boost/capy/buffers/slice.hpp

97.0% Lines (161/166) 100.0% Functions (107/107)
Line TLA 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 6652x 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 6652x : it_(it)
102 6652x , prefix_(prefix__)
103 6652x , suffix_(suffix__)
104 6652x , i_(i)
105 6652x , n_(n)
106 {
107 // n_ is the index of the end iterator
108 6652x }
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 9116x operator==(
123 const_iterator const& other) const noexcept
124 {
125 return
126 9144x it_ == other.it_ &&
127 3326x prefix_ == other.prefix_ &&
128 3326x suffix_ == other.suffix_ &&
129 15768x i_ == other.i_ &&
130 12442x n_ == other.n_;
131 }
132
133 bool
134 9116x operator!=(
135 const_iterator const& other) const noexcept
136 {
137 9116x return !(*this == other);
138 }
139
140 reference
141 5790x operator*() const noexcept
142 {
143 5790x value_type v = *it_;
144 using P = std::conditional_t<
145 MutableBufferSequence<BufferSequence>,
146 char*, char const*>;
147 5790x auto p = reinterpret_cast<P>(v.data());
148 5790x auto n = v.size();
149 5790x if(i_ == 0)
150 {
151 2943x p += prefix_;
152 2943x n -= prefix_;
153 }
154 5790x if(i_ == n_ - 1)
155 2943x n -= suffix_;
156 5790x return value_type(p, n);
157 }
158
159 const_iterator&
160 4502x operator++() noexcept
161 {
162 4502x BOOST_CAPY_ASSERT(i_ < n_);
163 4502x ++it_;
164 4502x ++i_;
165 4502x return *this;
166 }
167
168 const_iterator
169 644x operator++(int) noexcept
170 {
171 644x auto temp = *this;
172 644x ++(*this);
173 644x return temp;
174 }
175
176 const_iterator&
177 1288x operator--() noexcept
178 {
179 1288x BOOST_CAPY_ASSERT(i_ > 0);
180 1288x --it_;
181 1288x --i_;
182 1288x return *this;
183 }
184
185 const_iterator
186 644x operator--(int) noexcept
187 {
188 644x auto temp = *this;
189 644x --(*this);
190 644x return temp;
191 }
192 };
193
194 /** Constructor
195 */
196 slice_of() = default;
197
198 /** Constructor
199 */
200 194x slice_of(
201 BufferSequence const& bs)
202 194x : bs_(bs)
203 {
204 194x iter_type it = capy::begin(bs_);
205 194x iter_type eit = capy::end(bs_);
206 194x begin_ = 0;
207 194x end_ = std::distance(it, eit);
208 776x while(it != eit)
209 {
210 582x value_type b(*it);
211 582x size_ += b.size();
212 582x ++len_;
213 582x ++it;
214 }
215 194x }
216
217 /** Return an iterator to the beginning of the sequence
218 */
219 const_iterator
220 3326x begin() const noexcept
221 {
222 return const_iterator(
223 3326x begin_iter_impl(), prefix_, suffix_, 0, len_);
224 }
225
226 /** Return an iterator to the end of the sequence
227 */
228 const_iterator
229 3326x end() const noexcept
230 {
231 return const_iterator(
232 3326x end_iter_impl(), prefix_, suffix_, len_, len_);
233 }
234
235 friend
236 void
237 671x tag_invoke(
238 slice_tag const&,
239 slice_of<BufferSequence>& bs,
240 slice_how how,
241 std::size_t n)
242 {
243 671x bs.slice_impl(how, n);
244 671x }
245
246 private:
247 iter_type
248 3938x begin_iter_impl() const noexcept
249 {
250 3938x iter_type it = capy::begin(bs_);
251 3938x std::advance(it, begin_);
252 3938x return it;
253 }
254
255 iter_type
256 3591x end_iter_impl() const noexcept
257 {
258 3591x iter_type it = capy::begin(bs_);
259 3591x std::advance(it, end_);
260 3591x return it;
261 }
262
263 void
264 347x remove_prefix_impl(
265 std::size_t n)
266 {
267 347x if(n > size_)
268 25x n = size_;
269
270 // nice hack to simplify the loop (M. Nejati)
271 347x n += prefix_;
272 347x size_ += prefix_;
273 347x prefix_ = 0;
274
275 347x iter_type it = begin_iter_impl();
276
277 709x while(n > 0 && begin_ != end_)
278 {
279 612x value_type b = *it;
280 612x if(n < b.size())
281 {
282 250x prefix_ = n;
283 250x size_ -= n;
284 250x break;
285 }
286 362x n -= b.size();
287 362x size_ -= b.size();
288 362x ++begin_;
289 362x ++it;
290 362x --len_;
291 }
292 347x }
293
294 void
295 265x remove_suffix_impl(
296 std::size_t n)
297 {
298 265x if(size_ == 0)
299 {
300 BOOST_CAPY_ASSERT(begin_ == end_);
301 265x return;
302 }
303 265x BOOST_CAPY_ASSERT(begin_ != end_);
304
305 265x if(n > size_)
306 n = size_;
307
308 265x n += suffix_;
309 265x size_ += suffix_;
310 265x suffix_ = 0;
311
312 265x iter_type bit = begin_iter_impl();
313 265x iter_type it = end_iter_impl();
314 265x it--;
315
316 517x while(it != bit)
317 {
318 391x value_type b = *it;
319 391x if(n < b.size())
320 {
321 139x suffix_ = n;
322 139x size_ -= n;
323 139x return;
324 }
325 252x n -= b.size();
326 252x size_ -= b.size();
327 252x --it;
328 252x --end_;
329 252x --len_;
330 }
331 126x value_type b = *it;
332 126x auto m = b.size() - prefix_;
333 126x if(n < m)
334 {
335 126x suffix_ = n;
336 126x size_ -= n;
337 126x return;
338 }
339 end_ = begin_;
340 len_ = 0;
341 size_ = 0;
342 }
343
344 void
345 324x keep_prefix_impl(
346 std::size_t n)
347 {
348 324x if(n >= size_)
349 9x return;
350 315x if(n == 0)
351 {
352 50x end_ = begin_;
353 50x len_ = 0;
354 50x size_ = 0;
355 50x return;
356 }
357 265x 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 671x slice_impl(
378 slice_how how,
379 std::size_t n)
380 {
381 671x switch(how)
382 {
383 347x case slice_how::remove_prefix:
384 {
385 347x remove_prefix_impl(n);
386 347x break;
387 }
388 324x case slice_how::keep_prefix:
389 {
390 324x keep_prefix_impl(n);
391 324x break;
392 }
393 }
394 671x }
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 3142x void operator()(
413 BufferSequence& bs,
414 std::size_t n) const
415 {
416 3142x tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
417 3142x }
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 1132x void operator()(
427 BufferSequence& bs,
428 std::size_t n) const
429 {
430 1132x auto n0 = buffer_size(bs);
431 1132x if(n < n0)
432 998x tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
433 1132x }
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 3369x void operator()(
443 BufferSequence& bs,
444 std::size_t n) const
445 {
446 3369x tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
447 3369x }
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 1386x void operator()(
457 BufferSequence& bs,
458 std::size_t n) const
459 {
460 1386x auto n0 = buffer_size(bs);
461 1386x if(n > 0)
462 {
463 1297x if( n > n0)
464 89x n = n0;
465 1297x tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
466 }
467 1386x }
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 944x slice_type<BufferSequence> operator()(
478 BufferSequence const& bs,
479 std::size_t n) const noexcept
480 {
481 944x slice_type<BufferSequence> result(bs);
482 944x keep_prefix(result, n);
483 944x 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 959x slice_type<BufferSequence> operator()(
508 BufferSequence const& bs,
509 std::size_t n) const noexcept
510 {
511 959x slice_type<BufferSequence> result(bs);
512 959x remove_prefix(result, n);
513 959x 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