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`), so it can be passed anywhere a buffer sequence
32 : 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: O(buffers up
40 : to `offset`) for a to-end slice, O(buffers up to `offset + length`)
41 : for a bounded slice. It never sums the whole sequence.
42 : */
43 : template<class BS>
44 : requires MutableBufferSequence<BS> || ConstBufferSequence<BS>
45 : class slice_of
46 : {
47 : public:
48 : /// The buffer type yielded by iteration.
49 : using buffer_type = capy::buffer_type<BS>;
50 :
51 : /// The underlying sequence's iterator type.
52 : using iterator_type =
53 : decltype(capy::begin(std::declval<BS const&>()));
54 :
55 : private:
56 : iterator_type first_{};
57 : iterator_type last_{};
58 : std::size_t front_skip_ = 0; // bytes trimmed from *first_
59 : std::size_t back_skip_ = 0; // bytes trimmed from the final buffer
60 :
61 : static buffer_type
62 HIT 34112 : adjust(buffer_type const& b,
63 : std::size_t front_n, std::size_t back_n) noexcept
64 : {
65 : if constexpr (std::is_same_v<buffer_type, mutable_buffer>)
66 17006 : return mutable_buffer(
67 17006 : static_cast<char*>(b.data()) + front_n,
68 34012 : b.size() - front_n - back_n);
69 : else
70 17106 : return const_buffer(
71 17106 : static_cast<char const*>(b.data()) + front_n,
72 34212 : b.size() - front_n - back_n);
73 : }
74 :
75 : public:
76 : /// Bidirectional iterator that adjusts the first and last buffers.
77 : class const_iterator
78 : {
79 : iterator_type cur_{};
80 : iterator_type anchor_first_{};
81 : iterator_type anchor_last_{};
82 : std::size_t front_skip_ = 0;
83 : std::size_t back_skip_ = 0;
84 :
85 : public:
86 : using iterator_category = std::bidirectional_iterator_tag;
87 : using value_type = buffer_type;
88 : using difference_type = std::ptrdiff_t;
89 : using pointer = value_type*;
90 : using reference = value_type;
91 :
92 : const_iterator() noexcept = default;
93 :
94 51088 : const_iterator(
95 : iterator_type cur, iterator_type anchor_first,
96 : iterator_type anchor_last, std::size_t front_skip,
97 : std::size_t back_skip) noexcept
98 51088 : : cur_(cur)
99 51088 : , anchor_first_(anchor_first)
100 51088 : , anchor_last_(anchor_last)
101 51088 : , front_skip_(front_skip)
102 51088 : , back_skip_(back_skip)
103 : {
104 51088 : }
105 :
106 59163 : bool operator==(const_iterator const& o) const noexcept
107 : {
108 59163 : return cur_ == o.cur_;
109 : }
110 :
111 59163 : bool operator!=(const_iterator const& o) const noexcept
112 : {
113 59163 : return !(*this == o);
114 : }
115 :
116 34112 : value_type operator*() const noexcept
117 : {
118 34112 : buffer_type buf = *cur_;
119 34112 : auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0;
120 34112 : auto next = cur_;
121 34112 : ++next;
122 34112 : auto const back_n = (next == anchor_last_) ? back_skip_ : 0;
123 34112 : return adjust(buf, front_n, back_n);
124 : }
125 :
126 21911 : const_iterator& operator++() noexcept { ++cur_; return *this; }
127 6436 : const_iterator operator++(int) noexcept
128 6436 : { auto t = *this; ++*this; return t; }
129 12144 : const_iterator& operator--() noexcept { --cur_; return *this; }
130 6072 : const_iterator operator--(int) noexcept
131 6072 : { auto t = *this; --*this; return t; }
132 : };
133 :
134 : /// Construct an empty slice.
135 : slice_of() noexcept = default;
136 :
137 : /** Construct a view of `[offset, offset + length)` bytes of `bs`.
138 :
139 : @param bs The underlying sequence (must outlive the view).
140 : @param offset Bytes skipped from the front. Clamped to the total.
141 : @param length Bytes exposed; the default exposes to the end.
142 : */
143 2291 : slice_of(
144 : BS const& bs,
145 : std::size_t offset,
146 : std::size_t length =
147 : (std::numeric_limits<std::size_t>::max)()) noexcept
148 2291 : {
149 2291 : first_ = capy::begin(bs);
150 2291 : last_ = capy::end(bs);
151 :
152 : // Position first_/front_skip_ at byte `offset` (single forward pass).
153 2291 : std::size_t skip = offset;
154 2958 : while (first_ != last_)
155 : {
156 2853 : std::size_t const sz = buffer_type(*first_).size();
157 2853 : if (skip < sz)
158 : {
159 2186 : front_skip_ = skip;
160 2186 : break;
161 : }
162 667 : skip -= sz;
163 667 : ++first_;
164 : }
165 :
166 2291 : if (first_ == last_)
167 181 : return; // offset at or past the end: empty slice
168 2186 : if (length == (std::numeric_limits<std::size_t>::max)())
169 1042 : return; // to-end: last_ already end(bs), back_skip_ == 0
170 :
171 : // Walk `length` live bytes forward to fix last_/back_skip_.
172 1144 : std::size_t left = length;
173 1144 : auto cursor = first_;
174 1144 : std::size_t cursor_front = front_skip_;
175 1641 : while (cursor != last_ && left > 0)
176 : {
177 1536 : std::size_t const sz = buffer_type(*cursor).size();
178 1536 : std::size_t const avail = sz - cursor_front;
179 1536 : if (left <= avail)
180 : {
181 1039 : back_skip_ = avail - left;
182 1039 : ++cursor;
183 1039 : last_ = cursor;
184 1039 : return;
185 : }
186 497 : left -= avail;
187 497 : ++cursor;
188 497 : cursor_front = 0;
189 : }
190 105 : last_ = cursor;
191 : }
192 :
193 : /** Construct directly from a positioned iterator range.
194 :
195 : Used by `consuming_buffers::data()` to expose its current position
196 : without re-walking from the start.
197 : */
198 335 : slice_of(
199 : iterator_type first, iterator_type last,
200 : std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept
201 335 : : first_(first)
202 335 : , last_(last)
203 335 : , front_skip_(front_skip)
204 335 : , back_skip_(back_skip)
205 : {
206 335 : }
207 :
208 : /// Return an iterator to the first buffer.
209 25534 : const_iterator begin() const noexcept
210 : {
211 : return const_iterator(
212 25534 : first_, first_, last_, front_skip_, back_skip_);
213 : }
214 :
215 : /// Return an iterator past the last buffer.
216 25554 : const_iterator end() const noexcept
217 : {
218 : return const_iterator(
219 25554 : last_, first_, last_, front_skip_, back_skip_);
220 : }
221 : };
222 :
223 : } // namespace detail
224 : } // namespace capy
225 : } // namespace boost
226 :
227 : #endif
|