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/buffers
8 : //
9 :
10 : #ifndef BOOST_BUFFERS_SLICE_HPP
11 : #define BOOST_BUFFERS_SLICE_HPP
12 :
13 : #include <boost/buffers/detail/config.hpp>
14 : #include <boost/buffers/buffer.hpp>
15 : #include <boost/buffers/range.hpp>
16 : #include <boost/assert.hpp>
17 : #include <array>
18 : #include <iterator>
19 : #include <type_traits>
20 :
21 : namespace boost {
22 : namespace buffers {
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 = typename std::conditional<
45 : detail::has_tag_invoke<T>::value,
46 : T, slice_of<T> >::type;
47 :
48 : //------------------------------------------------
49 :
50 : /** A wrapper enabling a buffer sequence to be consumed
51 : */
52 : template<class BufferSequence>
53 : class slice_of
54 : {
55 : static_assert(! std::is_const<BufferSequence>::value,
56 : "BufferSequence can't be const");
57 :
58 : static_assert(! std::is_reference<BufferSequence>::value,
59 : "BufferSequence can't be a reference");
60 :
61 : static_assert(is_const_buffer_sequence<BufferSequence>::value,
62 : "BufferSequence does not meet type requirements");
63 :
64 : using iter_type = decltype(
65 : std::declval<BufferSequence const&>().begin());
66 :
67 : using difference_type =
68 : typename std::iterator_traits<iter_type>::difference_type;
69 :
70 : BufferSequence bs_;
71 : difference_type begin_ = 0; // index of first buffer in sequence
72 : difference_type end_ = 0; // 1 + index of last buffer in sequence
73 : std::size_t len_ = 0; // length of bs_
74 : std::size_t size_ = 0; // total bytes
75 : std::size_t prefix_ = 0; // used prefix bytes
76 : std::size_t suffix_ = 0; // used suffix bytes
77 :
78 : public:
79 : /** The type of values returned by iterators
80 : */
81 : using value_type = typename std::conditional<
82 : is_mutable_buffer_sequence<BufferSequence>::value,
83 : mutable_buffer, const_buffer>::type;
84 :
85 : /** The type of returned iterators
86 : */
87 : class const_iterator;
88 :
89 : /** Constructor
90 : */
91 44 : slice_of() = default;
92 :
93 : /** Constructor
94 : */
95 278925 : slice_of(
96 : BufferSequence const& bs)
97 278925 : : bs_(bs)
98 : {
99 278925 : iter_type it = buffers::begin(bs_);
100 278925 : iter_type eit = buffers::end(bs_);
101 278925 : begin_ = 0;
102 279129 : end_ = std::distance(it, eit);
103 837011 : while(it != eit)
104 : {
105 558086 : value_type b(*it);
106 558086 : size_ += b.size();
107 558086 : ++len_;
108 558086 : ++it;
109 : }
110 278925 : }
111 :
112 : /** Return an iterator to the beginning of the sequence
113 : */
114 : const_iterator
115 : begin() const noexcept;
116 :
117 : /** Return an iterator to the end of the sequence
118 : */
119 : const_iterator
120 : end() const noexcept;
121 :
122 : friend
123 : void
124 267095 : tag_invoke(
125 : slice_tag const&,
126 : slice_of<BufferSequence>& bs,
127 : slice_how how,
128 : std::size_t n)
129 : {
130 267095 : bs.slice_impl(how, n);
131 267095 : }
132 :
133 : private:
134 : iter_type
135 3455250 : begin_iter_impl() const noexcept
136 : {
137 3455250 : iter_type it = buffers::begin(bs_);
138 3455250 : std::advance(it, begin_);
139 3455250 : return it;
140 : }
141 :
142 : iter_type
143 3323750 : end_iter_impl() const noexcept
144 : {
145 3323750 : iter_type it = buffers::begin(bs_);
146 3323750 : std::advance(it, end_);
147 3323750 : return it;
148 : }
149 :
150 : void
151 131498 : remove_prefix_impl(
152 : std::size_t n)
153 : {
154 131498 : if(n > size_)
155 4127 : n = size_;
156 :
157 : // nice hack to simplify the loop (M. Nejati)
158 131498 : n += prefix_;
159 131498 : size_ += prefix_;
160 131498 : prefix_ = 0;
161 :
162 131498 : iter_type it = begin_iter_impl();
163 :
164 200594 : while(n > 0 && begin_ != end_)
165 : {
166 177829 : value_type b = *it;
167 177829 : if(n < b.size())
168 : {
169 108733 : prefix_ = n;
170 108733 : size_ -= n;
171 108733 : break;
172 : }
173 69096 : n -= b.size();
174 69096 : size_ -= b.size();
175 69096 : ++begin_;
176 69096 : ++it;
177 69096 : --len_;
178 : }
179 131498 : }
180 :
181 : void
182 115021 : remove_suffix_impl(
183 : std::size_t n)
184 : {
185 115021 : if(size_ == 0)
186 : {
187 0 : BOOST_ASSERT(begin_ == end_);
188 115021 : return;
189 : }
190 115021 : BOOST_ASSERT(begin_ != end_);
191 :
192 115021 : if(n > size_)
193 0 : n = size_;
194 :
195 115021 : n += suffix_;
196 115021 : size_ += suffix_;
197 115021 : suffix_ = 0;
198 :
199 115021 : iter_type bit = begin_iter_impl();
200 115021 : iter_type it = end_iter_impl();
201 115021 : it--;
202 :
203 189204 : while(it != bit)
204 : {
205 115088 : value_type b = *it;
206 115088 : if(n < b.size())
207 : {
208 40905 : suffix_ = n;
209 40905 : size_ -= n;
210 40905 : return;
211 : }
212 74183 : n -= b.size();
213 74183 : size_ -= b.size();
214 74183 : --it;
215 74183 : --end_;
216 74183 : --len_;
217 : }
218 74116 : value_type b = *it;
219 74116 : auto m = b.size() - prefix_;
220 74116 : if(n < m)
221 : {
222 74116 : suffix_ = n;
223 74116 : size_ -= n;
224 74116 : return;
225 : }
226 0 : end_ = begin_;
227 0 : len_ = 0;
228 0 : size_ = 0;
229 144 : }
230 :
231 : void
232 135597 : keep_prefix_impl(
233 : std::size_t n)
234 : {
235 135597 : if(n >= size_)
236 8230 : return;
237 127367 : if(n == 0)
238 : {
239 12346 : end_ = begin_;
240 12346 : len_ = 0;
241 12346 : size_ = 0;
242 12346 : return;
243 : }
244 115021 : remove_suffix_impl(size_ - n);
245 : }
246 :
247 : void
248 : keep_suffix_impl(
249 : std::size_t n)
250 : {
251 : if(n >= size_)
252 : return;
253 : if(n == 0)
254 : {
255 : begin_ = end_;
256 : len_ = 0;
257 : size_ = 0;
258 : return;
259 : }
260 : remove_prefix_impl(size_ - n);
261 : }
262 :
263 : void
264 267095 : slice_impl(
265 : slice_how how,
266 : std::size_t n)
267 : {
268 267095 : switch(how)
269 : {
270 131498 : case slice_how::remove_prefix:
271 : {
272 131498 : remove_prefix_impl(n);
273 131498 : break;
274 : }
275 135597 : case slice_how::keep_prefix:
276 : {
277 135597 : keep_prefix_impl(n);
278 135597 : break;
279 : }
280 : }
281 267095 : }
282 : };
283 :
284 : //------------------------------------------------
285 :
286 : template<class BufferSequence>
287 : class slice_of<BufferSequence>::
288 : const_iterator
289 : {
290 : using iter_type = typename
291 : slice_of::iter_type;
292 :
293 : iter_type it_;
294 : // VFALCO we could just point back to
295 : // the original sequence to save size
296 : std::size_t prefix_ = 0;
297 : std::size_t suffix_ = 0;
298 : std::size_t i_ = 0;
299 : std::size_t n_ = 0;
300 :
301 : friend class slice_of<BufferSequence>;
302 :
303 6417460 : const_iterator(
304 : iter_type it,
305 : std::size_t prefix__,
306 : std::size_t suffix__,
307 : std::size_t i,
308 : std::size_t n) noexcept
309 6417460 : : it_(it)
310 6417460 : , prefix_(prefix__)
311 6417460 : , suffix_(suffix__)
312 6417460 : , i_(i)
313 6417460 : , n_(n)
314 : {
315 : // n_ is the index of the end iterator
316 6417460 : }
317 :
318 : public:
319 : using value_type = typename slice_of::value_type;
320 : using reference = value_type;
321 : using pointer = void;
322 : using difference_type = std::ptrdiff_t;
323 : using iterator_category =
324 : std::bidirectional_iterator_tag;
325 : #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
326 : using iterator_concept = std::bidirectional_iterator_tag; // (since C++20)
327 : #endif
328 :
329 : const_iterator() = default;
330 :
331 : bool
332 7783230 : operator==(
333 : const_iterator const& other) const noexcept
334 : {
335 : return
336 10987331 : it_ == other.it_ &&
337 3208729 : prefix_ == other.prefix_ &&
338 3208729 : suffix_ == other.suffix_ &&
339 14200688 : i_ == other.i_ &&
340 10991959 : n_ == other.n_;
341 : }
342 :
343 : bool
344 7783230 : operator!=(
345 : const_iterator const& other) const noexcept
346 : {
347 7783230 : return !(*this == other);
348 : }
349 :
350 : reference
351 4574501 : operator*() const noexcept
352 : {
353 4574501 : value_type v = *it_;
354 : using P = typename std::conditional<
355 : is_mutable_buffer_sequence<BufferSequence>::value,
356 : char*, char const*>::type;
357 4574501 : auto p = reinterpret_cast<P>(v.data());
358 4574501 : auto n = v.size();
359 4574501 : if(i_ == 0)
360 : {
361 2969221 : p += prefix_;
362 2969221 : n -= prefix_;
363 : }
364 4574501 : if(i_ == n_ - 1)
365 2969221 : n -= suffix_;
366 4574501 : return value_type(p, n);
367 : }
368 :
369 : const_iterator&
370 3013270 : operator++() noexcept
371 : {
372 3013270 : BOOST_ASSERT(i_ < n_);
373 3013270 : ++it_;
374 3013270 : ++i_;
375 3013270 : return *this;
376 : }
377 :
378 : const_iterator
379 780616 : operator++(int) noexcept
380 : {
381 780616 : auto temp = *this;
382 780616 : ++(*this);
383 780616 : return temp;
384 : }
385 :
386 : const_iterator&
387 1561232 : operator--() noexcept
388 : {
389 1561232 : BOOST_ASSERT(i_ > 0);
390 1561232 : --it_;
391 1561232 : --i_;
392 1561232 : return *this;
393 : }
394 :
395 : const_iterator
396 780616 : operator--(int) noexcept
397 : {
398 780616 : auto temp = *this;
399 780616 : --(*this);
400 780616 : return temp;
401 : }
402 : };
403 :
404 : //------------------------------------------------
405 :
406 : template<class BufferSequence>
407 : auto
408 3208731 : slice_of<BufferSequence>::
409 : begin() const noexcept ->
410 : const_iterator
411 : {
412 : return const_iterator(
413 3208731 : begin_iter_impl(), prefix_, suffix_, 0, len_);
414 : }
415 :
416 : template<class BufferSequence>
417 : auto
418 3208729 : slice_of<BufferSequence>::
419 : end() const noexcept ->
420 : const_iterator
421 : {
422 : return const_iterator(
423 3208729 : end_iter_impl(), prefix_, suffix_, len_, len_);
424 : }
425 :
426 : //------------------------------------------------
427 :
428 : // in-place modify return value
429 : // -----------------------------
430 : // keep_prefix* prefix
431 : // keep_suffix suffix
432 : // remove_prefix* sans_prefix
433 : // remove_suffix sans_suffix
434 :
435 : /** Remove all but the first `n` bytes from a buffer sequence
436 : */
437 : constexpr struct keep_prefix_mrdocs_workaround_t
438 : {
439 : template<class BufferSequence>
440 276191 : auto operator()(
441 : BufferSequence& bs,
442 : std::size_t n) const -> typename std::enable_if<
443 : is_const_buffer_sequence<BufferSequence>::value &&
444 : detail::has_tag_invoke<BufferSequence>::value>::type
445 :
446 : {
447 276191 : tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
448 276191 : }
449 : } const keep_prefix{};
450 :
451 : /** Remove all but the last `n` bytes from a buffer sequence
452 : */
453 : constexpr struct keep_suffix_mrdocs_workaround_t
454 : {
455 : template<class BufferSequence>
456 139903 : auto operator()(
457 : BufferSequence& bs,
458 : std::size_t n) const -> typename std::enable_if<
459 : is_const_buffer_sequence<BufferSequence>::value &&
460 : detail::has_tag_invoke<BufferSequence>::value>::type
461 : {
462 139903 : auto n0 = size(bs);
463 139903 : if(n < n0)
464 123437 : tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
465 139903 : }
466 : } const keep_suffix{};
467 :
468 : /** Remove `n` bytes from the beginning of a buffer sequence
469 : */
470 : constexpr struct remove_prefix_mrdocs_workaround_t
471 : {
472 : template<class BufferSequence>
473 272370 : auto operator()(
474 : BufferSequence& bs,
475 : std::size_t n) const -> typename std::enable_if<
476 : is_const_buffer_sequence<BufferSequence>::value &&
477 : detail::has_tag_invoke<BufferSequence>::value>::type
478 : {
479 272370 : tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
480 272370 : }
481 : } const remove_prefix{};
482 :
483 : /** Remove `n` bytes from the end of a buffer sequence
484 : */
485 : constexpr struct remove_suffix_mrdocs_workaround_t
486 : {
487 : template<class BufferSequence>
488 140157 : auto operator()(
489 : BufferSequence& bs,
490 : std::size_t n) const -> typename std::enable_if<
491 : is_const_buffer_sequence<BufferSequence>::value &&
492 : detail::has_tag_invoke<BufferSequence>::value>::type
493 : {
494 140157 : auto n0 = size(bs);
495 140157 : if(n > 0)
496 : {
497 131902 : if( n > n0)
498 8255 : n = n0;
499 131902 : tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
500 : }
501 140157 : }
502 : } const remove_suffix{};
503 :
504 : //------------------------------------------------
505 :
506 : /** Return a sequence representing the first `n` bytes of a buffer sequence
507 : */
508 : constexpr struct prefix_mrdocs_workaround_t
509 : {
510 : template<class BufferSequence>
511 54 : auto operator()(
512 : BufferSequence const& bs,
513 : std::size_t n) const noexcept -> typename std::enable_if<
514 : is_const_buffer_sequence<BufferSequence>::value,
515 : slice_type<BufferSequence>>::type
516 : {
517 54 : slice_type<BufferSequence> result(bs);
518 54 : keep_prefix(result, n);
519 54 : return result;
520 : }
521 : } prefix{};
522 :
523 : /** Return a sequence representing the last `n` bytes of a buffer sequence
524 : */
525 : constexpr struct suffix_mrdocs_workaround_t
526 : {
527 : template<class BufferSequence>
528 : auto operator()(
529 : BufferSequence const& bs,
530 : std::size_t n) const noexcept -> typename std::enable_if<
531 : is_const_buffer_sequence<BufferSequence>::value,
532 : slice_type<BufferSequence>>::type
533 : {
534 : slice_type<BufferSequence> result(bs);
535 : keep_suffix(result, n);
536 : return result;
537 : }
538 : } suffix{};
539 :
540 : /** Return a sequence representing all but the first `n` bytes of a buffer sequence
541 : */
542 : constexpr struct sans_prefix_mrdocs_workaround_t
543 : {
544 : template<class BufferSequence>
545 117 : auto operator()(
546 : BufferSequence const& bs,
547 : std::size_t n) const noexcept -> typename std::enable_if<
548 : is_const_buffer_sequence<BufferSequence>::value,
549 : slice_type<BufferSequence>>::type
550 : {
551 117 : slice_type<BufferSequence> result(bs);
552 117 : remove_prefix(result, n);
553 117 : return result;
554 : }
555 : } sans_prefix{};
556 :
557 : /** Return a sequence representing all but the last `n` bytes of a buffer sequence
558 : */
559 : constexpr struct sans_suffix_mrdocs_workaround_t
560 : {
561 : template<class BufferSequence>
562 : auto operator()(
563 : BufferSequence const& bs,
564 : std::size_t n) const noexcept -> typename std::enable_if<
565 : is_const_buffer_sequence<BufferSequence>::value,
566 : slice_type<BufferSequence>>::type
567 : {
568 : slice_type<BufferSequence> result(bs);
569 : remove_suffix(result, n);
570 : return result;
571 : }
572 : } sans_suffix{};
573 :
574 : } // buffers
575 : } // boost
576 :
577 : #endif
|