0.00% Lines (0/0) 0.00% Functions (0/0)
TLA Baseline Branch
Line Hits Code Line Hits 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_BUFFERS_BUFFERS_ITERATOR_HPP  
11 - #define BOOST_CAPY_BUFFERS_BUFFERS_ITERATOR_HPP  
12 -  
13 - #include <boost/capy/detail/config.hpp>  
14 - #include <boost/capy/buffers.hpp>  
15 -  
16 - #include <compare>  
17 - #include <cstddef>  
18 - #include <iterator>  
19 - #include <ranges>  
20 - #include <type_traits>  
21 - #include <utility>  
22 -  
23 - namespace boost {  
24 - namespace capy {  
25 -  
26 - namespace detail {  
27 -  
28 - // Walking back from the end needs an end iterator, not a sentinel  
29 - template<class T>  
30 - concept common_buffer_sequence =  
31 - ConstBufferSequence<T> &&  
32 - (ConstBuffer<T> || std::ranges::common_range<T const>);  
33 -  
34 - } // detail  
35 -  
36 - /** A random access iterator over the bytes in a buffer sequence.  
37 -  
38 - A buffer sequence stores its bytes in several separate regions.  
39 - This iterator presents them as one run of bytes, so standard  
40 - algorithms such as `std::search` can scan across the boundaries  
41 - between buffers without first copying the data into contiguous  
42 - storage. Empty buffers in the sequence are skipped.  
43 -  
44 - The iterator dereferences to `ByteType&` for a  
45 - @ref MutableBufferSequence and to `ByteType const&` otherwise.  
46 -  
47 - Moving by `n` bytes walks the underlying buffers, so `operator+=`,  
48 - `operator-=`, `operator+`, `operator-` with an integer, and  
49 - `operator[]` are linear in the number of buffers crossed. Comparison  
50 - and the difference of two iterators are constant time.  
51 -  
52 - @par Preconditions  
53 - The buffer sequence and the memory it refers to outlive every  
54 - iterator obtained from it. Iterators from different sequences are  
55 - not comparable.  
56 -  
57 - @par Example  
58 - @code  
59 - std::array<capy::const_buffer, 2> bufs{  
60 - capy::make_buffer(std::string_view("GET / HTTP/1.1\r")),  
61 - capy::make_buffer(std::string_view("\n\r\nbody")) };  
62 -  
63 - // Find the end of the header, which spans both buffers  
64 - char const crlfcrlf[] = "\r\n\r\n";  
65 - auto it = std::search(  
66 - capy::buffers_begin(bufs), capy::buffers_end(bufs),  
67 - crlfcrlf, crlfcrlf + 4);  
68 - std::size_t header_size = it - capy::buffers_begin(bufs) + 4;  
69 - @endcode  
70 -  
71 - @tparam BufferSequence The buffer sequence type. It must be a single  
72 - buffer, or a range of buffers whose begin and end have the same type.  
73 -  
74 - @tparam ByteType The byte type presented by the iterator. Its size  
75 - must be 1.  
76 -  
77 - @see @ref buffers_begin, @ref buffers_end,  
78 - @ref buffer_sequence_begin  
79 - */  
80 - template<class BufferSequence, class ByteType = char>  
81 - requires detail::common_buffer_sequence<BufferSequence>  
82 - class buffers_iterator  
83 - {  
84 - static_assert(sizeof(ByteType) == 1, "ByteType must be a byte");  
85 -  
86 - using sequence_iterator = decltype(  
87 - buffer_sequence_begin(std::declval<BufferSequence const&>()));  
88 - using buffer_type = capy::buffer_type<BufferSequence>;  
89 - using byte_type = std::conditional_t<  
90 - MutableBufferSequence<BufferSequence>,  
91 - ByteType,  
92 - ByteType const>;  
93 -  
94 - buffer_type current_buffer_{};  
95 - std::size_t current_buffer_position_ = 0;  
96 - sequence_iterator current_{};  
97 - sequence_iterator end_{};  
98 - std::size_t position_ = 0;  
99 -  
100 - // Move to the next non-empty buffer, or to the end of the sequence  
101 - void  
DCB 102 - 202 skip_forward() noexcept  
103 - {  
DCB 104 - 363 while(current_ != end_)  
105 - {  
DCB 106 - 334 current_buffer_ = buffer_type(*current_);  
DCB 107 - 334 if(current_buffer_.size() > 0)  
DCB 108 - 173 return;  
DCB 109 - 161 ++current_;  
110 - }  
DCB 111 - 29 current_buffer_ = {};  
112 - }  
113 -  
114 - // Move to the previous non-empty buffer, positioned past its last byte  
115 - void  
DCB 116 - 155 skip_backward() noexcept  
117 - {  
118 - do  
119 - {  
DCB 120 - 277 --current_;  
DCB 121 - 277 current_buffer_ = buffer_type(*current_);  
122 - }  
DCB 123 - 277 while(current_buffer_.size() == 0);  
DCB 124 - 155 current_buffer_position_ = current_buffer_.size();  
DCB 125 - 155 }  
126 -  
127 - void  
DCB 128 - 318 advance(std::ptrdiff_t n) noexcept  
129 - {  
DCB 130 - 318 if(n >= 0)  
131 - {  
DCB 132 - 177 auto left = static_cast<std::size_t>(n);  
DCB 133 - 349 while(left > 0)  
134 - {  
DCB 135 - 269 std::size_t const avail =  
DCB 136 - 269 current_buffer_.size() - current_buffer_position_;  
DCB 137 - 269 if(left < avail)  
138 - {  
DCB 139 - 97 current_buffer_position_ += left;  
DCB 140 - 97 position_ += left;  
DCB 141 - 97 return;  
142 - }  
DCB 143 - 172 left -= avail;  
DCB 144 - 172 position_ += avail;  
DCB 145 - 172 ++current_;  
DCB 146 - 172 current_buffer_position_ = 0;  
DCB 147 - 172 skip_forward();  
148 - }  
149 - }  
150 - else  
151 - {  
DCB 152 - 141 auto left = static_cast<std::size_t>(-n);  
DCB 153 - 296 while(left > 0)  
154 - {  
DCB 155 - 296 if(left <= current_buffer_position_)  
156 - {  
DCB 157 - 141 current_buffer_position_ -= left;  
DCB 158 - 141 position_ -= left;  
DCB 159 - 141 return;  
160 - }  
DCB 161 - 155 left -= current_buffer_position_;  
DCB 162 - 155 position_ -= current_buffer_position_;  
DCB 163 - 155 skip_backward();  
164 - }  
165 - }  
166 - }  
167 -  
168 - public:  
169 - /// The signed type of the distance between two iterators.  
170 - using difference_type = std::ptrdiff_t;  
171 -  
172 - /// The type of the bytes the iterator visits.  
173 - using value_type = ByteType;  
174 -  
175 - /// The type returned by `operator->`.  
176 - using pointer = byte_type*;  
177 -  
178 - /// The type returned by `operator*`.  
179 - using reference = byte_type&;  
180 -  
181 - /// The iterator category.  
182 - using iterator_category = std::random_access_iterator_tag;  
183 -  
184 - /// Construct a singular iterator.  
DCB 185 - 14 buffers_iterator() = default;  
186 -  
187 - /** Return an iterator to the first byte of a buffer sequence.  
188 -  
189 - @param bs The buffer sequence. It must outlive the iterator.  
190 -  
191 - @return An iterator to the first byte, or the end iterator if  
192 - the sequence holds no bytes.  
193 - */  
194 - static  
195 - buffers_iterator  
DCB 196 - 18 begin(BufferSequence const& bs) noexcept  
197 - {  
DCB 198 - 18 buffers_iterator it;  
DCB 199 - 18 it.current_ = buffer_sequence_begin(bs);  
DCB 200 - 18 it.end_ = buffer_sequence_end(bs);  
DCB 201 - 18 it.skip_forward();  
DCB 202 - 18 return it;  
203 - }  
204 -  
205 - /** Return an iterator past the last byte of a buffer sequence.  
206 -  
207 - @param bs The buffer sequence. It must outlive the iterator.  
208 -  
209 - @return The end iterator.  
210 - */  
211 - static  
212 - buffers_iterator  
DCB 213 - 24 end(BufferSequence const& bs) noexcept  
214 - {  
DCB 215 - 24 buffers_iterator it;  
DCB 216 - 24 it.current_ = buffer_sequence_end(bs);  
DCB 217 - 24 it.end_ = it.current_;  
DCB 218 - 24 it.position_ = buffer_size(bs);  
DCB 219 - 24 return it;  
220 - }  
221 -  
222 - /** Return a reference to the current byte.  
223 -  
224 - @par Preconditions  
225 - The iterator is not the end iterator.  
226 - */  
227 - reference  
DCB 228 - 125 operator*() const noexcept  
229 - {  
230 - return static_cast<pointer>(  
DCB 231 - 125 current_buffer_.data())[current_buffer_position_];  
232 - }  
233 -  
234 - /** Return a pointer to the current byte.  
235 -  
236 - @par Preconditions  
237 - The iterator is not the end iterator.  
238 - */  
239 - pointer  
240 - operator->() const noexcept  
241 - {  
242 - return &**this;  
243 - }  
244 -  
245 - /** Return a reference to the byte `n` positions away.  
246 -  
247 - @par Complexity  
248 - Linear in the number of buffers crossed.  
249 -  
250 - @param n The offset from this iterator, in bytes.  
251 - */  
252 - reference  
DCB 253 - 33 operator[](difference_type n) const noexcept  
254 - {  
DCB 255 - 33 return *(*this + n);  
256 - }  
257 -  
258 - /// Advance to the next byte.  
259 - buffers_iterator&  
DCB 260 - 59 operator++() noexcept  
261 - {  
DCB 262 - 59 ++position_;  
DCB 263 - 59 if(++current_buffer_position_ < current_buffer_.size())  
DCB 264 - 47 return *this;  
DCB 265 - 12 ++current_;  
DCB 266 - 12 current_buffer_position_ = 0;  
DCB 267 - 12 skip_forward();  
DCB 268 - 12 return *this;  
269 - }  
270 -  
271 - /// Advance to the next byte, returning the previous position.  
272 - buffers_iterator  
273 - operator++(int) noexcept  
274 - {  
275 - auto tmp = *this;  
276 - ++*this;  
277 - return tmp;  
278 - }  
279 -  
280 - /// Move back to the previous byte.  
281 - buffers_iterator&  
DCB 282 - 20 operator--() noexcept  
283 - {  
DCB 284 - 20 advance(-1);  
DCB 285 - 20 return *this;  
286 - }  
287 -  
288 - /// Move back to the previous byte, returning the previous position.  
289 - buffers_iterator  
290 - operator--(int) noexcept  
291 - {  
292 - auto tmp = *this;  
293 - --*this;  
294 - return tmp;  
295 - }  
296 -  
297 - /** Move by `n` bytes.  
298 -  
299 - @par Complexity  
300 - Linear in the number of buffers crossed.  
301 -  
302 - @param n The number of bytes to move; negative moves backward.  
303 -  
304 - @return A reference to `*this`.  
305 - */  
306 - buffers_iterator&  
DCB 307 - 166 operator+=(difference_type n) noexcept  
308 - {  
DCB 309 - 166 advance(n);  
DCB 310 - 166 return *this;  
311 - }  
312 -  
313 - /** Move back by `n` bytes.  
314 -  
315 - @par Complexity  
316 - Linear in the number of buffers crossed.  
317 -  
318 - @param n The number of bytes to move back.  
319 -  
320 - @return A reference to `*this`.  
321 - */  
322 - buffers_iterator&  
DCB 323 - 132 operator-=(difference_type n) noexcept  
324 - {  
DCB 325 - 132 advance(-n);  
DCB 326 - 132 return *this;  
327 - }  
328 -  
329 - /// Return an iterator `n` bytes after `it`.  
330 - friend  
331 - buffers_iterator  
DCB 332 - 166 operator+(buffers_iterator it, difference_type n) noexcept  
333 - {  
DCB 334 - 166 it += n;  
DCB 335 - 166 return it;  
336 - }  
337 -  
338 - /// Return an iterator `n` bytes after `it`.  
339 - friend  
340 - buffers_iterator  
341 - operator+(difference_type n, buffers_iterator it) noexcept  
342 - {  
343 - it += n;  
344 - return it;  
345 - }  
346 -  
347 - /// Return an iterator `n` bytes before `it`.  
348 - friend  
349 - buffers_iterator  
DCB 350 - 11 operator-(buffers_iterator it, difference_type n) noexcept  
351 - {  
DCB 352 - 11 it -= n;  
DCB 353 - 11 return it;  
354 - }  
355 -  
356 - /// Return the number of bytes from `b` to `a`.  
357 - friend  
358 - difference_type  
DCB 359 - 264 operator-(  
360 - buffers_iterator const& a,  
361 - buffers_iterator const& b) noexcept  
362 - {  
DCB 363 - 264 return static_cast<difference_type>(a.position_) -  
DCB 364 - 264 static_cast<difference_type>(b.position_);  
365 - }  
366 -  
367 - /// Test whether two iterators refer to the same byte.  
368 - friend  
369 - bool  
DCB 370 - 312 operator==(  
371 - buffers_iterator const& a,  
372 - buffers_iterator const& b) noexcept  
373 - {  
DCB 374 - 312 return a.position_ == b.position_;  
375 - }  
376 -  
377 - /// Order two iterators by byte position.  
378 - friend  
379 - std::strong_ordering  
DCB 380 - 121 operator<=>(  
381 - buffers_iterator const& a,  
382 - buffers_iterator const& b) noexcept  
383 - {  
DCB 384 - 121 return a.position_ <=> b.position_;  
385 - }  
386 - };  
387 -  
388 - /** Return an iterator to the first byte of a buffer sequence.  
389 -  
390 - @param bs The buffer sequence. It must outlive the iterator.  
391 -  
392 - @return `buffers_iterator<BufferSequence>::begin(bs)`.  
393 -  
394 - @see @ref buffers_iterator, @ref buffers_end  
395 - */  
396 - template<detail::common_buffer_sequence BufferSequence>  
397 - buffers_iterator<BufferSequence>  
DCB 398 - 17 buffers_begin(BufferSequence const& bs) noexcept  
399 - {  
DCB 400 - 17 return buffers_iterator<BufferSequence>::begin(bs);  
401 - }  
402 -  
403 - /// Rejects a temporary sequence, since the iterator would dangle.  
404 - template<detail::common_buffer_sequence BufferSequence>  
405 - buffers_iterator<BufferSequence>  
406 - buffers_begin(BufferSequence const&& bs) = delete;  
407 -  
408 - /** Return an iterator past the last byte of a buffer sequence.  
409 -  
410 - @param bs The buffer sequence. It must outlive the iterator.  
411 -  
412 - @return `buffers_iterator<BufferSequence>::end(bs)`.  
413 -  
414 - @see @ref buffers_iterator, @ref buffers_begin  
415 - */  
416 - template<detail::common_buffer_sequence BufferSequence>  
417 - buffers_iterator<BufferSequence>  
DCB 418 - 24 buffers_end(BufferSequence const& bs) noexcept  
419 - {  
DCB 420 - 24 return buffers_iterator<BufferSequence>::end(bs);  
421 - }  
422 -  
423 - /// Rejects a temporary sequence, since the iterator would dangle.  
424 - template<detail::common_buffer_sequence BufferSequence>  
425 - buffers_iterator<BufferSequence>  
426 - buffers_end(BufferSequence const&& bs) = delete;  
427 -  
428 - } // capy  
429 - } // boost  
430 -  
431 - #endif