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