LCOV - code coverage report
Current view: top level - capy - buffers.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 65 65
Test Date: 2026-08-14 20:51:18 Functions: 90.3 % 247 223 24

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/capy
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_CAPY_BUFFERS_HPP
      12                 : #define BOOST_CAPY_BUFFERS_HPP
      13                 : 
      14                 : #include <boost/capy/detail/config.hpp>
      15                 : #include <concepts>
      16                 : #include <cstddef>
      17                 : #include <iterator>
      18                 : #include <memory>
      19                 : #include <ranges>
      20                 : #include <type_traits>
      21                 : 
      22                 : // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
      23                 : 
      24                 : namespace boost {
      25                 : 
      26                 : namespace asio {
      27                 : class const_buffer;
      28                 : class mutable_buffer;
      29                 : } // asio
      30                 : 
      31                 : namespace capy {
      32                 : 
      33                 : class const_buffer;
      34                 : class mutable_buffer;
      35                 : 
      36                 : /** A reference to a contiguous region of writable memory.
      37                 : 
      38                 :     Represents a pointer and size pair for a modifiable byte range.
      39                 :     Does not own the memory. Satisfies `MutableBufferSequence` (as a
      40                 :     single-element sequence) and is implicitly convertible to
      41                 :     `const_buffer`.
      42                 : 
      43                 :     @see const_buffer, MutableBufferSequence
      44                 : */
      45                 : class mutable_buffer
      46                 : {
      47                 :     unsigned char* p_ = nullptr;
      48                 :     std::size_t n_ = 0;
      49                 : 
      50                 : public:
      51                 :     /// Construct an empty buffer.
      52 HIT          19 :     mutable_buffer() = default;
      53                 : 
      54                 :     /** Construct a copy.
      55                 : 
      56                 :         @param other The buffer to copy.
      57                 :     */
      58                 :     mutable_buffer(
      59                 :         mutable_buffer const& other) = default;
      60                 : 
      61                 :     /** Assign by copying.
      62                 : 
      63                 :         @param other The buffer to copy.
      64                 : 
      65                 :         @return A reference to `*this`.
      66                 :     */
      67                 :     mutable_buffer& operator=(
      68                 :         mutable_buffer const& other) = default;
      69                 : 
      70                 :     /** Construct from a pointer and size.
      71                 : 
      72                 :         Takes `void*` so a pointer to any object type binds without a
      73                 :         cast, since the buffer represents a raw, untyped writable
      74                 :         region. Stored internally as `unsigned char*` for byte-wise
      75                 :         pointer arithmetic (see `operator+=`).
      76                 : 
      77                 :         @param data A pointer to the first byte of the region.
      78                 : 
      79                 :         @param size The size of the region, in bytes.
      80                 :     */
      81           35281 :     constexpr mutable_buffer(
      82                 :         void* data, std::size_t size) noexcept
      83           35281 :         : p_(static_cast<unsigned char*>(data))
      84           35281 :         , n_(size)
      85                 :     {
      86           35281 :     }
      87                 : 
      88                 :     /** Return a pointer to the memory region.
      89                 : 
      90                 :         Returns `void*`, symmetric with the constructor, so the
      91                 :         caller can reinterpret the raw region as whatever type it needs.
      92                 : 
      93                 :         @return A pointer to the first byte of the region.
      94                 :     */
      95           54037 :     constexpr void* data() const noexcept
      96                 :     {
      97           54037 :         return p_;
      98                 :     }
      99                 : 
     100                 :     /** Return the size in bytes.
     101                 : 
     102                 :         @return The size of the region, in bytes.
     103                 :     */
     104           80565 :     constexpr std::size_t size() const noexcept
     105                 :     {
     106           80565 :         return n_;
     107                 :     }
     108                 : 
     109                 :     /** Advance the buffer start, shrinking the region.
     110                 : 
     111                 :         @param n Bytes to skip. Clamped to `size()`.
     112                 : 
     113                 :         @return A reference to `*this`.
     114                 :     */
     115                 :     mutable_buffer&
     116           17732 :     operator+=(std::size_t n) noexcept
     117                 :     {
     118           17732 :         if( n > n_)
     119               1 :             n = n_;
     120           17732 :         p_ += n;
     121           17732 :         n_ -= n;
     122           17732 :         return *this;
     123                 :     }
     124                 : };
     125                 : 
     126                 : /** A reference to a contiguous region of read-only memory.
     127                 : 
     128                 :     Represents a pointer and size pair for a non-modifiable byte range.
     129                 :     Does not own the memory. Satisfies `ConstBufferSequence` (as a
     130                 :     single-element sequence). Implicitly constructible from
     131                 :     `mutable_buffer`.
     132                 : 
     133                 :     @see mutable_buffer, ConstBufferSequence
     134                 : */
     135                 : class const_buffer
     136                 : {
     137                 :     unsigned char const* p_ = nullptr;
     138                 :     std::size_t n_ = 0;
     139                 : 
     140                 : public:
     141                 :     /// Construct an empty buffer.
     142              13 :     const_buffer() = default;
     143                 : 
     144                 :     /** Construct a copy.
     145                 : 
     146                 :         @param other The buffer to copy.
     147                 :     */
     148                 :     const_buffer(const_buffer const& other) = default;
     149                 : 
     150                 :     /** Assign by copying.
     151                 : 
     152                 :         @param other The buffer to copy.
     153                 : 
     154                 :         @return A reference to `*this`.
     155                 :     */
     156                 :     const_buffer& operator=(
     157                 :         const_buffer const& other) = default;
     158                 : 
     159                 :     /** Construct from a pointer and size.
     160                 : 
     161                 :         Takes `void const*` so a pointer to any object type binds
     162                 :         without a cast, since the buffer represents a raw, untyped
     163                 :         read-only region. Stored internally as `unsigned char const*`
     164                 :         for byte-wise pointer arithmetic (see `operator+=`).
     165                 : 
     166                 :         @param data A pointer to the first byte of the region.
     167                 : 
     168                 :         @param size The size of the region, in bytes.
     169                 :     */
     170           32088 :     constexpr const_buffer(
     171                 :         void const* data, std::size_t size) noexcept
     172           32088 :         : p_(static_cast<unsigned char const*>(data))
     173           32088 :         , n_(size)
     174                 :     {
     175           32088 :     }
     176                 : 
     177                 :     /** Construct from mutable_buffer.
     178                 : 
     179                 :         @param b The writable buffer whose region is referenced.
     180                 :     */
     181            7887 :     constexpr const_buffer(
     182                 :         mutable_buffer const& b) noexcept
     183            7887 :         : p_(static_cast<unsigned char const*>(b.data()))
     184            7887 :         , n_(b.size())
     185                 :     {
     186            7887 :     }
     187                 : 
     188                 :     /** Return a pointer to the memory region.
     189                 : 
     190                 :         Returns `void const*`, symmetric with the constructor, so the
     191                 :         caller can reinterpret the raw region as whatever type it needs.
     192                 : 
     193                 :         @return A pointer to the first byte of the region.
     194                 :     */
     195           46527 :     constexpr void const* data() const noexcept
     196                 :     {
     197           46527 :         return p_;
     198                 :     }
     199                 : 
     200                 :     /** Return the size in bytes.
     201                 : 
     202                 :         @return The size of the region, in bytes.
     203                 :     */
     204           77663 :     constexpr std::size_t size() const noexcept
     205                 :     {
     206           77663 :         return n_;
     207                 :     }
     208                 : 
     209                 :     /** Advance the buffer start, shrinking the region.
     210                 : 
     211                 :         @param n Bytes to skip. Clamped to `size()`.
     212                 : 
     213                 :         @return A reference to `*this`.
     214                 :     */
     215                 :     const_buffer&
     216           17380 :     operator+=(std::size_t n) noexcept
     217                 :     {
     218           17380 :         if( n > n_)
     219               1 :             n = n_;
     220           17380 :         p_ += n;
     221           17380 :         n_ -= n;
     222           17380 :         return *this;
     223                 :     }
     224                 : };
     225                 : 
     226                 : /** Requires a type to convert to `const_buffer`, or be a range of such buffers.
     227                 : 
     228                 :     A type satisfies `ConstBufferSequence` if it represents one or more
     229                 :     contiguous memory regions that can be read. This includes single
     230                 :     buffers (convertible to `const_buffer`) and ranges of buffers.
     231                 : 
     232                 :     @par Syntactic Requirements
     233                 :     @li Convertible to `const_buffer`, OR
     234                 :     @li A bidirectional range with value type convertible to `const_buffer`
     235                 : 
     236                 :     @see const_buffer, MutableBufferSequence
     237                 : */
     238                 : template<typename T>
     239                 : concept ConstBufferSequence =
     240                 :     std::is_convertible_v<T, const_buffer> || (
     241                 :         std::ranges::bidirectional_range<T> &&
     242                 :         std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
     243                 : 
     244                 : /** Requires a type to convert to `mutable_buffer`, or be a range of such buffers.
     245                 : 
     246                 :     A type satisfies `MutableBufferSequence` if it represents one or more
     247                 :     contiguous memory regions that can be written. This includes single
     248                 :     buffers (convertible to `mutable_buffer`) and ranges of buffers.
     249                 : 
     250                 :     This does not imply `ConstBufferSequence`. A type reaching
     251                 :     `mutable_buffer` through its own conversion operator would need a
     252                 :     second conversion, to `const_buffer`. An implicit conversion
     253                 :     sequence allows only one user-defined step.
     254                 : 
     255                 :     @par Syntactic Requirements
     256                 :     @li Convertible to `mutable_buffer`, OR
     257                 :     @li A bidirectional range with value type convertible to `mutable_buffer`
     258                 : 
     259                 :     @see mutable_buffer, ConstBufferSequence
     260                 : */
     261                 : template<typename T>
     262                 : concept MutableBufferSequence =
     263                 :     std::is_convertible_v<T, mutable_buffer> || (
     264                 :         std::ranges::bidirectional_range<T> &&
     265                 :         std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
     266                 : 
     267                 : /** Return an iterator to the first buffer in a sequence.
     268                 : 
     269                 :     @functionobject
     270                 : */
     271                 : constexpr struct
     272                 : {
     273                 :     /** Return a pointer to a single buffer, forming a one-element range.
     274                 : 
     275                 :         @param b A single buffer.
     276                 : 
     277                 :         @return A pointer to `b`.
     278                 :     */
     279                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     280            6664 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     281                 :     {
     282            6664 :         return std::addressof(b);
     283                 :     }
     284                 : 
     285                 :     /** Return an iterator to the first buffer of a sequence.
     286                 : 
     287                 :         @param bs The buffer sequence.
     288                 : 
     289                 :         @return An iterator to the first buffer of `bs`.
     290                 :     */
     291                 :     template<ConstBufferSequence BS>
     292                 :         requires (!std::convertible_to<BS, const_buffer>)
     293           33709 :     auto operator()(BS const& bs) const noexcept
     294                 :     {
     295           33709 :         return std::ranges::begin(bs);
     296                 :     }
     297                 : 
     298                 :     /** Return an iterator to the first buffer of a sequence.
     299                 : 
     300                 :         @param bs The buffer sequence.
     301                 : 
     302                 :         @return An iterator to the first buffer of `bs`.
     303                 :     */
     304                 :     template<ConstBufferSequence BS>
     305                 :         requires (!std::convertible_to<BS, const_buffer>)
     306            9193 :     auto operator()(BS& bs) const noexcept
     307                 :     {
     308            9193 :         return std::ranges::begin(bs);
     309                 :     }
     310                 : } begin {};
     311                 : 
     312                 : /** Return an iterator past the last buffer in a sequence.
     313                 : 
     314                 :     @functionobject
     315                 : */
     316                 : constexpr struct
     317                 : {
     318                 :     /** Return a pointer one past a single buffer, forming a one-element range.
     319                 : 
     320                 :         @param b A single buffer.
     321                 : 
     322                 :         @return A pointer one past `b`.
     323                 :     */
     324                 :     template<std::convertible_to<const_buffer> ConvertibleToBuffer>
     325            6666 :     auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
     326                 :     {
     327            6666 :         return std::addressof(b) + 1;
     328                 :     }
     329                 : 
     330                 :     /** Return an iterator past the last buffer of a sequence.
     331                 : 
     332                 :         @param bs The buffer sequence.
     333                 : 
     334                 :         @return An iterator one past the last buffer of `bs`.
     335                 :     */
     336                 :     template<ConstBufferSequence BS>
     337                 :         requires (!std::convertible_to<BS, const_buffer>)
     338           33731 :     auto operator()(BS const& bs) const noexcept
     339                 :     {
     340           33731 :         return std::ranges::end(bs);
     341                 :     }
     342                 : 
     343                 :     /** Return an iterator past the last buffer of a sequence.
     344                 : 
     345                 :         @param bs The buffer sequence.
     346                 : 
     347                 :         @return An iterator one past the last buffer of `bs`.
     348                 :     */
     349                 :     template<ConstBufferSequence BS>
     350                 :         requires (!std::convertible_to<BS, const_buffer>)
     351            9193 :     auto operator()(BS& bs) const noexcept
     352                 :     {
     353            9193 :         return std::ranges::end(bs);
     354                 :     }
     355                 : } end {};
     356                 : 
     357                 : /** Return the total byte count across all buffers in a sequence.
     358                 : 
     359                 :     @functionobject
     360                 : */
     361                 : constexpr struct
     362                 : {
     363                 :     // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
     364                 :     // when iterating here. The class uses union storage with placement
     365                 :     // new for slots 0..n_-1, so reads inside this bounded loop are
     366                 :     // well-defined, but the optimizer can't prove the loop bound and
     367                 :     // warns. The runtime cost of value-initializing all N slots is
     368                 :     // non-trivial for non-trivial value types, so we suppress instead.
     369                 : #if defined(__GNUC__) && !defined(__clang__)
     370                 : #pragma GCC diagnostic push
     371                 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
     372                 : #endif
     373                 :     /** Return the total byte count across all buffers in a sequence.
     374                 : 
     375                 :         Sums the `size()` of each buffer in the sequence. This differs
     376                 :         from `buffer_length` which counts the number of buffer elements.
     377                 : 
     378                 :         @param bs The buffer sequence.
     379                 : 
     380                 :         @return The sum of the sizes of all buffers in `bs`.
     381                 : 
     382                 :         @par Example
     383                 :         @code
     384                 :         std::array<mutable_buffer, 2> bufs = { ... };
     385                 :         std::size_t total = buffer_size( bufs );  // sum of both sizes
     386                 :         @endcode
     387                 :     */
     388                 :     template<ConstBufferSequence CB>
     389            6296 :     constexpr std::size_t operator()(
     390                 :         CB const& bs) const noexcept
     391                 :     {
     392            6296 :         std::size_t n = 0;
     393            6296 :         auto const e = capy::end(bs);
     394           14520 :         for(auto it = capy::begin(bs); it != e; ++it)
     395            8224 :             n += const_buffer(*it).size();
     396            6296 :         return n;
     397                 :     }
     398                 : #if defined(__GNUC__) && !defined(__clang__)
     399                 : #pragma GCC diagnostic pop
     400                 : #endif
     401                 : } buffer_size {};
     402                 : 
     403                 : /** Check if a buffer sequence contains no data.
     404                 : 
     405                 :     @functionobject
     406                 : */
     407                 : constexpr struct
     408                 : {
     409                 :     // See note on buffer_size above — same union-storage false positive.
     410                 : #if defined(__GNUC__) && !defined(__clang__)
     411                 : #pragma GCC diagnostic push
     412                 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
     413                 : #endif
     414                 :     /** Check if a buffer sequence contains no data.
     415                 : 
     416                 :         @param bs The buffer sequence.
     417                 : 
     418                 :         @return `true` if all buffers have size zero or the sequence
     419                 :             is empty.
     420                 :     */
     421                 :     template<ConstBufferSequence CB>
     422            1584 :     constexpr bool operator()(
     423                 :         CB const& bs) const noexcept
     424                 :     {
     425            1584 :         auto it = begin(bs);
     426            1584 :         auto const end_ = end(bs);
     427            1632 :         while(it != end_)
     428                 :         {
     429            1596 :             const_buffer b(*it++);
     430            1596 :             if(b.size() != 0)
     431            1548 :                 return false;
     432                 :         }
     433              36 :         return true;
     434                 :     }
     435                 : #if defined(__GNUC__) && !defined(__clang__)
     436                 : #pragma GCC diagnostic pop
     437                 : #endif
     438                 : } buffer_empty {};
     439                 : 
     440                 : namespace detail {
     441                 : 
     442                 : template<class It>
     443                 : auto
     444              11 : length_impl(It first, It last, int)
     445                 :     -> decltype(static_cast<std::size_t>(last - first))
     446                 : {
     447              11 :     return static_cast<std::size_t>(last - first);
     448                 : }
     449                 : 
     450                 : template<class It>
     451                 : std::size_t
     452                 : length_impl(It first, It last, long)
     453                 : {
     454                 :     std::size_t n = 0;
     455                 :     while(first != last)
     456                 :     {
     457                 :         ++first;
     458                 :         ++n;
     459                 :     }
     460                 :     return n;
     461                 : }
     462                 : 
     463                 : } // detail
     464                 : 
     465                 : /** Return the number of buffer elements in a sequence.
     466                 : 
     467                 :     Counts the number of individual buffer objects, not bytes.
     468                 :     For a single buffer, returns 1. For a range, returns the
     469                 :     distance from `begin` to `end`.
     470                 : 
     471                 :     @param bs The buffer sequence.
     472                 : 
     473                 :     @return The number of buffers in `bs`.
     474                 : 
     475                 :     @see buffer_size
     476                 : */
     477                 : template<ConstBufferSequence CB>
     478                 : std::size_t
     479              11 : buffer_length(CB const& bs)
     480                 : {
     481              11 :     return detail::length_impl(
     482              11 :         begin(bs), end(bs), 0);
     483                 : }
     484                 : 
     485                 : /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise.
     486                 : template<typename BS>
     487                 : using buffer_type = std::conditional_t<
     488                 :     MutableBufferSequence<BS>,
     489                 :     mutable_buffer, const_buffer>;
     490                 : 
     491                 : } // capy
     492                 : } // boost
     493                 : 
     494                 : #endif
        

Generated by: LCOV version 2.3