LCOV - code coverage report
Current view: top level - capy - buffers.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 91 91
Test Date: 2026-03-09 22:47:34 Functions: 100.0 % 156 156

           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_HPP
      11                 : #define BOOST_CAPY_BUFFERS_HPP
      12                 : 
      13                 : #include <boost/capy/detail/config.hpp>
      14                 : #include <concepts>
      15                 : #include <cstddef>
      16                 : #include <iterator>
      17                 : #include <memory>
      18                 : #include <ranges>
      19                 : #include <type_traits>
      20                 : 
      21                 : // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
      22                 : 
      23                 : namespace boost {
      24                 : 
      25                 : namespace asio {
      26                 : class const_buffer;
      27                 : class mutable_buffer;
      28                 : } // asio
      29                 : 
      30                 : namespace capy {
      31                 : 
      32                 : class const_buffer;
      33                 : class mutable_buffer;
      34                 : 
      35                 : /// Tag type for customizing `buffer_size` via `tag_invoke`.
      36                 : struct size_tag {};
      37                 : 
      38                 : /// Tag type for customizing slice operations via `tag_invoke`.
      39                 : struct slice_tag {};
      40                 : 
      41                 : /** Constants for slice customization.
      42                 : 
      43                 :     Passed to `tag_invoke` overloads to specify which portion
      44                 :     of a buffer sequence to retain.
      45                 : */
      46                 : enum class slice_how
      47                 : {
      48                 :     /// Remove bytes from the front of the sequence.
      49                 :     remove_prefix,
      50                 : 
      51                 :     /// Keep only the first N bytes.
      52                 :     keep_prefix
      53                 : };
      54                 : 
      55                 : /** A reference to a contiguous region of writable memory.
      56                 : 
      57                 :     Represents a pointer and size pair for a modifiable byte range.
      58                 :     Does not own the memory. Satisfies `MutableBufferSequence` (as a
      59                 :     single-element sequence) and is implicitly convertible to
      60                 :     `const_buffer`.
      61                 : 
      62                 :     @see const_buffer, MutableBufferSequence
      63                 : */
      64                 : class mutable_buffer
      65                 : {
      66                 :     unsigned char* p_ = nullptr;
      67                 :     std::size_t n_ = 0;
      68                 : 
      69                 : public:
      70                 :     /// Construct an empty buffer.
      71 HIT         603 :     mutable_buffer() = default;
      72                 : 
      73                 :     /// Construct a copy.
      74                 :     mutable_buffer(
      75                 :         mutable_buffer const&) = default;
      76                 : 
      77                 :     /// Assign by copying.
      78                 :     mutable_buffer& operator=(
      79                 :         mutable_buffer const&) = default;
      80                 : 
      81                 :     /// Construct from pointer and size.
      82           29024 :     constexpr mutable_buffer(
      83                 :         void* data, std::size_t size) noexcept
      84           29024 :         : p_(static_cast<unsigned char*>(data))
      85           29024 :         , n_(size)
      86                 :     {
      87           29024 :     }
      88                 : 
      89                 :     /// Return a pointer to the memory region.
      90           72309 :     constexpr void* data() const noexcept
      91                 :     {
      92           72309 :         return p_;
      93                 :     }
      94                 : 
      95                 :     /// Return the size in bytes.
      96          111912 :     constexpr std::size_t size() const noexcept
      97                 :     {
      98          111912 :         return n_;
      99                 :     }
     100                 : 
     101                 :     /** Advance the buffer start, shrinking the region.
     102                 : 
     103                 :         @param n Bytes to skip. Clamped to `size()`.
     104                 :     */
     105                 :     mutable_buffer&
     106           26490 :     operator+=(std::size_t n) noexcept
     107                 :     {
     108           26490 :         if( n > n_)
     109              17 :             n = n_;
     110           26490 :         p_ += n;
     111           26490 :         n_ -= n;
     112           26490 :         return *this;
     113                 :     }
     114                 : 
     115                 :     /// Slice customization point for `tag_invoke`.
     116                 :     friend
     117                 :     void
     118            1335 :     tag_invoke(
     119                 :         slice_tag const&,
     120                 :         mutable_buffer& b,
     121                 :         slice_how how,
     122                 :         std::size_t n) noexcept
     123                 :     {
     124            1335 :         b.do_slice(how, n);
     125            1335 :     }
     126                 : 
     127                 : private:
     128            1335 :     void do_slice(
     129                 :         slice_how how, std::size_t n) noexcept
     130                 :     {
     131            1335 :         switch(how)
     132                 :         {
     133             659 :         case slice_how::remove_prefix:
     134             659 :             *this += n;
     135             659 :             return;
     136                 : 
     137             676 :         case slice_how::keep_prefix:
     138             676 :             if( n < n_)
     139             584 :                 n_ = n;
     140             676 :             return;
     141                 :         }
     142                 :     }
     143                 : };
     144                 : 
     145                 : /** A reference to a contiguous region of read-only memory.
     146                 : 
     147                 :     Represents a pointer and size pair for a non-modifiable byte range.
     148                 :     Does not own the memory. Satisfies `ConstBufferSequence` (as a
     149                 :     single-element sequence). Implicitly constructible from
     150                 :     `mutable_buffer`.
     151                 : 
     152                 :     @see mutable_buffer, ConstBufferSequence
     153                 : */
     154                 : class const_buffer
     155                 : {
     156                 :     unsigned char const* p_ = nullptr;
     157                 :     std::size_t n_ = 0;
     158                 : 
     159                 : public:
     160                 :     /// Construct an empty buffer.
     161             631 :     const_buffer() = default;
     162                 : 
     163                 :     /// Construct a copy.
     164                 :     const_buffer(const_buffer const&) = default;
     165                 : 
     166                 :     /// Assign by copying.
     167                 :     const_buffer& operator=(
     168                 :         const_buffer const& other) = default;
     169                 : 
     170                 :     /// Construct from pointer and size.
     171           24993 :     constexpr const_buffer(
     172                 :         void const* data, std::size_t size) noexcept
     173           24993 :         : p_(static_cast<unsigned char const*>(data))
     174           24993 :         , n_(size)
     175                 :     {
     176           24993 :     }
     177                 : 
     178                 :     /// Construct from mutable_buffer.
     179           16679 :     constexpr const_buffer(
     180                 :         mutable_buffer const& b) noexcept
     181           16679 :         : p_(static_cast<unsigned char const*>(b.data()))
     182           16679 :         , n_(b.size())
     183                 :     {
     184           16679 :     }
     185                 : 
     186                 :     /// Return a pointer to the memory region.
     187           65130 :     constexpr void const* data() const noexcept
     188                 :     {
     189           65130 :         return p_;
     190                 :     }
     191                 : 
     192                 :     /// Return the size in bytes.
     193          126623 :     constexpr std::size_t size() const noexcept
     194                 :     {
     195          126623 :         return n_;
     196                 :     }
     197                 : 
     198                 :     /** Advance the buffer start, shrinking the region.
     199                 : 
     200                 :         @param n Bytes to skip. Clamped to `size()`.
     201                 :     */
     202                 :     const_buffer&
     203           27145 :     operator+=(std::size_t n) noexcept
     204                 :     {
     205           27145 :         if( n > n_)
     206              16 :             n = n_;
     207           27145 :         p_ += n;
     208           27145 :         n_ -= n;
     209           27145 :         return *this;
     210                 :     }
     211                 : 
     212                 :     /// Slice customization point for `tag_invoke`.
     213                 :     friend
     214                 :     void
     215            2640 :     tag_invoke(
     216                 :         slice_tag const&,
     217                 :         const_buffer& b,
     218                 :         slice_how how,
     219                 :         std::size_t n) noexcept
     220                 :     {
     221            2640 :         b.do_slice(how, n);
     222            2640 :     }
     223                 : 
     224                 : private:
     225            2640 :     void do_slice(
     226                 :         slice_how how, std::size_t n) noexcept
     227                 :     {
     228            2640 :         switch(how)
     229                 :         {
     230            1313 :         case slice_how::remove_prefix:
     231            1313 :             *this += n;
     232            1313 :             return;
     233                 : 
     234            1327 :         case slice_how::keep_prefix:
     235            1327 :             if( n < n_)
     236            1238 :                 n_ = n;
     237            1327 :             return;
     238                 :         }
     239                 :     }
     240                 : };
     241                 : 
     242                 : /** Concept for sequences of read-only buffer regions.
     243                 : 
     244                 :     A type satisfies `ConstBufferSequence` if it represents one or more
     245                 :     contiguous memory regions that can be read. This includes single
     246                 :     buffers (convertible to `const_buffer`) and ranges of buffers.
     247                 : 
     248                 :     @par Syntactic Requirements
     249                 :     @li Convertible to `const_buffer`, OR
     250                 :     @li A bidirectional range with value type convertible to `const_buffer`
     251                 : 
     252                 :     @see const_buffer, MutableBufferSequence
     253                 : */
     254                 : template<typename T>
     255                 : concept ConstBufferSequence =
     256                 :     std::is_convertible_v<T, const_buffer> || (
     257                 :         std::ranges::bidirectional_range<T> &&
     258                 :         std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
     259                 : 
     260                 : /** Concept for sequences of writable buffer regions.
     261                 : 
     262                 :     A type satisfies `MutableBufferSequence` if it represents one or more
     263                 :     contiguous memory regions that can be written. This includes single
     264                 :     buffers (convertible to `mutable_buffer`) and ranges of buffers.
     265                 :     Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
     266                 : 
     267                 :     @par Syntactic Requirements
     268                 :     @li Convertible to `mutable_buffer`, OR
     269                 :     @li A bidirectional range with value type convertible to `mutable_buffer`
     270                 : 
     271                 :     @see mutable_buffer, ConstBufferSequence
     272                 : */
     273                 : template<typename T>
     274                 : concept MutableBufferSequence =
     275                 :     std::is_convertible_v<T, mutable_buffer> || (
     276                 :         std::ranges::bidirectional_range<T> &&
     277                 :         std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
     278                 : 
     279                 : /** Return an iterator to the first buffer in a sequence.
     280                 : 
     281                 :     Handles single buffers and ranges uniformly. For a single buffer,
     282                 :     returns a pointer to it (forming a one-element range).
     283                 : */
     284                 : constexpr struct begin_mrdocs_workaround_t
     285                 : {
     286                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     287           21798 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     288                 :     {
     289           21798 :         return std::addressof(b);
     290                 :     }
     291                 : 
     292                 :     template<ConstBufferSequence BS>
     293                 :         requires (!std::convertible_to<BS, const_buffer>)
     294           56174 :     auto operator()(BS const& bs) const noexcept
     295                 :     {
     296           56174 :         return std::ranges::begin(bs);
     297                 :     }
     298                 : 
     299                 :     template<ConstBufferSequence BS>
     300                 :         requires (!std::convertible_to<BS, const_buffer>)
     301           18574 :     auto operator()(BS& bs) const noexcept
     302                 :     {
     303           18574 :         return std::ranges::begin(bs);
     304                 :     }
     305                 : } begin {};
     306                 : 
     307                 : /** Return an iterator past the last buffer in a sequence.
     308                 : 
     309                 :     Handles single buffers and ranges uniformly. For a single buffer,
     310                 :     returns a pointer one past it.
     311                 : */
     312                 : constexpr struct end_mrdocs_workaround_t
     313                 : {
     314                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     315           21558 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     316                 :     {
     317           21558 :         return std::addressof(b) + 1;
     318                 :     }
     319                 : 
     320                 :     template<ConstBufferSequence BS>
     321                 :         requires (!std::convertible_to<BS, const_buffer>)
     322           48645 :     auto operator()(BS const& bs) const noexcept
     323                 :     {
     324           48645 :         return std::ranges::end(bs);
     325                 :     }
     326                 : 
     327                 :     template<ConstBufferSequence BS>
     328                 :         requires (!std::convertible_to<BS, const_buffer>)
     329           18574 :     auto operator()(BS& bs) const noexcept
     330                 :     {
     331           18574 :         return std::ranges::end(bs);
     332                 :     }
     333                 : } end {};
     334                 : 
     335                 : template<ConstBufferSequence CB>
     336                 : std::size_t
     337           13742 : tag_invoke(
     338                 :     size_tag const&,
     339                 :     CB const& bs) noexcept
     340                 : {
     341           13742 :     std::size_t n = 0;
     342           13742 :     auto const e = end(bs);
     343           34629 :     for(auto it = begin(bs); it != e; ++it)
     344           20887 :         n += const_buffer(*it).size();
     345           13742 :     return n;
     346                 : }
     347                 : 
     348                 : /** Return the total byte count across all buffers in a sequence.
     349                 : 
     350                 :     Sums the `size()` of each buffer in the sequence. This differs
     351                 :     from `buffer_length` which counts the number of buffer elements.
     352                 : 
     353                 :     @par Example
     354                 :     @code
     355                 :     std::array<mutable_buffer, 2> bufs = { ... };
     356                 :     std::size_t total = buffer_size( bufs );  // sum of both sizes
     357                 :     @endcode
     358                 : */
     359                 : constexpr struct buffer_size_mrdocs_workaround_t
     360                 : {
     361                 :     template<ConstBufferSequence CB>
     362           19241 :     constexpr std::size_t operator()(
     363                 :         CB const& bs) const noexcept
     364                 :     {
     365           19241 :         return tag_invoke(size_tag{}, bs);
     366                 :     }
     367                 : } buffer_size {};
     368                 : 
     369                 : /** Check if a buffer sequence contains no data.
     370                 : 
     371                 :     @return `true` if all buffers have size zero or the sequence
     372                 :         is empty.
     373                 : */
     374                 : constexpr struct buffer_empty_mrdocs_workaround_t
     375                 : {
     376                 :     template<ConstBufferSequence CB>
     377            4156 :     constexpr bool operator()(
     378                 :         CB const& bs) const noexcept
     379                 :     {
     380            4156 :         auto it = begin(bs);
     381            4156 :         auto const end_ = end(bs);
     382            4211 :         while(it != end_)
     383                 :         {
     384            4169 :             const_buffer b(*it++);
     385            4169 :             if(b.size() != 0)
     386            4114 :                 return false;
     387                 :         }
     388              42 :         return true;
     389                 :     }
     390                 : } buffer_empty {};
     391                 : 
     392                 : namespace detail {
     393                 : 
     394                 : template<class It>
     395                 : auto
     396             240 : length_impl(It first, It last, int)
     397                 :     -> decltype(static_cast<std::size_t>(last - first))
     398                 : {
     399             240 :     return static_cast<std::size_t>(last - first);
     400                 : }
     401                 : 
     402                 : template<class It>
     403                 : std::size_t
     404                 : length_impl(It first, It last, long)
     405                 : {
     406                 :     std::size_t n = 0;
     407                 :     while(first != last)
     408                 :     {
     409                 :         ++first;
     410                 :         ++n;
     411                 :     }
     412                 :     return n;
     413                 : }
     414                 : 
     415                 : } // detail
     416                 : 
     417                 : /** Return the number of buffer elements in a sequence.
     418                 : 
     419                 :     Counts the number of individual buffer objects, not bytes.
     420                 :     For a single buffer, returns 1. For a range, returns the
     421                 :     distance from `begin` to `end`.
     422                 : 
     423                 :     @see buffer_size
     424                 : */
     425                 : template<ConstBufferSequence CB>
     426                 : std::size_t
     427             240 : buffer_length(CB const& bs)
     428                 : {
     429             240 :     return detail::length_impl(
     430             240 :         begin(bs), end(bs), 0);
     431                 : }
     432                 : 
     433                 : /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
     434                 : template<typename BS>
     435                 : using buffer_type = std::conditional_t<
     436                 :     MutableBufferSequence<BS>,
     437                 :     mutable_buffer, const_buffer>;
     438                 : 
     439                 : } // capy
     440                 : } // boost
     441                 : 
     442                 : #endif
        

Generated by: LCOV version 2.3