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 : BufferSequence bs_;
68 : iter_type begin_;
69 : iter_type end_;
70 : std::size_t len_ = 0; // length of bs_
71 : std::size_t size_ = 0; // total bytes
72 : std::size_t prefix_ = 0; // used prefix bytes
73 : std::size_t suffix_ = 0; // used suffix bytes
74 :
75 : public:
76 : /** The type of values returned by iterators
77 : */
78 : using value_type = typename std::conditional<
79 : is_mutable_buffer_sequence<BufferSequence>::value,
80 : mutable_buffer, const_buffer>::type;
81 :
82 : /** The type of returned iterators
83 : */
84 : class const_iterator;
85 :
86 : /** Constructor
87 : */
88 : slice_of() = default;
89 :
90 : /** Constructor
91 : */
92 278721 : slice_of(
93 : BufferSequence const& bs)
94 278721 : : bs_(bs)
95 278721 : , begin_(buffers::begin(bs_))
96 278721 : , end_(buffers::end(bs_))
97 : {
98 278721 : auto it = begin_;
99 836627 : while(it != end_)
100 : {
101 557906 : value_type b(*it);
102 557906 : size_ += b.size();
103 557906 : ++len_;
104 557906 : ++it;
105 : }
106 278721 : }
107 :
108 : /** Return an iterator to the beginning of the sequence
109 : */
110 : const_iterator
111 : begin() const noexcept;
112 :
113 : /** Return an iterator to the end of the sequence
114 : */
115 : const_iterator
116 : end() const noexcept;
117 :
118 : friend
119 : void
120 266423 : tag_invoke(
121 : slice_tag const&,
122 : slice_of<BufferSequence>& bs,
123 : slice_how how,
124 : std::size_t n)
125 : {
126 266423 : bs.slice_impl(how, n);
127 266423 : }
128 :
129 : private:
130 : void
131 131154 : remove_prefix_impl(
132 : std::size_t n)
133 : {
134 : // nice hack to simplify the loop (M. Nejati)
135 131154 : n += prefix_;
136 131154 : size_ += prefix_;
137 131154 : prefix_ = 0;
138 :
139 200953 : while(n > 0 && begin_ != end_)
140 : {
141 178273 : value_type b = *begin_;
142 178273 : if(n < b.size())
143 : {
144 108474 : prefix_ = n;
145 108474 : size_ -= n;
146 108474 : break;
147 : }
148 69799 : n -= b.size();
149 69799 : size_ -= b.size();
150 69799 : ++begin_;
151 69799 : --len_;
152 : }
153 131154 : }
154 :
155 : void
156 114759 : remove_suffix_impl(
157 : std::size_t n)
158 : {
159 114759 : if(size_ == 0)
160 : {
161 0 : BOOST_ASSERT(begin_ == end_);
162 114759 : return;
163 : }
164 114759 : BOOST_ASSERT(begin_ != end_);
165 114759 : n += suffix_;
166 114759 : size_ += suffix_;
167 114759 : suffix_ = 0;
168 114759 : iter_type it = end_;
169 114759 : --it;
170 188774 : while(it != begin_)
171 : {
172 114829 : value_type b = *it;
173 114829 : if(n < b.size())
174 : {
175 40814 : suffix_ = n;
176 40814 : size_ -= n;
177 40814 : return;
178 : }
179 74015 : n -= b.size();
180 74015 : size_ -= b.size();
181 74015 : --it;
182 74015 : --end_;
183 74015 : --len_;
184 : }
185 73945 : value_type b = *it;
186 73945 : auto m = b.size() - prefix_;
187 73945 : if(n < m)
188 : {
189 73945 : suffix_ = n;
190 73945 : size_ -= n;
191 73945 : return;
192 : }
193 0 : end_ = begin_;
194 0 : len_ = 0;
195 : }
196 :
197 : void
198 135269 : keep_prefix_impl(
199 : std::size_t n)
200 : {
201 135269 : if(n >= size_)
202 8213 : return;
203 127056 : if(n == 0)
204 : {
205 12297 : end_ = begin_;
206 12297 : len_ = 0;
207 12297 : size_ = 0;
208 12297 : return;
209 : }
210 114759 : remove_suffix_impl(size_ - n);
211 : }
212 :
213 : void
214 : keep_suffix_impl(
215 : std::size_t n)
216 : {
217 : if(n >= size_)
218 : return;
219 : if(n == 0)
220 : {
221 : begin_ = end_;
222 : len_ = 0;
223 : size_ = 0;
224 : return;
225 : }
226 : remove_prefix_impl(size_ - n);
227 : }
228 :
229 : void
230 266423 : slice_impl(
231 : slice_how how,
232 : std::size_t n)
233 : {
234 266423 : switch(how)
235 : {
236 131154 : case slice_how::remove_prefix:
237 : {
238 131154 : remove_prefix_impl(n);
239 131154 : break;
240 : }
241 135269 : case slice_how::keep_prefix:
242 : {
243 135269 : keep_prefix_impl(n);
244 135269 : break;
245 : }
246 : }
247 266423 : }
248 : };
249 :
250 : //------------------------------------------------
251 :
252 : template<class BufferSequence>
253 : class slice_of<BufferSequence>::
254 : const_iterator
255 : {
256 : using iter_type = typename
257 : slice_of::iter_type;
258 :
259 : iter_type it_;
260 : // VFALCO we could just point back to
261 : // the original sequence to save size
262 : std::size_t prefix_ = 0;
263 : std::size_t suffix_ = 0;
264 : std::size_t i_ = 0;
265 : std::size_t n_ = 0;
266 :
267 : friend class slice_of<BufferSequence>;
268 :
269 6410228 : const_iterator(
270 : iter_type it,
271 : std::size_t prefix__,
272 : std::size_t suffix__,
273 : std::size_t i,
274 : std::size_t n) noexcept
275 6410228 : : it_(it)
276 6410228 : , prefix_(prefix__)
277 6410228 : , suffix_(suffix__)
278 6410228 : , i_(i)
279 6410228 : , n_(n)
280 : {
281 : // n_ is the index of the end iterator
282 6410228 : }
283 :
284 : public:
285 : using value_type = typename slice_of::value_type;
286 : using reference = value_type;
287 : using pointer = void;
288 : using difference_type = std::ptrdiff_t;
289 : using iterator_category =
290 : std::bidirectional_iterator_tag;
291 : #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
292 : using iterator_concept = std::bidirectional_iterator_tag; // (since C++20)
293 : #endif
294 :
295 : const_iterator() = default;
296 :
297 : bool
298 7765254 : operator==(
299 : const_iterator const& other) const noexcept
300 : {
301 : return
302 10970367 : it_ == other.it_ &&
303 3205113 : prefix_ == other.prefix_ &&
304 3205113 : suffix_ == other.suffix_ &&
305 14175480 : i_ == other.i_ &&
306 10970367 : n_ == other.n_;
307 : }
308 :
309 : bool
310 7765254 : operator!=(
311 : const_iterator const& other) const noexcept
312 : {
313 7765254 : return !(*this == other);
314 : }
315 :
316 : reference
317 4560141 : operator*() const noexcept
318 : {
319 4560141 : value_type v = *it_;
320 : using P = typename std::conditional<
321 : is_mutable_buffer_sequence<BufferSequence>::value,
322 : char*, char const*>::type;
323 4560141 : auto p = reinterpret_cast<P>(v.data());
324 4560141 : auto n = v.size();
325 4560141 : if(i_ == 0)
326 : {
327 2955699 : p += prefix_;
328 2955699 : n -= prefix_;
329 : }
330 4560141 : if(i_ == n_ - 1)
331 2955699 : n -= suffix_;
332 4560141 : return value_type(p, n);
333 : }
334 :
335 : const_iterator&
336 3003398 : operator++() noexcept
337 : {
338 3003398 : BOOST_ASSERT(i_ < n_);
339 3003398 : ++it_;
340 3003398 : ++i_;
341 3003398 : return *this;
342 : }
343 :
344 : const_iterator
345 778372 : operator++(int) noexcept
346 : {
347 778372 : auto temp = *this;
348 778372 : ++(*this);
349 778372 : return temp;
350 : }
351 :
352 : const_iterator&
353 1556744 : operator--() noexcept
354 : {
355 1556744 : BOOST_ASSERT(i_ > 0);
356 1556744 : --it_;
357 1556744 : --i_;
358 1556744 : return *this;
359 : }
360 :
361 : const_iterator
362 778372 : operator--(int) noexcept
363 : {
364 778372 : auto temp = *this;
365 778372 : --(*this);
366 778372 : return temp;
367 : }
368 : };
369 :
370 : //------------------------------------------------
371 :
372 : template<class BufferSequence>
373 : auto
374 3205115 : slice_of<BufferSequence>::
375 : begin() const noexcept ->
376 : const_iterator
377 : {
378 : return const_iterator(
379 3205115 : this->begin_, prefix_, suffix_, 0, len_);
380 : }
381 :
382 : template<class BufferSequence>
383 : auto
384 3205113 : slice_of<BufferSequence>::
385 : end() const noexcept ->
386 : const_iterator
387 : {
388 : return const_iterator(
389 3205113 : this->end_, prefix_, suffix_, len_, len_);
390 : }
391 :
392 : //------------------------------------------------
393 :
394 : // in-place modify return value
395 : // -----------------------------
396 : // keep_prefix* prefix
397 : // keep_suffix suffix
398 : // remove_prefix* sans_prefix
399 : // remove_suffix sans_suffix
400 :
401 : /** Remove all but the first `n` bytes from a buffer sequence
402 : */
403 : constexpr struct keep_prefix_mrdocs_workaround_t
404 : {
405 : template<class BufferSequence>
406 276140 : auto operator()(
407 : BufferSequence& bs,
408 : std::size_t n) const -> typename std::enable_if<
409 : is_const_buffer_sequence<BufferSequence>::value &&
410 : detail::has_tag_invoke<BufferSequence>::value>::type
411 :
412 : {
413 276140 : tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
414 276140 : }
415 : } const keep_prefix{};
416 :
417 : /** Remove all but the last `n` bytes from a buffer sequence
418 : */
419 : constexpr struct keep_suffix_mrdocs_workaround_t
420 : {
421 : template<class BufferSequence>
422 139852 : auto operator()(
423 : BufferSequence& bs,
424 : std::size_t n) const -> typename std::enable_if<
425 : is_const_buffer_sequence<BufferSequence>::value &&
426 : detail::has_tag_invoke<BufferSequence>::value>::type
427 : {
428 139852 : auto n0 = size(bs);
429 139852 : if(n < n0)
430 123398 : tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
431 139852 : }
432 : } const keep_suffix{};
433 :
434 : /** Remove `n` bytes from the beginning of a buffer sequence
435 : */
436 : constexpr struct remove_prefix_mrdocs_workaround_t
437 : {
438 : template<class BufferSequence>
439 272017 : auto operator()(
440 : BufferSequence& bs,
441 : std::size_t n) const -> typename std::enable_if<
442 : is_const_buffer_sequence<BufferSequence>::value &&
443 : detail::has_tag_invoke<BufferSequence>::value>::type
444 : {
445 272017 : tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
446 272017 : }
447 : } const remove_prefix{};
448 :
449 : /** Remove `n` bytes from the end of a buffer sequence
450 : */
451 : constexpr struct remove_suffix_mrdocs_workaround_t
452 : {
453 : template<class BufferSequence>
454 139852 : auto operator()(
455 : BufferSequence& bs,
456 : std::size_t n) const -> typename std::enable_if<
457 : is_const_buffer_sequence<BufferSequence>::value &&
458 : detail::has_tag_invoke<BufferSequence>::value>::type
459 : {
460 139852 : auto n0 = size(bs);
461 139852 : if(n > 0)
462 : {
463 131625 : if( n > n0)
464 8227 : n = n0;
465 131625 : tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
466 : }
467 139852 : }
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<class BufferSequence>
477 54 : auto operator()(
478 : BufferSequence const& bs,
479 : std::size_t n) const noexcept -> typename std::enable_if<
480 : is_const_buffer_sequence<BufferSequence>::value,
481 : slice_type<BufferSequence>>::type
482 : {
483 54 : slice_type<BufferSequence> result(bs);
484 54 : keep_prefix(result, n);
485 54 : return result;
486 : }
487 : } prefix{};
488 :
489 : /** Return a sequence representing the last `n` bytes of a buffer sequence
490 : */
491 : constexpr struct suffix_mrdocs_workaround_t
492 : {
493 : template<class BufferSequence>
494 : auto operator()(
495 : BufferSequence const& bs,
496 : std::size_t n) const noexcept -> typename std::enable_if<
497 : is_const_buffer_sequence<BufferSequence>::value,
498 : slice_type<BufferSequence>>::type
499 : {
500 : slice_type<BufferSequence> result(bs);
501 : keep_suffix(result, n);
502 : return result;
503 : }
504 : } suffix{};
505 :
506 : /** Return a sequence representing all but the first `n` bytes of a buffer sequence
507 : */
508 : constexpr struct sans_prefix_mrdocs_workaround_t
509 : {
510 : template<class BufferSequence>
511 69 : 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 69 : slice_type<BufferSequence> result(bs);
518 69 : remove_prefix(result, n);
519 69 : return result;
520 : }
521 : } sans_prefix{};
522 :
523 : /** Return a sequence representing all but the last `n` bytes of a buffer sequence
524 : */
525 : constexpr struct sans_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 : remove_suffix(result, n);
536 : return result;
537 : }
538 : } sans_suffix{};
539 :
540 : } // buffers
541 : } // boost
542 :
543 : #endif
|