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-10-30 18:56:53 Functions: 92.0 % 274 252 22

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

Generated by: LCOV version 2.3