LCOV - code coverage report
Current view: top level - capy - buffers.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 93 93
Test Date: 2026-02-02 05:00:52 Functions: 100.0 % 145 145

            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         1165 :     basic_buffer() = default;
      57        55165 :     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          580 :     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        23322 :     constexpr mutable_buffer(
     117              :         void* data, std::size_t size) noexcept
     118        23322 :         : basic_buffer<unsigned char>(
     119        23322 :             static_cast<unsigned char*>(data), size)
     120              :     {
     121        23322 :     }
     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        50007 :     constexpr void* data() const noexcept
     136              :     {
     137        50007 :         return p_;
     138              :     }
     139              : 
     140              :     /// Return the size in bytes.
     141        81834 :     constexpr std::size_t size() const noexcept
     142              :     {
     143        81834 :         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        22270 :     operator+=(std::size_t n) noexcept
     152              :     {
     153        22270 :         if( n > n_)
     154           17 :             n = n_;
     155        22270 :         p_ += n;
     156        22270 :         n_ -= n;
     157        22270 :         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          585 :     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        20475 :     constexpr const_buffer(
     217              :         void const* data, std::size_t size) noexcept
     218        20475 :         : basic_buffer<unsigned char const>(
     219        20475 :             static_cast<unsigned char const*>(data), size)
     220              :     {
     221        20475 :     }
     222              : 
     223              :     /// Construct from mutable_buffer.
     224        11368 :     constexpr const_buffer(
     225              :         mutable_buffer const& b) noexcept
     226        11368 :         : basic_buffer<unsigned char const>(
     227        11368 :             static_cast<unsigned char const*>(b.data()), b.size())
     228              :     {
     229        11368 :     }
     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        47570 :     constexpr void const* data() const noexcept
     245              :     {
     246        47570 :         return p_;
     247              :     }
     248              : 
     249              :     /// Return the size in bytes.
     250        96226 :     constexpr std::size_t size() const noexcept
     251              :     {
     252        96226 :         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        22939 :     operator+=(std::size_t n) noexcept
     261              :     {
     262        22939 :         if( n > n_)
     263           16 :             n = n_;
     264        22939 :         p_ += n;
     265        22939 :         n_ -= n;
     266        22939 :         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        15684 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     349              :     {
     350        15684 :         return std::addressof(b);
     351              :     }
     352              : 
     353              :     template<ConstBufferSequence BS>
     354              :         requires (!std::convertible_to<BS, const_buffer>)
     355        42825 :     auto operator()(BS const& bs) const noexcept
     356              :     {
     357        42825 :         return std::ranges::begin(bs);
     358              :     }
     359              : 
     360              :     template<ConstBufferSequence BS>
     361              :         requires (!std::convertible_to<BS, const_buffer>)
     362         9742 :     auto operator()(BS& bs) const noexcept
     363              :     {
     364         9742 :         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        15444 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     377              :     {
     378        15444 :         return std::addressof(b) + 1;
     379              :     }
     380              : 
     381              :     template<ConstBufferSequence BS>
     382              :         requires (!std::convertible_to<BS, const_buffer>)
     383        35296 :     auto operator()(BS const& bs) const noexcept
     384              :     {
     385        35296 :         return std::ranges::end(bs);
     386              :     }
     387              : 
     388              :     template<ConstBufferSequence BS>
     389              :         requires (!std::convertible_to<BS, const_buffer>)
     390         9742 :     auto operator()(BS& bs) const noexcept
     391              :     {
     392         9742 :         return std::ranges::end(bs);
     393              :     }
     394              : } end {};
     395              : 
     396              : //------------------------------------------------------------------------------
     397              : 
     398              : template<ConstBufferSequence CB>
     399              : std::size_t
     400        13072 : tag_invoke(
     401              :     size_tag const&,
     402              :     CB const& bs) noexcept
     403              : {
     404        13072 :     std::size_t n = 0;
     405        13072 :     auto const e = end(bs);
     406        32961 :     for(auto it = begin(bs); it != e; ++it)
     407        19889 :         n += const_buffer(*it).size();
     408        13072 :     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        13072 :     constexpr std::size_t operator()(
     428              :         CB const& bs) const noexcept
     429              :     {
     430        13072 :         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           20 :     constexpr bool operator()(
     443              :         CB const& bs) const noexcept
     444              :     {
     445           20 :         auto it = begin(bs);
     446           20 :         auto const end_ = end(bs);
     447           33 :         while(it != end_)
     448              :         {
     449           24 :             const_buffer b(*it++);
     450           24 :             if(b.size() != 0)
     451           11 :                 return false;
     452              :         }
     453            9 :         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