LCOV - code coverage report
Current view: top level - boost/buffers - any_buffers.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 89.6 % 154 138
Test Date: 2025-12-06 02:12:43 Functions: 78.5 % 158 124

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/buffers
       8              : //
       9              : 
      10              : #ifndef BOOST_BUFFERS_ANY_BUFFERS_HPP
      11              : #define BOOST_BUFFERS_ANY_BUFFERS_HPP
      12              : 
      13              : #include <boost/buffers/detail/config.hpp>
      14              : #include <boost/buffers/buffer.hpp>
      15              : #include <boost/core/detail/static_assert.hpp>
      16              : #include <boost/assert.hpp>
      17              : #include <atomic>
      18              : #include <cstddef>
      19              : #include <new>
      20              : #include <type_traits>
      21              : 
      22              : namespace boost {
      23              : namespace buffers {
      24              : 
      25              : /** A type-erased buffer sequence.
      26              : 
      27              :     This class template wraps any buffer sequence and
      28              :     exposes it through a uniform interface, hiding the
      29              :     concrete type. Iteration is performed via a type-erased
      30              :     bidirectional iterator.
      31              : 
      32              :     The implementation uses small buffer optimization (SBO)
      33              :     for iterators that are small, trivially aligned, and
      34              :     nothrow copy constructible. Larger iterators fall back
      35              :     to an index-based traversal strategy.
      36              : 
      37              :     @tparam IsConst If `true`, the sequence yields
      38              :     @ref const_buffer elements. If `false`, it yields
      39              :     @ref mutable_buffer elements.
      40              : 
      41              :     @see any_const_buffers, any_mutable_buffers
      42              : */
      43              : template<bool IsConst>
      44              : class any_buffers
      45              : {
      46              : public:
      47              :     /** The buffer type returned when dereferencing iterators.
      48              : 
      49              :         This is @ref const_buffer when `IsConst` is `true`,
      50              :         otherwise @ref mutable_buffer.
      51              :     */
      52              :     using value_type = typename std::conditional<
      53              :         IsConst, const_buffer, mutable_buffer>::type;
      54              : 
      55              :     /** A bidirectional iterator over the buffer sequence.
      56              : 
      57              :         @see begin, end
      58              :     */
      59              :     class const_iterator;
      60              : 
      61              :     /** Destructor.
      62              :     */
      63           22 :     ~any_buffers()
      64              :     {
      65           22 :         p_->destroy();
      66           22 :     }
      67              : 
      68              :     /** Constructor.
      69              :         Default-constructed objects are empty with zero length.
      70              :     */
      71              :     any_buffers() noexcept;
      72              : 
      73              :     /** Constructor.
      74              :     */
      75            7 :     any_buffers(
      76              :         any_buffers const& other) noexcept
      77            7 :     {
      78            7 :         other.p_->copy(*this);
      79            7 :     }
      80              : 
      81              :     /** Assignment.
      82              :     */
      83              :     any_buffers&
      84              :     operator=(
      85              :         any_buffers const& other) noexcept
      86              :     {
      87              :         if(this == &other)
      88              :             return *this;
      89              :         p_->destroy();
      90              :         other.p_->copy(*this);
      91              :         return *this;
      92              :     }
      93              : 
      94              :     /** Constructor.
      95              : 
      96              :         The type-erased buffer sequence is constructed
      97              :         from the specified buffer sequence, which must satisfy
      98              :         `ConstBufferSequence`. If `IsConst` is `false`, must
      99              :         also satisfy `MutableBufferSequence`.
     100              : 
     101              :         @param buffers The buffer sequence to type-erase.
     102              :     */
     103              :     template<class BufferSequence
     104              :         , class = typename std::enable_if<! std::is_same<
     105              :             any_buffers, typename std::decay<BufferSequence
     106              :                 >::type>::value>::type>
     107           11 :     any_buffers(
     108              :         BufferSequence&& buffers)
     109           11 :     {
     110              :         using T = typename std::decay<BufferSequence>::type;
     111           11 :         construct(std::forward<BufferSequence>(buffers),
     112              :             std::integral_constant<bool, (
     113              :                 sizeof(impl<T>) <= sbo_size)>{});
     114           11 :     }
     115              : 
     116              :     /** Return an iterator to the beginning.
     117              : 
     118              :         @return An iterator pointing to the first buffer,
     119              :         or `end()` if the sequence is empty.
     120              :     */
     121              :     const_iterator begin() const noexcept;
     122              : 
     123              :     /** Return an iterator to the end.
     124              : 
     125              :         @return An iterator pointing one past the last buffer.
     126              :     */
     127              :     const_iterator end() const noexcept;
     128              : 
     129              : private:
     130              :     friend struct any_buffers_test;
     131              : 
     132              :     static constexpr std::size_t sbo_size = 6 * sizeof(void*);
     133              : 
     134              :     static constexpr std::size_t iter_sbo_size = 4 * sizeof(void*);
     135              : 
     136              :     struct BOOST_SYMBOL_VISIBLE
     137              :         any_impl
     138              :     {
     139           15 :         virtual ~any_impl() = default;
     140              :         virtual bool is_small_buffers() const noexcept = 0;
     141              :         virtual bool is_small_iter() const noexcept = 0;
     142              :         virtual void destroy() const = 0;
     143              :         virtual void copy(any_buffers& dest) const = 0;
     144              :         virtual void it_copy(void*, void const*) const = 0;
     145              :         virtual void it_destroy(void*) const = 0;
     146              :         virtual void inc(void*) const = 0;
     147              :         virtual void dec(void*) const = 0;
     148              :         virtual auto deref(void const*) const -> value_type = 0;
     149              :         virtual bool equal(void const*, void const*) const = 0;
     150              :         virtual void begin(void*) const = 0;
     151              :         virtual void end(void*) const = 0;
     152              :     };
     153              : 
     154              :     template<class T, bool IsIterSmall = (sizeof(decltype(
     155              :         buffers::begin(std::declval<T const>()))) <= iter_sbo_size)>
     156              :     struct impl;
     157              : 
     158              :     // small buffer sequence
     159              :     template<class T>
     160            9 :     void construct(T&& t, std::true_type)
     161              :     {
     162              :         using U = typename std::decay<T>::type;
     163            9 :         p_ = ::new(&storage_) impl<U>(
     164              :             std::forward<T>(t));
     165            9 :     }
     166              : 
     167              :     template<class T>
     168            2 :     void construct(T&& t, std::false_type)
     169              :     {
     170              :         using U = typename std::decay<T>::type;
     171            2 :         p_ = new impl<U>(std::forward<T>(t));
     172            2 :     }
     173              : 
     174            7 :     bool is_small_buffers() const noexcept
     175              :     {
     176            7 :         return p_->is_small_buffers();
     177              :     }
     178              : 
     179            7 :     bool is_small_iter() const noexcept
     180              :     {
     181            7 :         return p_->is_small_iter();
     182              :     }
     183              : 
     184              :     alignas(std::max_align_t)
     185              :         unsigned char mutable storage_[sbo_size] = {};
     186              :     any_impl const* p_ = nullptr;
     187              : };
     188              : 
     189              : //-----------------------------------------------
     190              : 
     191              : /** Alias for a type-erased const buffer sequence.
     192              : 
     193              :     Equivalent to `any_buffers<true>`.
     194              : 
     195              :     @see any_buffers, any_mutable_buffers
     196              : */
     197              : using any_const_buffers = any_buffers<true>;
     198              : 
     199              : /** Alias for a type-erased mutable buffer sequence.
     200              : 
     201              :     Equivalent to `any_buffers<false>`.
     202              : 
     203              :     @see any_buffers, any_const_buffers
     204              : */
     205              : using any_mutable_buffers = any_buffers<false>;
     206              : 
     207              : //-----------------------------------------------
     208              : 
     209              : // small iterator
     210              : template<bool IsConst>
     211              : template<class T, bool>
     212              : struct any_buffers<IsConst>::
     213              :     impl : any_impl
     214              : {
     215              :     using iter_t = decltype(buffers::begin(
     216              :         std::declval<T const&>()));
     217              : 
     218              :     template<class T_>
     219           11 :     explicit impl(T_&& t) noexcept
     220           11 :         : t_(std::forward<T_>(t))
     221              :     {
     222           11 :     }
     223              : 
     224            3 :     bool is_small_buffers() const noexcept override
     225              :     {
     226            3 :         return sizeof(*this) <= sbo_size;
     227              :     }
     228              : 
     229            3 :     bool is_small_iter() const noexcept override
     230              :     {
     231            3 :         return true;
     232              :     }
     233              : 
     234           11 :     void destroy() const override
     235              :     {
     236           11 :         destroy(std::integral_constant<bool,
     237              :             sizeof(*this) <= sbo_size>{});
     238           11 :     }
     239              : 
     240           11 :     void destroy(std::true_type) const // small buffers
     241              :     {
     242           11 :         this->~impl();
     243           11 :     }
     244              : 
     245              :     void destroy(std::false_type) const
     246              :     {
     247              :         if(--refs_ == 0)
     248              :             delete this;
     249              :     }
     250              : 
     251            2 :     void copy(any_buffers& dest) const override
     252              :     {
     253            2 :         copy(dest, std::integral_constant<bool,
     254              :             sizeof(*this) <= sbo_size>{});
     255            2 :     }
     256              : 
     257            2 :     void copy(any_buffers& dest, std::true_type) const // small buffers
     258              :     {
     259            2 :         dest.p_ = ::new(&dest.storage_) impl<T>(t_);
     260            2 :     }
     261              : 
     262              :     void copy(any_buffers& dest, std::false_type) const
     263              :     {
     264              :         ++refs_;
     265              :         dest.p_ = this;
     266              :     }
     267              : 
     268            0 :     void it_copy(void* dest, void const* src) const override
     269              :     {
     270            0 :         ::new(dest) iter_t(*static_cast<iter_t const*>(src));
     271            0 :     }
     272              : 
     273           42 :     void it_destroy(void* p) const override
     274              :     {
     275           42 :         static_cast<iter_t*>(p)->~iter_t();
     276           42 :     }
     277              : 
     278           26 :     void inc(void* p) const override
     279              :     {
     280           26 :         ++(*static_cast<iter_t*>(p));
     281           26 :     }
     282              : 
     283            0 :     void dec(void* p) const  override
     284              :     {
     285            0 :         --(*static_cast<iter_t*>(p));
     286            0 :     }
     287              : 
     288           19 :     value_type deref(void const* p) const override
     289              :     {
     290           19 :         return *(*static_cast<iter_t const*>(p));
     291              :     }
     292              : 
     293           46 :     bool equal(void const* it0, void const* it1) const override
     294              :     {
     295           46 :         return  *static_cast<iter_t const*>(it0) ==
     296           46 :                 *static_cast<iter_t const*>(it1);
     297              :     }
     298              : 
     299           21 :     void begin(void* p) const override
     300              :     {
     301           21 :         ::new(p) iter_t(buffers::begin(t_));
     302           21 :     }
     303              : 
     304           21 :     void end(void* p) const override
     305              :     {
     306           21 :         ::new(p) iter_t(buffers::end(t_));
     307           21 :     }
     308              : 
     309              : private:
     310              :     T t_;
     311              :     std::atomic<std::size_t> mutable refs_{1};
     312              : };
     313              : 
     314              : template<bool IsConst>
     315              : template<class T>
     316              : struct any_buffers<IsConst>::
     317              :     impl<T, false> : any_impl
     318              : {
     319              :     struct iter_t
     320              :     {
     321              :         std::size_t i;
     322              :     };
     323              : 
     324              :     template<class T_>
     325            2 :     explicit impl(T_&& t) noexcept
     326            3 :         : t_(std::forward<T_>(t))
     327            3 :         , len_(length(t_))
     328              :     {
     329            2 :     }
     330              : 
     331            1 :     bool is_small_buffers() const noexcept override
     332              :     {
     333              :         return sizeof(*this) <=
     334            1 :             any_buffers<IsConst>::sbo_size;
     335              :     }
     336              : 
     337            1 :     bool is_small_iter() const noexcept override
     338              :     {
     339            1 :         return false;
     340              :     }
     341              : 
     342            3 :     void destroy() const override
     343              :     {
     344            3 :         destroy(std::integral_constant<bool,
     345              :             sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
     346            3 :     }
     347              : 
     348              :     void destroy(std::true_type) const // small buffers
     349              :     {
     350              :         this->~impl();
     351              :     }
     352              : 
     353            3 :     void destroy(std::false_type) const
     354              :     {
     355            3 :         if(--refs_ == 0)
     356            2 :             delete this;
     357            3 :     }
     358              : 
     359            1 :     void copy(any_buffers<IsConst>& dest) const override
     360              :     {
     361            1 :         copy(dest, std::integral_constant<bool,
     362              :             sizeof(*this) <= sbo_size>{});
     363            1 :     }
     364              : 
     365              :     void copy(any_buffers<IsConst>& dest,
     366              :         std::true_type) const // small buffers
     367              :     {
     368              :         dest.p_ = ::new(&dest.storage_) impl<T>(t_);
     369              :     }
     370              : 
     371            1 :     void copy(any_buffers<IsConst>& dest,
     372              :         std::false_type) const
     373              :     {
     374            1 :         ++refs_;
     375            1 :         dest.p_ = this;
     376            1 :     }
     377              : 
     378            0 :     void it_copy(void* dest, void const* src) const override
     379              :     {
     380            0 :         ::new(dest) iter_t(*static_cast<iter_t const*>(src));
     381            0 :     }
     382              : 
     383           14 :     void it_destroy(void* p) const override
     384              :     {
     385           14 :         static_cast<iter_t*>(p)->~iter_t();
     386           14 :     }
     387              : 
     388            2 :     void inc(void* p) const override
     389              :     {
     390            2 :         ++static_cast<iter_t*>(p)->i;
     391            2 :     }
     392              : 
     393            0 :     void dec(void* p) const  override
     394              :     {
     395            0 :         --static_cast<iter_t*>(p)->i;
     396            0 :     }
     397              : 
     398              :     typename any_buffers<IsConst>::value_type
     399            2 :     deref(void const* p) const override
     400              :     {
     401            2 :         auto const& it_ = *static_cast<iter_t const*>(p);
     402            2 :         auto it = buffers::begin(t_);
     403            3 :         for(auto i = it_.i; i; --i)
     404            1 :             ++it;
     405            2 :         return *it;
     406            0 :     }
     407              : 
     408            9 :     bool equal(void const* it0, void const* it1) const override
     409              :     {
     410            9 :         return  static_cast<iter_t const*>(it0)->i ==
     411            9 :                 static_cast<iter_t const*>(it1)->i;
     412              :     }
     413              : 
     414            7 :     void begin(void* p) const override
     415              :     {
     416            7 :         ::new(p) iter_t{ 0 };
     417            7 :     }
     418              : 
     419            7 :     void end(void* p) const override
     420              :     {
     421            7 :         ::new(p) iter_t{ len_ };
     422            7 :     }
     423              : 
     424              : private:
     425              :     T t_;
     426              :     std::atomic<std::size_t> mutable refs_{1};
     427              :     std::size_t len_;
     428              : };
     429              : 
     430              : //-----------------------------------------------
     431              : 
     432              : /** A bidirectional iterator for @ref any_buffers.
     433              : 
     434              :     This iterator provides type-erased access to the
     435              :     underlying buffer sequence elements. It models
     436              :     `BidirectionalIterator` and returns buffer objects
     437              :     by value.
     438              : */
     439              : template<bool IsConst>
     440              : class any_buffers<IsConst>::
     441              :     const_iterator
     442              : {
     443              : public:
     444              :     /** The buffer type returned by dereferencing.
     445              :     */
     446              :     using value_type = typename any_buffers::value_type;
     447              : 
     448              :     /** The type returned by `operator*`.
     449              : 
     450              :         Buffers are returned by value.
     451              :     */
     452              :     using reference = value_type;
     453              : 
     454              :     /** Pointer type (void, not used).
     455              :     */
     456              :     using pointer = void;
     457              : 
     458              :     /** Signed integer type for iterator differences.
     459              :     */
     460              :     using difference_type = std::ptrdiff_t;
     461              : 
     462              :     /** Iterator category tag.
     463              :     */
     464              :     using iterator_category =
     465              :         std::bidirectional_iterator_tag;
     466              : 
     467              : #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
     468              :     /** Iterator concept tag (C++20).
     469              :     */
     470              :     using iterator_concept = std::bidirectional_iterator_tag;
     471              : #endif
     472              : 
     473              :     /** Destructor.
     474              : 
     475              :         Destroys the type-erased iterator state.
     476              :     */
     477          108 :     ~const_iterator()
     478              :     {
     479          108 :         p_->it_destroy(&storage_);
     480          108 :     }
     481              : 
     482              :     /** Default constructor.
     483              : 
     484              :         Constructs a singular iterator. A default-constructed
     485              :         iterator may only be assigned to or destroyed.
     486              :     */
     487              :     const_iterator() noexcept;
     488              : 
     489              :     /** Copy constructor.
     490              : 
     491              :         @param other The iterator to copy.
     492              :     */
     493            2 :     const_iterator(
     494              :         const_iterator const& other) noexcept
     495            2 :         : p_(other.p_)
     496              :     {
     497            2 :         p_->it_copy(&storage_, &other.storage_);
     498            2 :     }
     499              : 
     500              :     /** Copy assignment.
     501              : 
     502              :         @param other The iterator to copy.
     503              :         @return `*this`
     504              :     */
     505              :     const_iterator& operator=(
     506              :         const_iterator const& other) noexcept
     507              :     {
     508              :         if(this == &other)
     509              :             return *this;
     510              :         p_->it_destroy(&storage_);
     511              :         p_ = other.p_;
     512              :         p_->it_copy(&storage_, &other.storage_);
     513              :         return *this;
     514              :     }
     515              : 
     516              :     /** Test for equality.
     517              : 
     518              :         @param other The iterator to compare.
     519              :         @return `true` if both iterators point to the
     520              :         same element of the same sequence.
     521              :     */
     522              :     bool
     523           81 :     operator==(
     524              :         const_iterator const& other) const noexcept
     525              :     {
     526           81 :         if(p_ != other.p_)
     527            0 :             return false;
     528           81 :         return p_->equal(&storage_, &other.storage_);
     529              :     }
     530              : 
     531              :     /** Test for inequality.
     532              : 
     533              :         @param other The iterator to compare.
     534              :         @return `true` if the iterators point to
     535              :         different elements or different sequences.
     536              :     */
     537              :     bool
     538           77 :     operator!=(
     539              :         const_iterator const& other) const noexcept
     540              :     {
     541           77 :         return !(*this == other);
     542              :     }
     543              : 
     544              :     /** Dereference the iterator.
     545              : 
     546              :         @return The buffer at the current position.
     547              : 
     548              :         @pre The iterator is dereferenceable
     549              :         (not default-constructed or past-the-end).
     550              :     */
     551              :     reference
     552           27 :     operator*() const noexcept
     553              :     {
     554           27 :         return p_->deref(&storage_);
     555              :     }
     556              : 
     557              :     /** Pre-increment.
     558              : 
     559              :         Advances the iterator to the next buffer.
     560              : 
     561              :         @return `*this`
     562              : 
     563              :         @pre The iterator is incrementable.
     564              :     */
     565              :     const_iterator&
     566           30 :     operator++() noexcept
     567              :     {
     568           30 :         p_->inc(&storage_);
     569           30 :         return *this;
     570              :     }
     571              : 
     572              :     /** Post-increment.
     573              : 
     574              :         Advances the iterator to the next buffer.
     575              : 
     576              :         @return A copy of the iterator before incrementing.
     577              : 
     578              :         @pre The iterator is incrementable.
     579              :     */
     580              :     const_iterator
     581              :     operator++(int) noexcept
     582              :     {
     583              :         auto temp = *this;
     584              :         ++(*this);
     585              :         return temp;
     586              :     }
     587              : 
     588              :     /** Pre-decrement.
     589              : 
     590              :         Moves the iterator to the previous buffer.
     591              : 
     592              :         @return `*this`
     593              : 
     594              :         @pre The iterator is decrementable.
     595              :     */
     596              :     const_iterator&
     597            2 :     operator--() noexcept
     598              :     {
     599            2 :         BOOST_ASSERT(p_ != nullptr);
     600            2 :         p_->dec(&storage_);
     601            2 :         return *this;
     602              :     }
     603              : 
     604              :     /** Post-decrement.
     605              : 
     606              :         Moves the iterator to the previous buffer.
     607              : 
     608              :         @return A copy of the iterator before decrementing.
     609              : 
     610              :         @pre The iterator is decrementable.
     611              :     */
     612              :     const_iterator
     613              :     operator--(int) noexcept
     614              :     {
     615              :         auto temp = *this;
     616              :         --(*this);
     617              :         return temp;
     618              :     }
     619              : 
     620              : private:
     621              :     friend class any_buffers;
     622              : 
     623              :     struct begin_tag {};
     624              :     struct end_tag {};
     625              : 
     626           54 :     const_iterator(begin_tag,
     627              :         any_impl const* p) noexcept
     628           54 :         : p_(p)
     629              :     {
     630           54 :         p_->begin(&storage_);
     631           54 :         BOOST_ASSERT(p_ != nullptr);
     632           54 :     }
     633              : 
     634           52 :     const_iterator(end_tag,
     635              :         any_impl const* p) noexcept
     636           52 :         : p_(p)
     637              :     {
     638           52 :         p_->end(&storage_);
     639           52 :         BOOST_ASSERT(p_ != nullptr);
     640           52 :     }
     641              : 
     642              :     alignas(std::max_align_t)
     643              :         unsigned char mutable storage_[iter_sbo_size] = {};
     644              :     any_buffers::any_impl const* p_ = nullptr;
     645              : };
     646              : 
     647              : //-----------------------------------------------
     648              : 
     649              : template<>
     650              : BOOST_BUFFERS_DECL
     651              : any_buffers<true>::
     652              : any_buffers() noexcept;
     653              : 
     654              : template<>
     655              : BOOST_BUFFERS_DECL
     656              : any_buffers<false>::
     657              : any_buffers() noexcept;
     658              : 
     659              : template<>
     660              : BOOST_BUFFERS_DECL
     661              : any_buffers<true>::
     662              : const_iterator::
     663              : const_iterator() noexcept;
     664              : 
     665              : template<>
     666              : BOOST_BUFFERS_DECL
     667              : any_buffers<false>::
     668              : const_iterator::
     669              : const_iterator() noexcept;
     670              : 
     671              : //-----------------------------------------------
     672              : 
     673              : template<bool IsConst>
     674              : auto
     675           54 : any_buffers<IsConst>::
     676              : begin() const noexcept ->
     677              :     const_iterator
     678              : {
     679           54 :     if(! p_)
     680            0 :         return const_iterator();
     681              :     return const_iterator(typename
     682           54 :         const_iterator::begin_tag{}, p_);
     683              : }
     684              : 
     685              : template<bool IsConst>
     686              : auto
     687           52 : any_buffers<IsConst>::
     688              : end() const noexcept ->
     689              :     const_iterator
     690              : {
     691           52 :     if(! p_)
     692            0 :         return const_iterator();
     693              :     return const_iterator(typename
     694           52 :         const_iterator::end_tag{}, p_);
     695              : }
     696              : 
     697              : } // buffers
     698              : } // boost
     699              : 
     700              : #endif
        

Generated by: LCOV version 2.1