TLA Line data 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 HIT 6652 : 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 6652 : : it_(it)
126 6652 : , prefix_(prefix__)
127 6652 : , suffix_(suffix__)
128 6652 : , i_(i)
129 6652 : , n_(n)
130 : {
131 : // n_ is the index of the end iterator
132 6652 : }
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 9116 : operator==(
148 : const_iterator const& other) const noexcept
149 : {
150 : return
151 9144 : it_ == other.it_ &&
152 3326 : prefix_ == other.prefix_ &&
153 3326 : suffix_ == other.suffix_ &&
154 15768 : i_ == other.i_ &&
155 12442 : n_ == other.n_;
156 : }
157 :
158 : /// Test for inequality.
159 : bool
160 9116 : operator!=(
161 : const_iterator const& other) const noexcept
162 : {
163 9116 : return !(*this == other);
164 : }
165 :
166 : /// Return the current buffer, adjusted for prefix/suffix.
167 : reference
168 5790 : operator*() const noexcept
169 : {
170 5790 : value_type v = *it_;
171 : using P = std::conditional_t<
172 : MutableBufferSequence<BufferSequence>,
173 : char*, char const*>;
174 5790 : auto p = reinterpret_cast<P>(v.data());
175 5790 : auto n = v.size();
176 5790 : if(i_ == 0)
177 : {
178 2943 : p += prefix_;
179 2943 : n -= prefix_;
180 : }
181 5790 : if(i_ == n_ - 1)
182 2943 : n -= suffix_;
183 5790 : return value_type(p, n);
184 : }
185 :
186 : /// Advance to the next element.
187 : const_iterator&
188 4502 : operator++() noexcept
189 : {
190 4502 : BOOST_CAPY_ASSERT(i_ < n_);
191 4502 : ++it_;
192 4502 : ++i_;
193 4502 : return *this;
194 : }
195 :
196 : /// Advance to the next element (postfix).
197 : const_iterator
198 644 : operator++(int) noexcept
199 : {
200 644 : auto temp = *this;
201 644 : ++(*this);
202 644 : return temp;
203 : }
204 :
205 : /// Move to the previous element.
206 : const_iterator&
207 1288 : operator--() noexcept
208 : {
209 1288 : BOOST_CAPY_ASSERT(i_ > 0);
210 1288 : --it_;
211 1288 : --i_;
212 1288 : return *this;
213 : }
214 :
215 : /// Move to the previous element (postfix).
216 : const_iterator
217 644 : operator--(int) noexcept
218 : {
219 644 : auto temp = *this;
220 644 : --(*this);
221 644 : return temp;
222 : }
223 : };
224 :
225 : /** Constructor
226 : */
227 : slice_of() = default;
228 :
229 : /** Constructor
230 : */
231 194 : slice_of(
232 : BufferSequence const& bs)
233 194 : : bs_(bs)
234 : {
235 194 : iter_type it = capy::begin(bs_);
236 194 : iter_type eit = capy::end(bs_);
237 194 : begin_ = 0;
238 194 : end_ = std::distance(it, eit);
239 776 : while(it != eit)
240 : {
241 582 : value_type b(*it);
242 582 : size_ += b.size();
243 582 : ++len_;
244 582 : ++it;
245 : }
246 194 : }
247 :
248 : /** Return an iterator to the beginning of the sequence
249 : */
250 : const_iterator
251 3326 : begin() const noexcept
252 : {
253 : return const_iterator(
254 3326 : begin_iter_impl(), prefix_, suffix_, 0, len_);
255 : }
256 :
257 : /** Return an iterator to the end of the sequence
258 : */
259 : const_iterator
260 3326 : end() const noexcept
261 : {
262 : return const_iterator(
263 3326 : end_iter_impl(), prefix_, suffix_, len_, len_);
264 : }
265 :
266 : /// Slice customization point for this type.
267 : friend
268 : void
269 671 : tag_invoke(
270 : slice_tag const&,
271 : slice_of<BufferSequence>& bs,
272 : slice_how how,
273 : std::size_t n)
274 : {
275 671 : bs.slice_impl(how, n);
276 671 : }
277 :
278 : private:
279 : iter_type
280 3938 : begin_iter_impl() const noexcept
281 : {
282 3938 : iter_type it = capy::begin(bs_);
283 3938 : std::advance(it, begin_);
284 3938 : return it;
285 : }
286 :
287 : iter_type
288 3591 : end_iter_impl() const noexcept
289 : {
290 3591 : iter_type it = capy::begin(bs_);
291 3591 : std::advance(it, end_);
292 3591 : return it;
293 : }
294 :
295 : void
296 347 : remove_prefix_impl(
297 : std::size_t n)
298 : {
299 347 : if(n > size_)
300 25 : n = size_;
301 :
302 : // nice hack to simplify the loop (M. Nejati)
303 347 : n += prefix_;
304 347 : size_ += prefix_;
305 347 : prefix_ = 0;
306 :
307 347 : iter_type it = begin_iter_impl();
308 :
309 709 : while(n > 0 && begin_ != end_)
310 : {
311 612 : value_type b = *it;
312 612 : if(n < b.size())
313 : {
314 250 : prefix_ = n;
315 250 : size_ -= n;
316 250 : break;
317 : }
318 362 : n -= b.size();
319 362 : size_ -= b.size();
320 362 : ++begin_;
321 362 : ++it;
322 362 : --len_;
323 : }
324 347 : }
325 :
326 : void
327 265 : remove_suffix_impl(
328 : std::size_t n)
329 : {
330 265 : if(size_ == 0)
331 : {
332 MIS 0 : BOOST_CAPY_ASSERT(begin_ == end_);
333 HIT 265 : return;
334 : }
335 265 : BOOST_CAPY_ASSERT(begin_ != end_);
336 :
337 265 : if(n > size_)
338 MIS 0 : n = size_;
339 :
340 HIT 265 : n += suffix_;
341 265 : size_ += suffix_;
342 265 : suffix_ = 0;
343 :
344 265 : iter_type bit = begin_iter_impl();
345 265 : iter_type it = end_iter_impl();
346 265 : it--;
347 :
348 517 : while(it != bit)
349 : {
350 391 : value_type b = *it;
351 391 : if(n < b.size())
352 : {
353 139 : suffix_ = n;
354 139 : size_ -= n;
355 139 : return;
356 : }
357 252 : n -= b.size();
358 252 : size_ -= b.size();
359 252 : --it;
360 252 : --end_;
361 252 : --len_;
362 : }
363 126 : value_type b = *it;
364 126 : auto m = b.size() - prefix_;
365 126 : if(n < m)
366 : {
367 126 : suffix_ = n;
368 126 : size_ -= n;
369 126 : return;
370 : }
371 MIS 0 : end_ = begin_;
372 0 : len_ = 0;
373 0 : size_ = 0;
374 : }
375 :
376 : void
377 HIT 324 : keep_prefix_impl(
378 : std::size_t n)
379 : {
380 324 : if(n >= size_)
381 9 : return;
382 315 : if(n == 0)
383 : {
384 50 : end_ = begin_;
385 50 : len_ = 0;
386 50 : size_ = 0;
387 50 : return;
388 : }
389 265 : 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 671 : slice_impl(
410 : slice_how how,
411 : std::size_t n)
412 : {
413 671 : switch(how)
414 : {
415 347 : case slice_how::remove_prefix:
416 : {
417 347 : remove_prefix_impl(n);
418 347 : break;
419 : }
420 324 : case slice_how::keep_prefix:
421 : {
422 324 : keep_prefix_impl(n);
423 324 : break;
424 : }
425 : }
426 671 : }
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 3142 : void operator()(
443 : BufferSequence& bs,
444 : std::size_t n) const
445 : {
446 3142 : tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
447 3142 : }
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 1132 : void operator()(
457 : BufferSequence& bs,
458 : std::size_t n) const
459 : {
460 1132 : auto n0 = buffer_size(bs);
461 1132 : if(n < n0)
462 998 : tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
463 1132 : }
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 3369 : void operator()(
473 : BufferSequence& bs,
474 : std::size_t n) const
475 : {
476 3369 : tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
477 3369 : }
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 1386 : void operator()(
487 : BufferSequence& bs,
488 : std::size_t n) const
489 : {
490 1386 : auto n0 = buffer_size(bs);
491 1386 : if(n > 0)
492 : {
493 1297 : if( n > n0)
494 89 : n = n0;
495 1297 : tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
496 : }
497 1386 : }
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 944 : slice_type<BufferSequence> operator()(
506 : BufferSequence const& bs,
507 : std::size_t n) const noexcept
508 : {
509 944 : slice_type<BufferSequence> result(bs);
510 944 : keep_prefix(result, n);
511 944 : 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 959 : slice_type<BufferSequence> operator()(
536 : BufferSequence const& bs,
537 : std::size_t n) const noexcept
538 : {
539 959 : slice_type<BufferSequence> result(bs);
540 959 : remove_prefix(result, n);
541 959 : 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
|