LCOV - code coverage report
Current view: top level - capy/buffers - buffers_iterator.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 88 88
Test Date: 2026-10-30 18:56:53 Functions: 96.6 % 87 84 3

           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_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
     102 HIT         202 :     skip_forward() noexcept
     103                 :     {
     104             363 :         while(current_ != end_)
     105                 :         {
     106             334 :             current_buffer_ = buffer_type(*current_);
     107             334 :             if(current_buffer_.size() > 0)
     108             173 :                 return;
     109             161 :             ++current_;
     110                 :         }
     111              29 :         current_buffer_ = {};
     112                 :     }
     113                 : 
     114                 :     // Move to the previous non-empty buffer, positioned past its last byte
     115                 :     void
     116             155 :     skip_backward() noexcept
     117                 :     {
     118                 :         do
     119                 :         {
     120             277 :             --current_;
     121             277 :             current_buffer_ = buffer_type(*current_);
     122                 :         }
     123             277 :         while(current_buffer_.size() == 0);
     124             155 :         current_buffer_position_ = current_buffer_.size();
     125             155 :     }
     126                 : 
     127                 :     void
     128             318 :     advance(std::ptrdiff_t n) noexcept
     129                 :     {
     130             318 :         if(n >= 0)
     131                 :         {
     132             177 :             auto left = static_cast<std::size_t>(n);
     133             349 :             while(left > 0)
     134                 :             {
     135             269 :                 std::size_t const avail =
     136             269 :                     current_buffer_.size() - current_buffer_position_;
     137             269 :                 if(left < avail)
     138                 :                 {
     139              97 :                     current_buffer_position_ += left;
     140              97 :                     position_ += left;
     141              97 :                     return;
     142                 :                 }
     143             172 :                 left -= avail;
     144             172 :                 position_ += avail;
     145             172 :                 ++current_;
     146             172 :                 current_buffer_position_ = 0;
     147             172 :                 skip_forward();
     148                 :             }
     149                 :         }
     150                 :         else
     151                 :         {
     152             141 :             auto left = static_cast<std::size_t>(-n);
     153             296 :             while(left > 0)
     154                 :             {
     155             296 :                 if(left <= current_buffer_position_)
     156                 :                 {
     157             141 :                     current_buffer_position_ -= left;
     158             141 :                     position_ -= left;
     159             141 :                     return;
     160                 :                 }
     161             155 :                 left -= current_buffer_position_;
     162             155 :                 position_ -= current_buffer_position_;
     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.
     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
     196              18 :     begin(BufferSequence const& bs) noexcept
     197                 :     {
     198              18 :         buffers_iterator it;
     199              18 :         it.current_ = buffer_sequence_begin(bs);
     200              18 :         it.end_ = buffer_sequence_end(bs);
     201              18 :         it.skip_forward();
     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
     213              24 :     end(BufferSequence const& bs) noexcept
     214                 :     {
     215              24 :         buffers_iterator it;
     216              24 :         it.current_ = buffer_sequence_end(bs);
     217              24 :         it.end_ = it.current_;
     218              24 :         it.position_ = buffer_size(bs);
     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
     228             125 :     operator*() const noexcept
     229                 :     {
     230                 :         return static_cast<pointer>(
     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
     253              33 :     operator[](difference_type n) const noexcept
     254                 :     {
     255              33 :         return *(*this + n);
     256                 :     }
     257                 : 
     258                 :     /// Advance to the next byte.
     259                 :     buffers_iterator&
     260              59 :     operator++() noexcept
     261                 :     {
     262              59 :         ++position_;
     263              59 :         if(++current_buffer_position_ < current_buffer_.size())
     264              47 :             return *this;
     265              12 :         ++current_;
     266              12 :         current_buffer_position_ = 0;
     267              12 :         skip_forward();
     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&
     282              20 :     operator--() noexcept
     283                 :     {
     284              20 :         advance(-1);
     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&
     307             166 :     operator+=(difference_type n) noexcept
     308                 :     {
     309             166 :         advance(n);
     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&
     323             132 :     operator-=(difference_type n) noexcept
     324                 :     {
     325             132 :         advance(-n);
     326             132 :         return *this;
     327                 :     }
     328                 : 
     329                 :     /// Return an iterator `n` bytes after `it`.
     330                 :     friend
     331                 :     buffers_iterator
     332             166 :     operator+(buffers_iterator it, difference_type n) noexcept
     333                 :     {
     334             166 :         it += n;
     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
     350              11 :     operator-(buffers_iterator it, difference_type n) noexcept
     351                 :     {
     352              11 :         it -= n;
     353              11 :         return it;
     354                 :     }
     355                 : 
     356                 :     /// Return the number of bytes from `b` to `a`.
     357                 :     friend
     358                 :     difference_type
     359             264 :     operator-(
     360                 :         buffers_iterator const& a,
     361                 :         buffers_iterator const& b) noexcept
     362                 :     {
     363             264 :         return static_cast<difference_type>(a.position_) -
     364             264 :             static_cast<difference_type>(b.position_);
     365                 :     }
     366                 : 
     367                 :     /// Test whether two iterators refer to the same byte.
     368                 :     friend
     369                 :     bool
     370             312 :     operator==(
     371                 :         buffers_iterator const& a,
     372                 :         buffers_iterator const& b) noexcept
     373                 :     {
     374             312 :         return a.position_ == b.position_;
     375                 :     }
     376                 : 
     377                 :     /// Order two iterators by byte position.
     378                 :     friend
     379                 :     std::strong_ordering
     380             121 :     operator<=>(
     381                 :         buffers_iterator const& a,
     382                 :         buffers_iterator const& b) noexcept
     383                 :     {
     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>
     398              17 : buffers_begin(BufferSequence const& bs) noexcept
     399                 : {
     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>
     418              24 : buffers_end(BufferSequence const& bs) noexcept
     419                 : {
     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
        

Generated by: LCOV version 2.3