LCOV - code coverage report
Current view: top level - capy - buffers.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 65 65
Test Date: 2026-06-08 22:33:24 Functions: 100.0 % 147 147

           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                 : /** A reference to a contiguous region of writable memory.
      36                 : 
      37                 :     Represents a pointer and size pair for a modifiable byte range.
      38                 :     Does not own the memory. Satisfies `MutableBufferSequence` (as a
      39                 :     single-element sequence) and is implicitly convertible to
      40                 :     `const_buffer`.
      41                 : 
      42                 :     @see const_buffer, MutableBufferSequence
      43                 : */
      44                 : class mutable_buffer
      45                 : {
      46                 :     unsigned char* p_ = nullptr;
      47                 :     std::size_t n_ = 0;
      48                 : 
      49                 : public:
      50                 :     /// Construct an empty buffer.
      51 HIT          29 :     mutable_buffer() = default;
      52                 : 
      53                 :     /// Construct a copy.
      54                 :     mutable_buffer(
      55                 :         mutable_buffer const&) = default;
      56                 : 
      57                 :     /// Assign by copying.
      58                 :     mutable_buffer& operator=(
      59                 :         mutable_buffer const&) = default;
      60                 : 
      61                 :     /// Construct from pointer and size.
      62           42582 :     constexpr mutable_buffer(
      63                 :         void* data, std::size_t size) noexcept
      64           42582 :         : p_(static_cast<unsigned char*>(data))
      65           42582 :         , n_(size)
      66                 :     {
      67           42582 :     }
      68                 : 
      69                 :     /// Return a pointer to the memory region.
      70           62337 :     constexpr void* data() const noexcept
      71                 :     {
      72           62337 :         return p_;
      73                 :     }
      74                 : 
      75                 :     /// Return the size in bytes.
      76           97996 :     constexpr std::size_t size() const noexcept
      77                 :     {
      78           97996 :         return n_;
      79                 :     }
      80                 : 
      81                 :     /** Advance the buffer start, shrinking the region.
      82                 : 
      83                 :         @param n Bytes to skip. Clamped to `size()`.
      84                 :     */
      85                 :     mutable_buffer&
      86           19809 :     operator+=(std::size_t n) noexcept
      87                 :     {
      88           19809 :         if( n > n_)
      89               1 :             n = n_;
      90           19809 :         p_ += n;
      91           19809 :         n_ -= n;
      92           19809 :         return *this;
      93                 :     }
      94                 : };
      95                 : 
      96                 : /** A reference to a contiguous region of read-only memory.
      97                 : 
      98                 :     Represents a pointer and size pair for a non-modifiable byte range.
      99                 :     Does not own the memory. Satisfies `ConstBufferSequence` (as a
     100                 :     single-element sequence). Implicitly constructible from
     101                 :     `mutable_buffer`.
     102                 : 
     103                 :     @see mutable_buffer, ConstBufferSequence
     104                 : */
     105                 : class const_buffer
     106                 : {
     107                 :     unsigned char const* p_ = nullptr;
     108                 :     std::size_t n_ = 0;
     109                 : 
     110                 : public:
     111                 :     /// Construct an empty buffer.
     112              57 :     const_buffer() = default;
     113                 : 
     114                 :     /// Construct a copy.
     115                 :     const_buffer(const_buffer const&) = default;
     116                 : 
     117                 :     /// Assign by copying.
     118                 :     const_buffer& operator=(
     119                 :         const_buffer const& other) = default;
     120                 : 
     121                 :     /// Construct from pointer and size.
     122           43427 :     constexpr const_buffer(
     123                 :         void const* data, std::size_t size) noexcept
     124           43427 :         : p_(static_cast<unsigned char const*>(data))
     125           43427 :         , n_(size)
     126                 :     {
     127           43427 :     }
     128                 : 
     129                 :     /// Construct from mutable_buffer.
     130           12005 :     constexpr const_buffer(
     131                 :         mutable_buffer const& b) noexcept
     132           12005 :         : p_(static_cast<unsigned char const*>(b.data()))
     133           12005 :         , n_(b.size())
     134                 :     {
     135           12005 :     }
     136                 : 
     137                 :     /// Return a pointer to the memory region.
     138           59971 :     constexpr void const* data() const noexcept
     139                 :     {
     140           59971 :         return p_;
     141                 :     }
     142                 : 
     143                 :     /// Return the size in bytes.
     144          112898 :     constexpr std::size_t size() const noexcept
     145                 :     {
     146          112898 :         return n_;
     147                 :     }
     148                 : 
     149                 :     /** Advance the buffer start, shrinking the region.
     150                 : 
     151                 :         @param n Bytes to skip. Clamped to `size()`.
     152                 :     */
     153                 :     const_buffer&
     154           19810 :     operator+=(std::size_t n) noexcept
     155                 :     {
     156           19810 :         if( n > n_)
     157               1 :             n = n_;
     158           19810 :         p_ += n;
     159           19810 :         n_ -= n;
     160           19810 :         return *this;
     161                 :     }
     162                 : };
     163                 : 
     164                 : /** Concept for sequences of read-only buffer regions.
     165                 : 
     166                 :     A type satisfies `ConstBufferSequence` if it represents one or more
     167                 :     contiguous memory regions that can be read. This includes single
     168                 :     buffers (convertible to `const_buffer`) and ranges of buffers.
     169                 : 
     170                 :     @par Syntactic Requirements
     171                 :     @li Convertible to `const_buffer`, OR
     172                 :     @li A bidirectional range with value type convertible to `const_buffer`
     173                 : 
     174                 :     @see const_buffer, MutableBufferSequence
     175                 : */
     176                 : template<typename T>
     177                 : concept ConstBufferSequence =
     178                 :     std::is_convertible_v<T, const_buffer> || (
     179                 :         std::ranges::bidirectional_range<T> &&
     180                 :         std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
     181                 : 
     182                 : /** Concept for sequences of writable buffer regions.
     183                 : 
     184                 :     A type satisfies `MutableBufferSequence` if it represents one or more
     185                 :     contiguous memory regions that can be written. This includes single
     186                 :     buffers (convertible to `mutable_buffer`) and ranges of buffers.
     187                 :     Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
     188                 : 
     189                 :     @par Syntactic Requirements
     190                 :     @li Convertible to `mutable_buffer`, OR
     191                 :     @li A bidirectional range with value type convertible to `mutable_buffer`
     192                 : 
     193                 :     @see mutable_buffer, ConstBufferSequence
     194                 : */
     195                 : template<typename T>
     196                 : concept MutableBufferSequence =
     197                 :     std::is_convertible_v<T, mutable_buffer> || (
     198                 :         std::ranges::bidirectional_range<T> &&
     199                 :         std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
     200                 : 
     201                 : namespace detail {
     202                 : struct begin_fn
     203                 : {
     204                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     205           13217 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     206                 :     {
     207           13217 :         return std::addressof(b);
     208                 :     }
     209                 : 
     210                 :     template<ConstBufferSequence BS>
     211                 :         requires (!std::convertible_to<BS, const_buffer>)
     212           46371 :     auto operator()(BS const& bs) const noexcept
     213                 :     {
     214           46371 :         return std::ranges::begin(bs);
     215                 :     }
     216                 : 
     217                 :     template<ConstBufferSequence BS>
     218                 :         requires (!std::convertible_to<BS, const_buffer>)
     219            9192 :     auto operator()(BS& bs) const noexcept
     220                 :     {
     221            9192 :         return std::ranges::begin(bs);
     222                 :     }
     223                 : };
     224                 : } // detail
     225                 : 
     226                 : /** Return an iterator to the first buffer in a sequence.
     227                 : 
     228                 :     Handles single buffers and ranges uniformly. For a single buffer,
     229                 :     returns a pointer to it (forming a one-element range).
     230                 : 
     231                 :     @param bs The buffer sequence.
     232                 : 
     233                 :     @return An iterator to the first buffer in the sequence.
     234                 : */
     235                 : constexpr detail::begin_fn begin {};
     236                 : 
     237                 : namespace detail {
     238                 : struct end_fn
     239                 : {
     240                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     241           12955 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     242                 :     {
     243           12955 :         return std::addressof(b) + 1;
     244                 :     }
     245                 : 
     246                 :     template<ConstBufferSequence BS>
     247                 :         requires (!std::convertible_to<BS, const_buffer>)
     248           46371 :     auto operator()(BS const& bs) const noexcept
     249                 :     {
     250           46371 :         return std::ranges::end(bs);
     251                 :     }
     252                 : 
     253                 :     template<ConstBufferSequence BS>
     254                 :         requires (!std::convertible_to<BS, const_buffer>)
     255            9192 :     auto operator()(BS& bs) const noexcept
     256                 :     {
     257            9192 :         return std::ranges::end(bs);
     258                 :     }
     259                 : };
     260                 : } // detail
     261                 : 
     262                 : /** Return an iterator past the last buffer in a sequence.
     263                 : 
     264                 :     Handles single buffers and ranges uniformly. For a single buffer,
     265                 :     returns a pointer one past it.
     266                 : 
     267                 :     @param bs The buffer sequence.
     268                 : 
     269                 :     @return An iterator one past the last buffer in the sequence.
     270                 : */
     271                 : constexpr detail::end_fn end {};
     272                 : 
     273                 : namespace detail {
     274                 : struct buffer_size_fn
     275                 : {
     276                 :     // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
     277                 :     // when iterating here. The class uses union storage with placement
     278                 :     // new for slots 0..n_-1, so reads inside this bounded loop are
     279                 :     // well-defined, but the optimizer can't prove the loop bound and
     280                 :     // warns. The runtime cost of value-initializing all N slots is
     281                 :     // non-trivial for non-trivial value types, so we suppress instead.
     282                 : #if defined(__GNUC__) && !defined(__clang__)
     283                 : #pragma GCC diagnostic push
     284                 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
     285                 : #endif
     286                 :     template<ConstBufferSequence CB>
     287           12824 :     constexpr std::size_t operator()(
     288                 :         CB const& bs) const noexcept
     289                 :     {
     290           12824 :         std::size_t n = 0;
     291           12824 :         auto const e = capy::end(bs);
     292           27175 :         for(auto it = capy::begin(bs); it != e; ++it)
     293           14351 :             n += const_buffer(*it).size();
     294           12824 :         return n;
     295                 :     }
     296                 : #if defined(__GNUC__) && !defined(__clang__)
     297                 : #pragma GCC diagnostic pop
     298                 : #endif
     299                 : };
     300                 : } // detail
     301                 : 
     302                 : /** Return the total byte count across all buffers in a sequence.
     303                 : 
     304                 :     Sums the `size()` of each buffer in the sequence. This differs
     305                 :     from `buffer_length` which counts the number of buffer elements.
     306                 : 
     307                 :     @param bs The buffer sequence.
     308                 : 
     309                 :     @return The total number of bytes across all buffers in the sequence.
     310                 : 
     311                 :     @par Example
     312                 :     @code
     313                 :     std::array<mutable_buffer, 2> bufs = { ... };
     314                 :     std::size_t total = buffer_size( bufs );  // sum of both sizes
     315                 :     @endcode
     316                 : */
     317                 : constexpr detail::buffer_size_fn buffer_size {};
     318                 : 
     319                 : namespace detail {
     320                 : struct buffer_empty_fn
     321                 : {
     322                 :     // See note on buffer_size above — same union-storage false positive.
     323                 : #if defined(__GNUC__) && !defined(__clang__)
     324                 : #pragma GCC diagnostic push
     325                 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
     326                 : #endif
     327                 :     template<ConstBufferSequence CB>
     328            4263 :     constexpr bool operator()(
     329                 :         CB const& bs) const noexcept
     330                 :     {
     331            4263 :         auto it = begin(bs);
     332            4263 :         auto const end_ = end(bs);
     333            4304 :         while(it != end_)
     334                 :         {
     335            4264 :             const_buffer b(*it++);
     336            4264 :             if(b.size() != 0)
     337            4223 :                 return false;
     338                 :         }
     339              40 :         return true;
     340                 :     }
     341                 : #if defined(__GNUC__) && !defined(__clang__)
     342                 : #pragma GCC diagnostic pop
     343                 : #endif
     344                 : };
     345                 : } // detail
     346                 : 
     347                 : /** Check if a buffer sequence contains no data.
     348                 : 
     349                 :     @param bs The buffer sequence.
     350                 : 
     351                 :     @return `true` if all buffers have size zero or the sequence
     352                 :         is empty.
     353                 : */
     354                 : constexpr detail::buffer_empty_fn buffer_empty {};
     355                 : 
     356                 : namespace detail {
     357                 : 
     358                 : template<class It>
     359                 : auto
     360             263 : length_impl(It first, It last, int)
     361                 :     -> decltype(static_cast<std::size_t>(last - first))
     362                 : {
     363             263 :     return static_cast<std::size_t>(last - first);
     364                 : }
     365                 : 
     366                 : template<class It>
     367                 : std::size_t
     368                 : length_impl(It first, It last, long)
     369                 : {
     370                 :     std::size_t n = 0;
     371                 :     while(first != last)
     372                 :     {
     373                 :         ++first;
     374                 :         ++n;
     375                 :     }
     376                 :     return n;
     377                 : }
     378                 : 
     379                 : } // detail
     380                 : 
     381                 : /** Return the number of buffer elements in a sequence.
     382                 : 
     383                 :     Counts the number of individual buffer objects, not bytes.
     384                 :     For a single buffer, returns 1. For a range, returns the
     385                 :     distance from `begin` to `end`.
     386                 : 
     387                 :     @see buffer_size
     388                 : */
     389                 : template<ConstBufferSequence CB>
     390                 : std::size_t
     391             263 : buffer_length(CB const& bs)
     392                 : {
     393             263 :     return detail::length_impl(
     394             263 :         begin(bs), end(bs), 0);
     395                 : }
     396                 : 
     397                 : /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
     398                 : template<typename BS>
     399                 : using buffer_type = std::conditional_t<
     400                 :     MutableBufferSequence<BS>,
     401                 :     mutable_buffer, const_buffer>;
     402                 : 
     403                 : } // capy
     404                 : } // boost
     405                 : 
     406                 : #endif
        

Generated by: LCOV version 2.3