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 | ||||||