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