LCOV - code coverage report
Current view: top level - capy - buffers.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 93 93
Test Date: 2026-02-17 18:14:47 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                 : namespace detail {
      36                 : 
      37                 : // satisfies Asio's buffer constructors, CANNOT be removed!
      38                 : template<class T, std::size_t Extent = (std::size_t)(-1)>
      39                 : class basic_buffer
      40                 : {
      41                 :     constexpr auto data() const noexcept ->
      42                 :         std::conditional_t<std::is_const_v<T>, void const*, void*>
      43                 :     {
      44                 :         return p_;
      45                 :     }
      46                 : 
      47                 :     constexpr std::size_t size() const noexcept
      48                 :     {
      49                 :         return n_;
      50                 :     }
      51                 : 
      52                 :     friend class capy::const_buffer;
      53                 :     friend class capy::mutable_buffer;
      54                 :     friend class asio::const_buffer;
      55                 :     friend class asio::mutable_buffer;
      56 HIT        1234 :     basic_buffer() = default;
      57           66725 :     constexpr basic_buffer(T* p, std::size_t n) noexcept : p_(p), n_(n) {}
      58                 :     constexpr basic_buffer<T, (std::size_t)(-1)> subspan(
      59                 :         std::size_t, std::size_t = (std::size_t)(-1)) const noexcept;
      60                 : 
      61                 :     T* p_ = nullptr;
      62                 :     std::size_t n_ = 0;
      63                 : };
      64                 : 
      65                 : } // detail
      66                 : 
      67                 : //------------------------------------------------
      68                 : 
      69                 : /// Tag type for customizing `buffer_size` via `tag_invoke`.
      70                 : struct size_tag {};
      71                 : 
      72                 : /// Tag type for customizing slice operations via `tag_invoke`.
      73                 : struct slice_tag {};
      74                 : 
      75                 : /** Constants for slice customization.
      76                 : 
      77                 :     Passed to `tag_invoke` overloads to specify which portion
      78                 :     of a buffer sequence to retain.
      79                 : */
      80                 : enum class slice_how
      81                 : {
      82                 :     /// Remove bytes from the front of the sequence.
      83                 :     remove_prefix,
      84                 : 
      85                 :     /// Keep only the first N bytes.
      86                 :     keep_prefix
      87                 : };
      88                 : 
      89                 : //------------------------------------------------
      90                 : 
      91                 : /** A reference to a contiguous region of writable memory.
      92                 : 
      93                 :     Represents a pointer and size pair for a modifiable byte range.
      94                 :     Does not own the memory. Satisfies `MutableBufferSequence` (as a
      95                 :     single-element sequence) and is implicitly convertible to
      96                 :     `const_buffer`.
      97                 : 
      98                 :     @see const_buffer, MutableBufferSequence
      99                 : */
     100                 : class mutable_buffer
     101                 :     : public detail::basic_buffer<unsigned char>
     102                 : {
     103                 : public:
     104                 :     /// Construct an empty buffer.
     105             603 :     mutable_buffer() = default;
     106                 : 
     107                 :     /// Copy constructor.
     108                 :     mutable_buffer(
     109                 :         mutable_buffer const&) = default;
     110                 : 
     111                 :     /// Copy assignment.
     112                 :     mutable_buffer& operator=(
     113                 :         mutable_buffer const&) = default;
     114                 : 
     115                 :     /// Construct from pointer and size.
     116           27752 :     constexpr mutable_buffer(
     117                 :         void* data, std::size_t size) noexcept
     118           27752 :         : basic_buffer<unsigned char>(
     119           27752 :             static_cast<unsigned char*>(data), size)
     120                 :     {
     121           27752 :     }
     122                 : 
     123                 :     /// Construct from Asio mutable_buffer.
     124                 :     template<class MutableBuffer>
     125                 :         requires std::same_as<MutableBuffer, asio::mutable_buffer>
     126                 :     constexpr mutable_buffer(
     127                 :         MutableBuffer const& b) noexcept
     128                 :         : basic_buffer<unsigned char>(
     129                 :             static_cast<unsigned char*>(
     130                 :                 b.data()), b.size())
     131                 :     {
     132                 :     }
     133                 : 
     134                 :     /// Return a pointer to the memory region.
     135           71546 :     constexpr void* data() const noexcept
     136                 :     {
     137           71546 :         return p_;
     138                 :     }
     139                 : 
     140                 :     /// Return the size in bytes.
     141          109881 :     constexpr std::size_t size() const noexcept
     142                 :     {
     143          109881 :         return n_;
     144                 :     }
     145                 : 
     146                 :     /** Advance the buffer start, shrinking the region.
     147                 : 
     148                 :         @param n Bytes to skip. Clamped to `size()`.
     149                 :     */
     150                 :     mutable_buffer&
     151           26482 :     operator+=(std::size_t n) noexcept
     152                 :     {
     153           26482 :         if( n > n_)
     154              17 :             n = n_;
     155           26482 :         p_ += n;
     156           26482 :         n_ -= n;
     157           26482 :         return *this;
     158                 :     }
     159                 : 
     160                 :     /// Slice customization point for `tag_invoke`.
     161                 :     friend
     162                 :     void
     163            1335 :     tag_invoke(
     164                 :         slice_tag const&,
     165                 :         mutable_buffer& b,
     166                 :         slice_how how,
     167                 :         std::size_t n) noexcept
     168                 :     {
     169            1335 :         b.do_slice(how, n);
     170            1335 :     }
     171                 : 
     172                 : private:
     173            1335 :     void do_slice(
     174                 :         slice_how how, std::size_t n) noexcept
     175                 :     {
     176            1335 :         switch(how)
     177                 :         {
     178             659 :         case slice_how::remove_prefix:
     179             659 :             *this += n;
     180             659 :             return;
     181                 : 
     182             676 :         case slice_how::keep_prefix:
     183             676 :             if( n < n_)
     184             584 :                 n_ = n;
     185             676 :             return;
     186                 :         }
     187                 :     }
     188                 : };
     189                 : 
     190                 : //------------------------------------------------
     191                 : 
     192                 : /** A reference to a contiguous region of read-only memory.
     193                 : 
     194                 :     Represents a pointer and size pair for a non-modifiable byte range.
     195                 :     Does not own the memory. Satisfies `ConstBufferSequence` (as a
     196                 :     single-element sequence). Implicitly constructible from
     197                 :     `mutable_buffer`.
     198                 : 
     199                 :     @see mutable_buffer, ConstBufferSequence
     200                 : */
     201                 : class const_buffer
     202                 :     : public detail::basic_buffer<unsigned char const>
     203                 : {
     204                 : public:
     205                 :     /// Construct an empty buffer.
     206             631 :     const_buffer() = default;
     207                 : 
     208                 :     /// Copy constructor.
     209                 :     const_buffer(const_buffer const&) = default;
     210                 : 
     211                 :     /// Copy assignment.
     212                 :     const_buffer& operator=(
     213                 :         const_buffer const& other) = default;
     214                 : 
     215                 :     /// Construct from pointer and size.
     216           22294 :     constexpr const_buffer(
     217                 :         void const* data, std::size_t size) noexcept
     218           22294 :         : basic_buffer<unsigned char const>(
     219           22294 :             static_cast<unsigned char const*>(data), size)
     220                 :     {
     221           22294 :     }
     222                 : 
     223                 :     /// Construct from mutable_buffer.
     224           16679 :     constexpr const_buffer(
     225                 :         mutable_buffer const& b) noexcept
     226           16679 :         : basic_buffer<unsigned char const>(
     227           16679 :             static_cast<unsigned char const*>(b.data()), b.size())
     228                 :     {
     229           16679 :     }
     230                 : 
     231                 :     /// Construct from Asio buffer types.
     232                 :     template<class ConstBuffer>
     233                 :         requires (std::same_as<ConstBuffer, asio::const_buffer> ||
     234                 :                   std::same_as<ConstBuffer, asio::mutable_buffer>)
     235                 :     constexpr const_buffer(
     236                 :         ConstBuffer const& b) noexcept
     237                 :         : basic_buffer<unsigned char const>(
     238                 :             static_cast<unsigned char const*>(
     239                 :                 b.data()), b.size())
     240                 :     {
     241                 :     }
     242                 : 
     243                 :     /// Return a pointer to the memory region.
     244           63750 :     constexpr void const* data() const noexcept
     245                 :     {
     246           63750 :         return p_;
     247                 :     }
     248                 : 
     249                 :     /// Return the size in bytes.
     250          123901 :     constexpr std::size_t size() const noexcept
     251                 :     {
     252          123901 :         return n_;
     253                 :     }
     254                 : 
     255                 :     /** Advance the buffer start, shrinking the region.
     256                 : 
     257                 :         @param n Bytes to skip. Clamped to `size()`.
     258                 :     */
     259                 :     const_buffer&
     260           27137 :     operator+=(std::size_t n) noexcept
     261                 :     {
     262           27137 :         if( n > n_)
     263              16 :             n = n_;
     264           27137 :         p_ += n;
     265           27137 :         n_ -= n;
     266           27137 :         return *this;
     267                 :     }
     268                 : 
     269                 :     /// Slice customization point for `tag_invoke`.
     270                 :     friend
     271                 :     void
     272            2640 :     tag_invoke(
     273                 :         slice_tag const&,
     274                 :         const_buffer& b,
     275                 :         slice_how how,
     276                 :         std::size_t n) noexcept
     277                 :     {
     278            2640 :         b.do_slice(how, n);
     279            2640 :     }
     280                 : 
     281                 : private:
     282            2640 :     void do_slice(
     283                 :         slice_how how, std::size_t n) noexcept
     284                 :     {
     285            2640 :         switch(how)
     286                 :         {
     287            1313 :         case slice_how::remove_prefix:
     288            1313 :             *this += n;
     289            1313 :             return;
     290                 : 
     291            1327 :         case slice_how::keep_prefix:
     292            1327 :             if( n < n_)
     293            1238 :                 n_ = n;
     294            1327 :             return;
     295                 :         }
     296                 :     }
     297                 : };
     298                 : 
     299                 : //------------------------------------------------
     300                 : 
     301                 : /** Concept for sequences of read-only buffer regions.
     302                 : 
     303                 :     A type satisfies `ConstBufferSequence` if it represents one or more
     304                 :     contiguous memory regions that can be read. This includes single
     305                 :     buffers (convertible to `const_buffer`) and ranges of buffers.
     306                 : 
     307                 :     @par Syntactic Requirements
     308                 :     @li Convertible to `const_buffer`, OR
     309                 :     @li A bidirectional range with value type convertible to `const_buffer`
     310                 : 
     311                 :     @see const_buffer, MutableBufferSequence
     312                 : */
     313                 : template<typename T>
     314                 : concept ConstBufferSequence =
     315                 :     std::is_convertible_v<T, const_buffer> || (
     316                 :         std::ranges::bidirectional_range<T> &&
     317                 :         std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
     318                 : 
     319                 : /** Concept for sequences of writable buffer regions.
     320                 : 
     321                 :     A type satisfies `MutableBufferSequence` if it represents one or more
     322                 :     contiguous memory regions that can be written. This includes single
     323                 :     buffers (convertible to `mutable_buffer`) and ranges of buffers.
     324                 :     Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
     325                 : 
     326                 :     @par Syntactic Requirements
     327                 :     @li Convertible to `mutable_buffer`, OR
     328                 :     @li A bidirectional range with value type convertible to `mutable_buffer`
     329                 : 
     330                 :     @see mutable_buffer, ConstBufferSequence
     331                 : */
     332                 : template<typename T>
     333                 : concept MutableBufferSequence =
     334                 :     std::is_convertible_v<T, mutable_buffer> || (
     335                 :         std::ranges::bidirectional_range<T> &&
     336                 :         std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
     337                 : 
     338                 : //------------------------------------------------------------------------------
     339                 : 
     340                 : /** Return an iterator to the first buffer in a sequence.
     341                 : 
     342                 :     Handles single buffers and ranges uniformly. For a single buffer,
     343                 :     returns a pointer to it (forming a one-element range).
     344                 : */
     345                 : constexpr struct begin_mrdocs_workaround_t
     346                 : {
     347                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     348           21794 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     349                 :     {
     350           21794 :         return std::addressof(b);
     351                 :     }
     352                 : 
     353                 :     template<ConstBufferSequence BS>
     354                 :         requires (!std::convertible_to<BS, const_buffer>)
     355           56166 :     auto operator()(BS const& bs) const noexcept
     356                 :     {
     357           56166 :         return std::ranges::begin(bs);
     358                 :     }
     359                 : 
     360                 :     template<ConstBufferSequence BS>
     361                 :         requires (!std::convertible_to<BS, const_buffer>)
     362           18574 :     auto operator()(BS& bs) const noexcept
     363                 :     {
     364           18574 :         return std::ranges::begin(bs);
     365                 :     }
     366                 : } begin {};
     367                 : 
     368                 : /** Return an iterator past the last buffer in a sequence.
     369                 : 
     370                 :     Handles single buffers and ranges uniformly. For a single buffer,
     371                 :     returns a pointer one past it.
     372                 : */
     373                 : constexpr struct end_mrdocs_workaround_t
     374                 : {
     375                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     376           21554 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     377                 :     {
     378           21554 :         return std::addressof(b) + 1;
     379                 :     }
     380                 : 
     381                 :     template<ConstBufferSequence BS>
     382                 :         requires (!std::convertible_to<BS, const_buffer>)
     383           48637 :     auto operator()(BS const& bs) const noexcept
     384                 :     {
     385           48637 :         return std::ranges::end(bs);
     386                 :     }
     387                 : 
     388                 :     template<ConstBufferSequence BS>
     389                 :         requires (!std::convertible_to<BS, const_buffer>)
     390           18574 :     auto operator()(BS& bs) const noexcept
     391                 :     {
     392           18574 :         return std::ranges::end(bs);
     393                 :     }
     394                 : } end {};
     395                 : 
     396                 : //------------------------------------------------------------------------------
     397                 : 
     398                 : template<ConstBufferSequence CB>
     399                 : std::size_t
     400           13738 : tag_invoke(
     401                 :     size_tag const&,
     402                 :     CB const& bs) noexcept
     403                 : {
     404           13738 :     std::size_t n = 0;
     405           13738 :     auto const e = end(bs);
     406           34617 :     for(auto it = begin(bs); it != e; ++it)
     407           20879 :         n += const_buffer(*it).size();
     408           13738 :     return n;
     409                 : }
     410                 : 
     411                 : //------------------------------------------------------------------------------
     412                 : 
     413                 : /** Return the total byte count across all buffers in a sequence.
     414                 : 
     415                 :     Sums the `size()` of each buffer in the sequence. This differs
     416                 :     from `buffer_length` which counts the number of buffer elements.
     417                 : 
     418                 :     @par Example
     419                 :     @code
     420                 :     std::array<mutable_buffer, 2> bufs = { ... };
     421                 :     std::size_t total = buffer_size( bufs );  // sum of both sizes
     422                 :     @endcode
     423                 : */
     424                 : constexpr struct buffer_size_mrdocs_workaround_t
     425                 : {
     426                 :     template<ConstBufferSequence CB>
     427           19237 :     constexpr std::size_t operator()(
     428                 :         CB const& bs) const noexcept
     429                 :     {
     430           19237 :         return tag_invoke(size_tag{}, bs);
     431                 :     }
     432                 : } buffer_size {};
     433                 : 
     434                 : /** Check if a buffer sequence contains no data.
     435                 : 
     436                 :     @return `true` if all buffers have size zero or the sequence
     437                 :         is empty.
     438                 : */
     439                 : constexpr struct buffer_empty_mrdocs_workaround_t
     440                 : {
     441                 :     template<ConstBufferSequence CB>
     442            4156 :     constexpr bool operator()(
     443                 :         CB const& bs) const noexcept
     444                 :     {
     445            4156 :         auto it = begin(bs);
     446            4156 :         auto const end_ = end(bs);
     447            4211 :         while(it != end_)
     448                 :         {
     449            4169 :             const_buffer b(*it++);
     450            4169 :             if(b.size() != 0)
     451            4114 :                 return false;
     452                 :         }
     453              42 :         return true;
     454                 :     }
     455                 : } buffer_empty {};
     456                 : 
     457                 : //-----------------------------------------------
     458                 : 
     459                 : namespace detail {
     460                 : 
     461                 : template<class It>
     462                 : auto
     463             240 : length_impl(It first, It last, int)
     464                 :     -> decltype(static_cast<std::size_t>(last - first))
     465                 : {
     466             240 :     return static_cast<std::size_t>(last - first);
     467                 : }
     468                 : 
     469                 : template<class It>
     470                 : std::size_t
     471                 : length_impl(It first, It last, long)
     472                 : {
     473                 :     std::size_t n = 0;
     474                 :     while(first != last)
     475                 :     {
     476                 :         ++first;
     477                 :         ++n;
     478                 :     }
     479                 :     return n;
     480                 : }
     481                 : 
     482                 : } // detail
     483                 : 
     484                 : /** Return the number of buffer elements in a sequence.
     485                 : 
     486                 :     Counts the number of individual buffer objects, not bytes.
     487                 :     For a single buffer, returns 1. For a range, returns the
     488                 :     distance from `begin` to `end`.
     489                 : 
     490                 :     @see buffer_size
     491                 : */
     492                 : template<ConstBufferSequence CB>
     493                 : std::size_t
     494             240 : buffer_length(CB const& bs)
     495                 : {
     496             240 :     return detail::length_impl(
     497             240 :         begin(bs), end(bs), 0);
     498                 : }
     499                 : 
     500                 : /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
     501                 : template<typename BS>
     502                 : using buffer_type = std::conditional_t<
     503                 :     MutableBufferSequence<BS>,
     504                 :     mutable_buffer, const_buffer>;
     505                 : 
     506                 : } // capy
     507                 : } // boost
     508                 : 
     509                 : #endif
        

Generated by: LCOV version 2.3