TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
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_DETAIL_SLICE_OF_HPP
11 : #define BOOST_CAPY_DETAIL_SLICE_OF_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/buffers.hpp>
15 :
16 : #include <cstddef>
17 : #include <iterator>
18 : #include <limits>
19 : #include <type_traits>
20 :
21 : namespace boost {
22 : namespace capy {
23 : namespace detail {
24 :
25 : /** A borrowed view over a byte sub-range of a buffer sequence.
26 :
27 : `slice_of<BS>` is the generic result of `buffer_slice` for a sequence
28 : that is not closed under sub-ranging (everything except a single
29 : buffer). It models the same buffer-sequence concept as `BS`:
30 : `MutableBufferSequence` if `BS` is mutable, otherwise
31 : `ConstBufferSequence`. It can therefore be passed anywhere a buffer
32 : sequence is expected.
33 :
34 : It stores iterators into the underlying sequence plus front/back byte
35 : offsets; it neither owns nor copies the descriptors. The underlying
36 : sequence must outlive the view.
37 :
38 : @par Complexity
39 : Construction is a single forward pass to the cut points. It is
40 : O(buffers up to `offset`) for a to-end slice, and O(buffers up to
41 : `offset + length`) for a bounded slice. It never sums the whole
42 : sequence.
43 : */
44 : template<class BS>
45 : requires MutableBufferSequence<BS> || ConstBufferSequence<BS>
46 : class slice_of
47 : {
48 : public:
49 : /// The buffer type yielded by iteration.
50 : using buffer_type = capy::buffer_type<BS>;
51 :
52 : /// The underlying sequence's iterator type.
53 : using iterator_type =
54 : decltype(capy::begin(std::declval<BS const&>()));
55 :
56 : private:
57 : iterator_type first_{};
58 : iterator_type last_{};
59 : std::size_t front_skip_ = 0; // bytes trimmed from *first_
60 : std::size_t back_skip_ = 0; // bytes trimmed from the final buffer
61 :
62 : static buffer_type
63 HIT 34185 : adjust(buffer_type const& b,
64 : std::size_t front_n, std::size_t back_n) noexcept
65 : {
66 : if constexpr (std::is_same_v<buffer_type, mutable_buffer>)
67 17023 : return mutable_buffer(
68 17023 : static_cast<char*>(b.data()) + front_n,
69 34046 : b.size() - front_n - back_n);
70 : else
71 17162 : return const_buffer(
72 17162 : static_cast<char const*>(b.data()) + front_n,
73 34324 : b.size() - front_n - back_n);
74 : }
75 :
76 : public:
77 : /// Bidirectional iterator that adjusts the first and last buffers.
78 : class const_iterator
79 : {
80 : iterator_type cur_{};
81 : iterator_type anchor_first_{};
82 : iterator_type anchor_last_{};
83 : std::size_t front_skip_ = 0;
84 : std::size_t back_skip_ = 0;
85 :
86 : public:
87 : using iterator_category = std::bidirectional_iterator_tag;
88 : using value_type = buffer_type;
89 : using difference_type = std::ptrdiff_t;
90 : using pointer = value_type*;
91 : using reference = value_type;
92 :
93 : const_iterator() noexcept = default;
94 :
95 51198 : const_iterator(
96 : iterator_type cur, iterator_type anchor_first,
97 : iterator_type anchor_last, std::size_t front_skip,
98 : std::size_t back_skip) noexcept
99 51198 : : cur_(cur)
100 51198 : , anchor_first_(anchor_first)
101 51198 : , anchor_last_(anchor_last)
102 51198 : , front_skip_(front_skip)
103 51198 : , back_skip_(back_skip)
104 : {
105 51198 : }
106 :
107 59275 : bool operator==(const_iterator const& o) const noexcept
108 : {
109 59275 : return cur_ == o.cur_;
110 : }
111 :
112 59255 : bool operator!=(const_iterator const& o) const noexcept
113 : {
114 59255 : return !(*this == o);
115 : }
116 :
117 34185 : value_type operator*() const noexcept
118 : {
119 34185 : buffer_type buf = *cur_;
120 34185 : auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0;
121 34185 : auto next = cur_;
122 34185 : ++next;
123 34185 : auto const back_n = (next == anchor_last_) ? back_skip_ : 0;
124 34185 : return adjust(buf, front_n, back_n);
125 : }
126 :
127 21985 : const_iterator& operator++() noexcept { ++cur_; return *this; }
128 6450 : const_iterator operator++(int) noexcept
129 6450 : { auto t = *this; ++*this; return t; }
130 12144 : const_iterator& operator--() noexcept { --cur_; return *this; }
131 6072 : const_iterator operator--(int) noexcept
132 6072 : { auto t = *this; --*this; return t; }
133 : };
134 :
135 : /// Construct an empty slice.
136 : slice_of() noexcept = default;
137 :
138 : /** Construct a view of `[offset, offset + length)` bytes of `bs`.
139 :
140 : @param bs The underlying sequence (must outlive the view).
141 : @param offset Bytes skipped from the front. Clamped to the total.
142 : @param length Bytes exposed; the default exposes to the end.
143 : */
144 2293 : slice_of(
145 : BS const& bs,
146 : std::size_t offset,
147 : std::size_t length =
148 : (std::numeric_limits<std::size_t>::max)()) noexcept
149 2293 : {
150 2293 : first_ = capy::begin(bs);
151 2293 : last_ = capy::end(bs);
152 :
153 : // Position first_/front_skip_ at byte `offset` (single forward pass).
154 2293 : std::size_t skip = offset;
155 2961 : while (first_ != last_)
156 : {
157 2856 : std::size_t const sz = buffer_type(*first_).size();
158 2856 : if (skip < sz)
159 : {
160 2188 : front_skip_ = skip;
161 2188 : break;
162 : }
163 668 : skip -= sz;
164 668 : ++first_;
165 : }
166 :
167 2293 : if (first_ == last_)
168 181 : return; // offset at or past the end: empty slice
169 2188 : if (length == (std::numeric_limits<std::size_t>::max)())
170 1043 : return; // to-end: last_ already end(bs), back_skip_ == 0
171 :
172 : // Walk `length` live bytes forward to fix last_/back_skip_.
173 1145 : std::size_t left = length;
174 1145 : auto cursor = first_;
175 1145 : std::size_t cursor_front = front_skip_;
176 1643 : while (cursor != last_ && left > 0)
177 : {
178 1538 : std::size_t const sz = buffer_type(*cursor).size();
179 1538 : std::size_t const avail = sz - cursor_front;
180 1538 : if (left <= avail)
181 : {
182 1040 : back_skip_ = avail - left;
183 1040 : ++cursor;
184 1040 : last_ = cursor;
185 1040 : return;
186 : }
187 498 : left -= avail;
188 498 : ++cursor;
189 498 : cursor_front = 0;
190 : }
191 105 : last_ = cursor;
192 : }
193 :
194 : /** Construct directly from a positioned iterator range.
195 :
196 : Used by `consuming_buffers::data()` to expose its current position
197 : without re-walking from the start.
198 : */
199 362 : slice_of(
200 : iterator_type first, iterator_type last,
201 : std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept
202 362 : : first_(first)
203 362 : , last_(last)
204 362 : , front_skip_(front_skip)
205 362 : , back_skip_(back_skip)
206 : {
207 362 : }
208 :
209 : /// Return an iterator to the first buffer.
210 25589 : const_iterator begin() const noexcept
211 : {
212 : return const_iterator(
213 25589 : first_, first_, last_, front_skip_, back_skip_);
214 : }
215 :
216 : /// Return an iterator past the last buffer.
217 25609 : const_iterator end() const noexcept
218 : {
219 : return const_iterator(
220 25609 : last_, first_, last_, front_skip_, back_skip_);
221 : }
222 : };
223 :
224 : } // namespace detail
225 : } // namespace capy
226 : } // namespace boost
227 :
228 : #endif
|