LCOV - code coverage report
Current view: top level - capy/buffers - slice.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 97.0 % 166 161 5
Test Date: 2026-03-09 21:20:58 Functions: 100.0 % 110 110

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       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_SLICE_HPP
      11                 : #define BOOST_CAPY_BUFFERS_SLICE_HPP
      12                 : 
      13                 : #include <boost/capy/detail/config.hpp>
      14                 : #include <boost/capy/buffers.hpp>
      15                 : #include <array>
      16                 : #include <cassert>
      17                 : #include <iterator>
      18                 : #include <type_traits>
      19                 : 
      20                 : namespace boost {
      21                 : namespace capy {
      22                 : 
      23                 : template<class T> class slice_of;
      24                 : 
      25                 : namespace detail {
      26                 : 
      27                 : template<class T, class = void>
      28                 : struct has_tag_invoke : std::false_type {};
      29                 : 
      30                 : template<class T>
      31                 : struct has_tag_invoke<T, decltype(tag_invoke(
      32                 :     std::declval<slice_tag const&>(),
      33                 :     std::declval<T&>(),
      34                 :     std::declval<slice_how>(),
      35                 :     std::declval<std::size_t>()))>
      36                 :     : std::true_type {};
      37                 : 
      38                 : } // detail
      39                 : 
      40                 : /** Alias for the type representing a slice of T
      41                 : */
      42                 : template<class T>
      43                 : using slice_type = std::conditional_t<
      44                 :     detail::has_tag_invoke<T>::value,
      45                 :     T, slice_of<T>>;
      46                 : 
      47                 : /** A view of a sub-range of a buffer sequence.
      48                 : 
      49                 :     This class wraps a buffer sequence and presents a
      50                 :     contiguous byte sub-range by adjusting the first and
      51                 :     last buffers. The prefix and suffix can be removed or
      52                 :     kept using the free functions @ref keep_prefix,
      53                 :     @ref remove_prefix, etc.
      54                 : 
      55                 :     The wrapped sequence is stored by value. The underlying
      56                 :     buffer memory must remain valid for the lifetime of the
      57                 :     slice.
      58                 : 
      59                 :     @par Thread Safety
      60                 :     Distinct objects: Safe.
      61                 :     Shared objects: Unsafe.
      62                 : 
      63                 :     @par Example
      64                 :     @code
      65                 :     mutable_buffer buf(data, 100);
      66                 :     auto s = prefix(buf, 50);   // first 50 bytes
      67                 :     remove_prefix(s, 10);       // now bytes 10..49
      68                 :     @endcode
      69                 : 
      70                 :     @tparam BufferSequence The buffer sequence type, stored
      71                 :         by value. Must satisfy @ref ConstBufferSequence.
      72                 : 
      73                 :     @see keep_prefix, remove_prefix, prefix, sans_prefix
      74                 : */
      75                 : template<ConstBufferSequence BufferSequence>
      76                 : class slice_of<BufferSequence>
      77                 : {
      78                 :     static_assert(!std::is_const_v<BufferSequence>,
      79                 :         "BufferSequence can't be const");
      80                 : 
      81                 :     static_assert(!std::is_reference_v<BufferSequence>,
      82                 :         "BufferSequence can't be a reference");
      83                 : 
      84                 :     using iter_type = decltype(
      85                 :         std::declval<BufferSequence const&>().begin());
      86                 : 
      87                 :     using difference_type =
      88                 :         typename std::iterator_traits<iter_type>::difference_type;
      89                 : 
      90                 :     BufferSequence bs_;
      91                 :     difference_type begin_ = 0; // index of first buffer in sequence
      92                 :     difference_type end_ = 0;   // 1 + index of last buffer in sequence
      93                 :     std::size_t len_ = 0;       // length of bs_
      94                 :     std::size_t size_ = 0;      // total bytes
      95                 :     std::size_t prefix_ = 0;    // used prefix bytes
      96                 :     std::size_t suffix_ = 0;    // used suffix bytes
      97                 : 
      98                 : public:
      99                 :     /** The type of values returned by iterators
     100                 :     */
     101                 :     using value_type = std::conditional_t<
     102                 :         MutableBufferSequence<BufferSequence>,
     103                 :         mutable_buffer, const_buffer>;
     104                 : 
     105                 :     /** The type of returned iterators
     106                 :     */
     107                 :     class const_iterator
     108                 :     {
     109                 :         iter_type it_;
     110                 :         // VFALCO we could just point back to
     111                 :         // the original sequence to save size
     112                 :         std::size_t prefix_ = 0;
     113                 :         std::size_t suffix_ = 0;
     114                 :         std::size_t i_ = 0;
     115                 :         std::size_t n_ = 0;
     116                 : 
     117                 :         friend class slice_of<BufferSequence>;
     118                 : 
     119 HIT        6652 :         const_iterator(
     120                 :             iter_type it,
     121                 :             std::size_t prefix__,
     122                 :             std::size_t suffix__,
     123                 :             std::size_t i,
     124                 :             std::size_t n) noexcept
     125            6652 :             : it_(it)
     126            6652 :             , prefix_(prefix__)
     127            6652 :             , suffix_(suffix__)
     128            6652 :             , i_(i)
     129            6652 :             , n_(n)
     130                 :         {
     131                 :             // n_ is the index of the end iterator
     132            6652 :         }
     133                 : 
     134                 :     public:
     135                 :         using value_type = typename slice_of::value_type;
     136                 :         using reference = value_type;
     137                 :         using pointer = void;
     138                 :         using difference_type = std::ptrdiff_t;
     139                 :         using iterator_category =
     140                 :             std::bidirectional_iterator_tag;
     141                 :         using iterator_concept = std::bidirectional_iterator_tag;
     142                 : 
     143                 :         const_iterator() = default;
     144                 : 
     145                 :         /// Test for equality.
     146                 :         bool
     147            9116 :         operator==(
     148                 :             const_iterator const& other) const noexcept
     149                 :         {
     150                 :             return
     151            9144 :                 it_     == other.it_ &&
     152            3326 :                 prefix_ == other.prefix_ &&
     153            3326 :                 suffix_ == other.suffix_ &&
     154           15768 :                 i_      == other.i_ &&
     155           12442 :                 n_      == other.n_;
     156                 :         }
     157                 : 
     158                 :         /// Test for inequality.
     159                 :         bool
     160            9116 :         operator!=(
     161                 :             const_iterator const& other) const noexcept
     162                 :         {
     163            9116 :             return !(*this == other);
     164                 :         }
     165                 : 
     166                 :         /// Return the current buffer, adjusted for prefix/suffix.
     167                 :         reference
     168            5790 :         operator*() const noexcept
     169                 :         {
     170            5790 :             value_type v = *it_;
     171                 :             using P = std::conditional_t<
     172                 :                 MutableBufferSequence<BufferSequence>,
     173                 :                 char*, char const*>;
     174            5790 :             auto p = reinterpret_cast<P>(v.data());
     175            5790 :             auto n = v.size();
     176            5790 :             if(i_ == 0)
     177                 :             {
     178            2943 :                 p += prefix_;
     179            2943 :                 n -= prefix_;
     180                 :             }
     181            5790 :             if(i_ == n_ - 1)
     182            2943 :                 n -= suffix_;
     183            5790 :             return value_type(p, n);
     184                 :         }
     185                 : 
     186                 :         /// Advance to the next element.
     187                 :         const_iterator&
     188            4502 :         operator++() noexcept
     189                 :         {
     190            4502 :             BOOST_CAPY_ASSERT(i_ < n_);
     191            4502 :             ++it_;
     192            4502 :             ++i_;
     193            4502 :             return *this;
     194                 :         }
     195                 : 
     196                 :         /// Advance to the next element (postfix).
     197                 :         const_iterator
     198             644 :         operator++(int) noexcept
     199                 :         {
     200             644 :             auto temp = *this;
     201             644 :             ++(*this);
     202             644 :             return temp;
     203                 :         }
     204                 : 
     205                 :         /// Move to the previous element.
     206                 :         const_iterator&
     207            1288 :         operator--() noexcept
     208                 :         {
     209            1288 :             BOOST_CAPY_ASSERT(i_ > 0);
     210            1288 :             --it_;
     211            1288 :             --i_;
     212            1288 :             return *this;
     213                 :         }
     214                 : 
     215                 :         /// Move to the previous element (postfix).
     216                 :         const_iterator
     217             644 :         operator--(int) noexcept
     218                 :         {
     219             644 :             auto temp = *this;
     220             644 :             --(*this);
     221             644 :             return temp;
     222                 :         }
     223                 :     };
     224                 : 
     225                 :     /** Constructor
     226                 :     */
     227                 :     slice_of() = default;
     228                 : 
     229                 :     /** Constructor
     230                 :     */
     231             194 :     slice_of(
     232                 :         BufferSequence const& bs)
     233             194 :         : bs_(bs)
     234                 :     {
     235             194 :         iter_type it = capy::begin(bs_);
     236             194 :         iter_type eit = capy::end(bs_);
     237             194 :         begin_ = 0;
     238             194 :         end_ = std::distance(it, eit);
     239             776 :         while(it != eit)
     240                 :         {
     241             582 :             value_type b(*it);
     242             582 :             size_ += b.size();
     243             582 :             ++len_;
     244             582 :             ++it;
     245                 :         }
     246             194 :     }
     247                 : 
     248                 :     /** Return an iterator to the beginning of the sequence
     249                 :     */
     250                 :     const_iterator
     251            3326 :     begin() const noexcept
     252                 :     {
     253                 :         return const_iterator(
     254            3326 :             begin_iter_impl(), prefix_, suffix_, 0, len_);
     255                 :     }
     256                 : 
     257                 :     /** Return an iterator to the end of the sequence
     258                 :     */
     259                 :     const_iterator
     260            3326 :     end() const noexcept
     261                 :     {
     262                 :         return const_iterator(
     263            3326 :             end_iter_impl(), prefix_, suffix_, len_, len_);
     264                 :     }
     265                 : 
     266                 :     /// Slice customization point for this type.
     267                 :     friend
     268                 :     void
     269             671 :     tag_invoke(
     270                 :         slice_tag const&,
     271                 :         slice_of<BufferSequence>& bs,
     272                 :         slice_how how,
     273                 :         std::size_t n)
     274                 :     {
     275             671 :         bs.slice_impl(how, n);
     276             671 :     }
     277                 : 
     278                 : private:
     279                 :     iter_type
     280            3938 :     begin_iter_impl() const noexcept
     281                 :     {
     282            3938 :         iter_type it = capy::begin(bs_);
     283            3938 :         std::advance(it, begin_);
     284            3938 :         return it;
     285                 :     }
     286                 : 
     287                 :     iter_type
     288            3591 :     end_iter_impl() const noexcept
     289                 :     {
     290            3591 :         iter_type it = capy::begin(bs_);
     291            3591 :         std::advance(it, end_);
     292            3591 :         return it;
     293                 :     }
     294                 : 
     295                 :     void
     296             347 :     remove_prefix_impl(
     297                 :         std::size_t n)
     298                 :     {
     299             347 :         if(n > size_)
     300              25 :             n = size_;
     301                 : 
     302                 :         // nice hack to simplify the loop (M. Nejati)
     303             347 :         n += prefix_;
     304             347 :         size_ += prefix_;
     305             347 :         prefix_ = 0;
     306                 : 
     307             347 :         iter_type it = begin_iter_impl();
     308                 : 
     309             709 :         while(n > 0 && begin_ != end_)
     310                 :         {
     311             612 :             value_type b = *it;
     312             612 :             if(n < b.size())
     313                 :             {
     314             250 :                 prefix_ = n;
     315             250 :                 size_ -= n;
     316             250 :                 break;
     317                 :             }
     318             362 :             n -= b.size();
     319             362 :             size_ -= b.size();
     320             362 :             ++begin_;
     321             362 :             ++it;
     322             362 :             --len_;
     323                 :         }
     324             347 :     }
     325                 : 
     326                 :     void
     327             265 :     remove_suffix_impl(
     328                 :         std::size_t n)
     329                 :     {
     330             265 :         if(size_ == 0)
     331                 :         {
     332 MIS           0 :             BOOST_CAPY_ASSERT(begin_ == end_);
     333 HIT         265 :             return;
     334                 :         }
     335             265 :         BOOST_CAPY_ASSERT(begin_ != end_);
     336                 : 
     337             265 :         if(n > size_)
     338 MIS           0 :             n = size_;
     339                 : 
     340 HIT         265 :         n += suffix_;
     341             265 :         size_ += suffix_;
     342             265 :         suffix_ = 0;
     343                 : 
     344             265 :         iter_type bit = begin_iter_impl();
     345             265 :         iter_type it = end_iter_impl();
     346             265 :         it--;
     347                 : 
     348             517 :         while(it != bit)
     349                 :         {
     350             391 :             value_type b = *it;
     351             391 :             if(n < b.size())
     352                 :             {
     353             139 :                 suffix_ = n;
     354             139 :                 size_ -= n;
     355             139 :                 return;
     356                 :             }
     357             252 :             n -= b.size();
     358             252 :             size_ -= b.size();
     359             252 :             --it;
     360             252 :             --end_;
     361             252 :             --len_;
     362                 :         }
     363             126 :         value_type b = *it;
     364             126 :         auto m = b.size() - prefix_;
     365             126 :         if(n < m)
     366                 :         {
     367             126 :             suffix_ = n;
     368             126 :             size_ -= n;
     369             126 :             return;
     370                 :         }
     371 MIS           0 :         end_ = begin_;
     372               0 :         len_ = 0;
     373               0 :         size_ = 0;
     374                 :     }
     375                 : 
     376                 :     void
     377 HIT         324 :     keep_prefix_impl(
     378                 :         std::size_t n)
     379                 :     {
     380             324 :         if(n >= size_)
     381               9 :             return;
     382             315 :         if(n == 0)
     383                 :         {
     384              50 :             end_ = begin_;
     385              50 :             len_ = 0;
     386              50 :             size_ = 0;
     387              50 :             return;
     388                 :         }
     389             265 :         remove_suffix_impl(size_ - n);
     390                 :     }
     391                 : 
     392                 :     void
     393                 :     keep_suffix_impl(
     394                 :         std::size_t n)
     395                 :     {
     396                 :         if(n >= size_)
     397                 :             return;
     398                 :         if(n == 0)
     399                 :         {
     400                 :             begin_ = end_;
     401                 :             len_ = 0;
     402                 :             size_ = 0;
     403                 :             return;
     404                 :         }
     405                 :         remove_prefix_impl(size_ - n);
     406                 :     }
     407                 : 
     408                 :     void
     409             671 :     slice_impl(
     410                 :         slice_how how,
     411                 :         std::size_t n)
     412                 :     {
     413             671 :         switch(how)
     414                 :         {
     415             347 :         case slice_how::remove_prefix:
     416                 :         {
     417             347 :             remove_prefix_impl(n);
     418             347 :             break;
     419                 :         }
     420             324 :         case slice_how::keep_prefix:
     421                 :         {
     422             324 :             keep_prefix_impl(n);
     423             324 :             break;
     424                 :         }
     425                 :         }
     426             671 :     }
     427                 : };
     428                 : 
     429                 : // in-place modify  return value
     430                 : // -----------------------------
     431                 : // keep_prefix*     prefix
     432                 : // keep_suffix      suffix
     433                 : // remove_prefix*   sans_prefix
     434                 : // remove_suffix    sans_suffix
     435                 : 
     436                 : /** Remove all but the first `n` bytes from a buffer sequence
     437                 : */
     438                 : constexpr struct keep_prefix_mrdocs_workaround_t
     439                 : {
     440                 :     template<ConstBufferSequence BufferSequence>
     441                 :         requires detail::has_tag_invoke<BufferSequence>::value
     442            3142 :     void operator()(
     443                 :         BufferSequence& bs,
     444                 :         std::size_t n) const
     445                 :     {
     446            3142 :         tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n);
     447            3142 :     }
     448                 : } const keep_prefix{};
     449                 : 
     450                 : /** Remove all but the last `n` bytes from a buffer sequence
     451                 : */
     452                 : constexpr struct keep_suffix_mrdocs_workaround_t
     453                 : {
     454                 :     template<ConstBufferSequence BufferSequence>
     455                 :         requires detail::has_tag_invoke<BufferSequence>::value
     456            1132 :     void operator()(
     457                 :         BufferSequence& bs,
     458                 :         std::size_t n) const
     459                 :     {
     460            1132 :         auto n0 = buffer_size(bs);
     461            1132 :         if(n < n0)
     462             998 :             tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n0 - n);
     463            1132 :     }
     464                 : } const keep_suffix{};
     465                 : 
     466                 : /** Remove `n` bytes from the beginning of a buffer sequence
     467                 : */
     468                 : constexpr struct remove_prefix_mrdocs_workaround_t
     469                 : {
     470                 :     template<ConstBufferSequence BufferSequence>
     471                 :         requires detail::has_tag_invoke<BufferSequence>::value
     472            3369 :     void operator()(
     473                 :         BufferSequence& bs,
     474                 :         std::size_t n) const
     475                 :     {
     476            3369 :         tag_invoke(slice_tag{}, bs, slice_how::remove_prefix, n);
     477            3369 :     }
     478                 : } const remove_prefix{};
     479                 : 
     480                 : /** Remove `n` bytes from the end of a buffer sequence
     481                 : */
     482                 : constexpr struct remove_suffix_mrdocs_workaround_t
     483                 : {
     484                 :     template<ConstBufferSequence BufferSequence>
     485                 :         requires detail::has_tag_invoke<BufferSequence>::value
     486            1386 :     void operator()(
     487                 :         BufferSequence& bs,
     488                 :         std::size_t n) const
     489                 :     {
     490            1386 :         auto n0 = buffer_size(bs);
     491            1386 :         if(n > 0)
     492                 :         {
     493            1297 :             if( n > n0)
     494              89 :                 n = n0;
     495            1297 :             tag_invoke(slice_tag{}, bs, slice_how::keep_prefix, n0 - n);
     496                 :         }
     497            1386 :     }
     498                 : } const remove_suffix{};
     499                 : 
     500                 : /** Return a sequence representing the first `n` bytes of a buffer sequence
     501                 : */
     502                 : constexpr struct prefix_mrdocs_workaround_t
     503                 : {
     504                 :     template<ConstBufferSequence BufferSequence>
     505             944 :     slice_type<BufferSequence> operator()(
     506                 :         BufferSequence const& bs,
     507                 :         std::size_t n) const noexcept
     508                 :     {
     509             944 :         slice_type<BufferSequence> result(bs);
     510             944 :         keep_prefix(result, n);
     511             944 :         return result;
     512                 :     }
     513                 : } prefix{};
     514                 : 
     515                 : /** Return a sequence representing the last `n` bytes of a buffer sequence
     516                 : */
     517                 : constexpr struct suffix_mrdocs_workaround_t
     518                 : {
     519                 :     template<ConstBufferSequence BufferSequence>
     520                 :     slice_type<BufferSequence> operator()(
     521                 :         BufferSequence const& bs,
     522                 :         std::size_t n) const noexcept
     523                 :     {
     524                 :         slice_type<BufferSequence> result(bs);
     525                 :         keep_suffix(result, n);
     526                 :         return result;
     527                 :     }
     528                 : } suffix{};
     529                 : 
     530                 : /** Return a sequence representing all but the first `n` bytes of a buffer sequence
     531                 : */
     532                 : constexpr struct sans_prefix_mrdocs_workaround_t
     533                 : {
     534                 :     template<ConstBufferSequence BufferSequence>
     535             959 :     slice_type<BufferSequence> operator()(
     536                 :         BufferSequence const& bs,
     537                 :         std::size_t n) const noexcept
     538                 :     {
     539             959 :         slice_type<BufferSequence> result(bs);
     540             959 :         remove_prefix(result, n);
     541             959 :         return result;
     542                 :     }
     543                 : } sans_prefix{};
     544                 : 
     545                 : /** Return a sequence representing all but the last `n` bytes of a buffer sequence
     546                 : */
     547                 : constexpr struct sans_suffix_mrdocs_workaround_t
     548                 : {
     549                 :     template<ConstBufferSequence BufferSequence>
     550                 :     slice_type<BufferSequence> operator()(
     551                 :         BufferSequence const& bs,
     552                 :         std::size_t n) const noexcept
     553                 :     {
     554                 :         slice_type<BufferSequence> result(bs);
     555                 :         remove_suffix(result, n);
     556                 :         return result;
     557                 :     }
     558                 : } sans_suffix{};
     559                 : 
     560                 : } // capy
     561                 : } // boost
     562                 : 
     563                 : #endif
        

Generated by: LCOV version 2.3