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 view `consuming_buffers::data()` returns. It
28 : models the same buffer-sequence concept as `BS`:
29 : `MutableBufferSequence` if `BS` is mutable, otherwise
30 : `ConstBufferSequence`. It can therefore be passed anywhere a buffer
31 : sequence is expected.
32 :
33 : It stores iterators into the underlying sequence plus front/back byte
34 : offsets; it neither owns nor copies the descriptors. The underlying
35 : sequence must outlive the view.
36 :
37 : @par Complexity
38 : Construction is a single forward pass to the cut points. It is
39 : O(buffers up to `offset`) for a to-end slice, and O(buffers up to
40 : `offset + length`) for a bounded slice. It never sums the whole
41 : sequence.
42 : */
43 : template<class BS>
44 : requires 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::buffer_sequence_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 34135 : 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 17004 : return mutable_buffer(
67 17004 : static_cast<char*>(b.data()) + front_n,
68 34008 : b.size() - front_n - back_n);
69 : else
70 17131 : return const_buffer(
71 17131 : static_cast<char const*>(b.data()) + front_n,
72 34262 : 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 51125 : 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 51125 : : cur_(cur)
99 51125 : , anchor_first_(anchor_first)
100 51125 : , anchor_last_(anchor_last)
101 51125 : , front_skip_(front_skip)
102 51125 : , back_skip_(back_skip)
103 : {
104 51125 : }
105 :
106 59189 : bool operator==(const_iterator const& o) const noexcept
107 : {
108 59189 : return cur_ == o.cur_;
109 : }
110 :
111 59189 : bool operator!=(const_iterator const& o) const noexcept
112 : {
113 59189 : return !(*this == o);
114 : }
115 :
116 34135 : value_type operator*() const noexcept
117 : {
118 34135 : buffer_type buf = *cur_;
119 34135 : auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0;
120 34135 : auto next = cur_;
121 34135 : ++next;
122 34135 : auto const back_n = (next == anchor_last_) ? back_skip_ : 0;
123 34135 : return adjust(buf, front_n, back_n);
124 : }
125 :
126 21929 : const_iterator& operator++() noexcept { ++cur_; return *this; }
127 6446 : const_iterator operator++(int) noexcept
128 6446 : { 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 2285 : 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 2285 : {
149 2285 : first_ = capy::buffer_sequence_begin(bs);
150 2285 : last_ = capy::buffer_sequence_end(bs);
151 :
152 : // Position first_/front_skip_ at byte `offset` (single forward pass).
153 2285 : std::size_t skip = offset;
154 2950 : while (first_ != last_)
155 : {
156 2846 : std::size_t const sz = buffer_type(*first_).size();
157 2846 : if (skip < sz)
158 : {
159 2181 : front_skip_ = skip;
160 2181 : break;
161 : }
162 665 : skip -= sz;
163 665 : ++first_;
164 : }
165 :
166 2285 : if (first_ == last_)
167 180 : return; // offset at or past the end: empty slice
168 2181 : if (length == (std::numeric_limits<std::size_t>::max)())
169 1039 : return; // to-end: last_ already at the end, back_skip_ == 0
170 :
171 : // Walk `length` live bytes forward to fix last_/back_skip_.
172 1142 : std::size_t left = length;
173 1142 : auto cursor = first_;
174 1142 : std::size_t cursor_front = front_skip_;
175 1637 : while (cursor != last_ && left > 0)
176 : {
177 1533 : std::size_t const sz = buffer_type(*cursor).size();
178 1533 : std::size_t const avail = sz - cursor_front;
179 1533 : if (left <= avail)
180 : {
181 1038 : back_skip_ = avail - left;
182 1038 : ++cursor;
183 1038 : last_ = cursor;
184 1038 : return;
185 : }
186 495 : left -= avail;
187 495 : ++cursor;
188 495 : cursor_front = 0;
189 : }
190 104 : 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 354 : slice_of(
199 : iterator_type first, iterator_type last,
200 : std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept
201 354 : : first_(first)
202 354 : , last_(last)
203 354 : , front_skip_(front_skip)
204 354 : , back_skip_(back_skip)
205 : {
206 354 : }
207 :
208 : /// Return an iterator to the first buffer.
209 25555 : const_iterator begin() const noexcept
210 : {
211 : return const_iterator(
212 25555 : first_, first_, last_, front_skip_, back_skip_);
213 : }
214 :
215 : /// Return an iterator past the last buffer.
216 25570 : const_iterator end() const noexcept
217 : {
218 : return const_iterator(
219 25570 : last_, first_, last_, front_skip_, back_skip_);
220 : }
221 : };
222 :
223 : } // namespace detail
224 : } // namespace capy
225 : } // namespace boost
226 :
227 : #endif
|