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