LCOV - code coverage report
Current view: top level - json - storage_ptr.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 68 68
Test Date: 2026-01-02 17:31:39 Functions: 96.8 % 31 30

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 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/boostorg/json
       8              : //
       9              : 
      10              : #ifndef BOOST_JSON_STORAGE_PTR_HPP
      11              : #define BOOST_JSON_STORAGE_PTR_HPP
      12              : 
      13              : #include <boost/core/detail/static_assert.hpp>
      14              : #include <boost/container/pmr/polymorphic_allocator.hpp>
      15              : #include <boost/json/detail/config.hpp>
      16              : #include <boost/json/detail/shared_resource.hpp>
      17              : #include <boost/json/detail/default_resource.hpp>
      18              : #include <boost/json/is_deallocate_trivial.hpp>
      19              : #include <new>
      20              : #include <type_traits>
      21              : #include <utility>
      22              : 
      23              : namespace boost {
      24              : namespace json {
      25              : 
      26              : /** A smart pointer to a memory resource.
      27              : 
      28              :     This class is used to hold a pointer to a memory resource. The pointed-to
      29              :     resource is always valid. Depending on the means of construction, the
      30              :     ownership will be either:
      31              : 
      32              :     @li Non-owning, when constructing from a raw pointer to
      33              :     @ref boost::container::pmr::memory_resource or from a
      34              :     @ref boost::container::pmr::polymorphic_allocator. In this case the caller
      35              :     is responsible for ensuring that the lifetime of the memory resource
      36              :     extends until there are no more calls to allocate or deallocate.
      37              : 
      38              :     @li Owning, when constructing using the function @ref make_shared_resource.
      39              :     In this case ownership is shared; the lifetime of the memory resource
      40              :     extends until the last copy of the `storage_ptr` is destroyed.
      41              : 
      42              :     @par Examples
      43              :     These statements create a memory resource on the stack and construct
      44              :     a pointer from it without taking ownership:
      45              : 
      46              :     @code
      47              :     monotonic_resource mr;                  // Create our memory resource on the stack
      48              :     storage_ptr sp( &mr );                  // Construct a non-owning pointer to the resource
      49              :     @endcode
      50              : 
      51              :     This function creates a pointer to a memory resource using shared ownership
      52              :     and returns it. The lifetime of the memory resource extends until the last
      53              :     copy of the pointer is destroyed:
      54              : 
      55              :     @code
      56              :     // Create a counted memory resource and return it
      57              :     storage_ptr make_storage()
      58              :     {
      59              :         return make_shared_resource< monotonic_resource >();
      60              :     }
      61              :     @endcode
      62              : 
      63              :     @par Thread Safety
      64              :     Instances of this type provide the default level of thread safety for all
      65              :     C++ objects. Specifically, it conforms to
      66              :     [16.4.6.10 Data race avoidance](http://eel.is/c++draft/res.on.data.races).
      67              : 
      68              :     @see
      69              :         @ref make_shared_resource,
      70              :         @ref boost::container::pmr::polymorphic_allocator,
      71              :         @ref boost::container::pmr::memory_resource.
      72              : 
      73              : */
      74              : class storage_ptr
      75              : {
      76              : #ifndef BOOST_JSON_DOCS
      77              :     // VFALCO doc toolchain shows this when it shouldn't
      78              :     friend struct detail::shared_resource;
      79              : #endif
      80              :     using shared_resource =
      81              :         detail::shared_resource;
      82              : 
      83              :     using default_resource =
      84              :         detail::default_resource;
      85              : 
      86              :     std::uintptr_t i_;
      87              : 
      88              :     shared_resource*
      89          302 :     get_shared() const noexcept
      90              :     {
      91              :         return static_cast<shared_resource*>(
      92              :             reinterpret_cast<container::pmr::memory_resource*>(
      93          302 :                 i_ & ~3));
      94              :     }
      95              : 
      96              :     void
      97      6352572 :     addref() const noexcept
      98              :     {
      99      6352572 :         if(is_shared())
     100          141 :             get_shared()->refs.fetch_add(
     101              :                 1, std::memory_order_relaxed);
     102      6352572 :     }
     103              : 
     104              :     void
     105     43989937 :     release() const noexcept
     106              :     {
     107     43989937 :         if(is_shared())
     108              :         {
     109          161 :             auto const p = get_shared();
     110          161 :             if(p->refs.fetch_sub(1,
     111          161 :                     std::memory_order_acq_rel) == 1)
     112           20 :                 delete p;
     113              :         }
     114     43989937 :     }
     115              : 
     116              :     template<class T>
     117           20 :     storage_ptr(
     118              :         detail::shared_resource_impl<T>* p) noexcept
     119           20 :         : i_(reinterpret_cast<std::uintptr_t>(
     120           20 :                 static_cast<container::pmr::memory_resource*>(p)) + 1 +
     121              :             (json::is_deallocate_trivial<T>::value ? 2 : 0))
     122              :     {
     123           20 :         BOOST_ASSERT(p);
     124           20 :     }
     125              : 
     126              : public:
     127              :     /** Destructor.
     128              : 
     129              :         If the pointer has shared ownership of the resource, the shared
     130              :         ownership is released. If this is the last owned copy, the memory
     131              :         resource is destroyed.
     132              : 
     133              :         @par Complexity
     134              :         Constant.
     135              : 
     136              :         @par Exception Safety
     137              :         No-throw guarantee.
     138              :     */
     139     41913287 :     ~storage_ptr() noexcept
     140              :     {
     141     41913287 :         release();
     142     41913287 :     }
     143              : 
     144              :     /** Constructors.
     145              : 
     146              :         @li **(1)** constructs a non-owning pointer that refers to the
     147              :         \<\<default_memory_resource,default memory resource\>\>.
     148              : 
     149              :         @li **(2)** constructs a non-owning pointer that points to the memory
     150              :         resource `r`.
     151              : 
     152              :         @li **(3)** constructs a non-owning pointer that points to the same
     153              :         memory resource as `alloc`, obtained by calling `alloc.resource()`.
     154              : 
     155              :         @li **(4)**, **(5)** construct a pointer to the same memory resource as
     156              :         `other`, with the same ownership.
     157              : 
     158              :         After **(4)** and **(5)** if `other` was owning, then the constructed
     159              :         pointer is also owning. In particular, **(4)** transfers ownership to
     160              :         the constructed pointer while **(5)** causes it to share ownership with
     161              :         `other`. Otherwise, and with other overloads the constructed pointer
     162              :         doesn't own its memory resource and the caller is responsible for
     163              :         maintaining the lifetime of the pointed-to
     164              :         @ref boost::container::pmr::memory_resource.
     165              : 
     166              :         After **(4)**, `other` will point to the default memory resource.
     167              : 
     168              :         @par Constraints
     169              :         @code
     170              :         std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
     171              :         @endcode
     172              : 
     173              :         @pre
     174              :         @code
     175              :         r != nullptr
     176              :         @endcode
     177              : 
     178              :         @par Complexity
     179              :         Constant.
     180              : 
     181              :         @par Exception Safety
     182              :         No-throw guarantee.
     183              : 
     184              :         @{
     185              :     */
     186     14653426 :     storage_ptr() noexcept
     187     14653426 :         : i_(0)
     188              :     {
     189     14653426 :     }
     190              : 
     191              :     /** Overload
     192              : 
     193              :         @tparam T The type of memory resource.
     194              :         @param r A non-null pointer to the memory resource to use.
     195              :     */
     196              :     template<class T
     197              : #ifndef BOOST_JSON_DOCS
     198              :         , class = typename std::enable_if<
     199              :             std::is_convertible<T*,
     200              :                 container::pmr::memory_resource*>::value>::type
     201              : #endif
     202              :     >
     203        57222 :     storage_ptr(T* r) noexcept
     204        57222 :         : i_(reinterpret_cast<std::uintptr_t>(
     205           18 :                 static_cast<container::pmr::memory_resource *>(r)) +
     206              :             (json::is_deallocate_trivial<T>::value ? 2 : 0))
     207              :     {
     208        57222 :         BOOST_ASSERT(r);
     209        57222 :     }
     210              : 
     211              :     /** Overload
     212              : 
     213              :         @tparam V Any type.
     214              :         @param alloc A @ref boost::container::pmr::polymorphic_allocator to
     215              :         construct from.
     216              :     */
     217              :     template<class V>
     218           10 :     storage_ptr(
     219              :         container::pmr::polymorphic_allocator<V> const& alloc) noexcept
     220           10 :         : i_(reinterpret_cast<std::uintptr_t>(
     221           10 :             alloc.resource()))
     222              :     {
     223           10 :     }
     224              : 
     225              :     /** Overload
     226              : 
     227              :         @param other Another pointer.
     228              :     */
     229     22965263 :     storage_ptr(
     230              :         storage_ptr&& other) noexcept
     231     22965263 :         : i_(detail::exchange(other.i_, 0))
     232              :     {
     233     22965263 :     }
     234              : 
     235              :     /** Overload
     236              : 
     237              :         @param other
     238              :     */
     239      6352571 :     storage_ptr(
     240              :         storage_ptr const& other) noexcept
     241      6352571 :         : i_(other.i_)
     242              :     {
     243      6352571 :         addref();
     244      6352571 :     }
     245              :     /// @}
     246              : 
     247              :     /** Assignment operators.
     248              : 
     249              :         This function assigns a pointer that points to the same memory resource
     250              :         as `other`, with the same ownership:
     251              : 
     252              :         @li If `other` is non-owning, then the assigned-to pointer will be be
     253              :         non-owning.
     254              : 
     255              :         @li If `other` has shared ownership, then **(1)** transfers ownership
     256              :         to the assigned-to pointer, while after **(2)** it shares the ownership
     257              :         with `other`.
     258              : 
     259              :         If the assigned-to pointer previously had shared ownership, it is
     260              :         released before the function returns.
     261              : 
     262              :         After **(1)**, `other` will point to the
     263              :         \<\<default_memory_resource,default memory resource\>\>.
     264              : 
     265              :         @par Complexity
     266              :         Constant.
     267              : 
     268              :         @par Exception Safety
     269              :         No-throw guarantee.
     270              : 
     271              :         @param other Another pointer.
     272              : 
     273              :         @{
     274              :     */
     275              :     storage_ptr&
     276      2076649 :     operator=(
     277              :         storage_ptr&& other) noexcept
     278              :     {
     279      2076649 :         release();
     280      2076649 :         i_ = detail::exchange(other.i_, 0);
     281      2076649 :         return *this;
     282              :     }
     283              : 
     284              :     storage_ptr&
     285            1 :     operator=(
     286              :         storage_ptr const& other) noexcept
     287              :     {
     288            1 :         other.addref();
     289            1 :         release();
     290            1 :         i_ = other.i_;
     291            1 :         return *this;
     292              :     }
     293              :     /// @}
     294              : 
     295              :     /** Check if ownership of the memory resource is shared.
     296              : 
     297              :         This function returns true for memory resources created using @ref
     298              :         make_shared_resource.
     299              :     */
     300              :     bool
     301     50342510 :     is_shared() const noexcept
     302              :     {
     303     50342510 :         return (i_ & 1) != 0;
     304              :     }
     305              : 
     306              :     /** Check if calling `deallocate` on the memory resource has no effect.
     307              : 
     308              :         This function is used to determine if the deallocate function of the
     309              :         pointed to memory resource is trivial. The value of @ref
     310              :         is_deallocate_trivial is evaluated and saved when the memory resource
     311              :         is constructed and the type is known, before the type is erased.
     312              :     */
     313              :     bool
     314            1 :     is_deallocate_trivial() const noexcept
     315              :     {
     316            1 :         return (i_ & 2) != 0;
     317              :     }
     318              : 
     319              :     /** Check if ownership of the memory resource is not shared and deallocate is trivial.
     320              : 
     321              :         This function is used to determine if calls to deallocate can
     322              :         effectively be skipped. Equivalent to `! is_shared() &&
     323              :         is_deallocate_trivial()`.
     324              :     */
     325              :     bool
     326      4323548 :     is_not_shared_and_deallocate_is_trivial() const noexcept
     327              :     {
     328      4323548 :         return (i_ & 3) == 2;
     329              :     }
     330              : 
     331              :     /** Return a pointer to the memory resource.
     332              : 
     333              :         This function returns a pointer to the
     334              :         referenced @ref boost::container::pmr::memory_resource.
     335              : 
     336              :         @par Complexity
     337              :         Constant.
     338              : 
     339              :         @par Exception Safety
     340              :         No-throw guarantee.
     341              :     */
     342              :     container::pmr::memory_resource*
     343       653237 :     get() const noexcept
     344              :     {
     345       653237 :         if(i_ != 0)
     346              :             return reinterpret_cast<
     347       122565 :                 container::pmr::memory_resource*>(i_ & ~3);
     348       530672 :         return default_resource::get();
     349              :     }
     350              : 
     351              :     /** Return a pointer to the memory resource.
     352              : 
     353              :         This function returns a pointer to the referenced @ref
     354              :         boost::container::pmr::memory_resource.
     355              : 
     356              :         @par Complexity
     357              :         Constant.
     358              : 
     359              :         @par Exception Safety
     360              :         No-throw guarantee.
     361              :     */
     362              :     container::pmr::memory_resource*
     363       649749 :     operator->() const noexcept
     364              :     {
     365       649749 :         return get();
     366              :     }
     367              : 
     368              :     /** Return a reference to the memory resource.
     369              : 
     370              :         This function returns a reference to the pointed-to @ref
     371              :         boost::container::pmr::memory_resource.
     372              : 
     373              :         @par Complexity
     374              : 
     375              :         Constant.
     376              : 
     377              :         @par Exception Safety
     378              : 
     379              :         No-throw guarantee.
     380              :     */
     381              :     container::pmr::memory_resource&
     382         3456 :     operator*() const noexcept
     383              :     {
     384         3456 :         return *get();
     385              :     }
     386              : 
     387              :     template<class U, class... Args>
     388              :     friend
     389              :     storage_ptr
     390              :     make_shared_resource(Args&&... args);
     391              : };
     392              : 
     393              : #if defined(_MSC_VER)
     394              : # pragma warning( push )
     395              : # if !defined(__clang__) && _MSC_VER <= 1900
     396              : #  pragma warning( disable : 4702 )
     397              : # endif
     398              : #endif
     399              : 
     400              : /** Return a pointer that owns a new, dynamically allocated memory resource.
     401              : 
     402              :     This function dynamically allocates a new memory resource as if by
     403              :     `operator new` that uses shared ownership. The lifetime of the memory
     404              :     resource will be extended until the last @ref storage_ptr which points to
     405              :     it is destroyed.
     406              : 
     407              :     @par Constraints
     408              :     @code
     409              :     std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
     410              :     @endcode
     411              : 
     412              :     @par Complexity
     413              :     Same as `new U( std::forward<Args>(args)... )`.
     414              : 
     415              :     @par Exception Safety
     416              :     Strong guarantee.
     417              : 
     418              :     @tparam U The type of memory resource to create.
     419              : 
     420              :     @param args Parameters forwarded to the constructor of `U`.
     421              : */
     422              : template<class U, class... Args>
     423              : storage_ptr
     424           21 : make_shared_resource(Args&&... args)
     425              : {
     426              :     // If this generates an error, it means that
     427              :     // `T` is not a memory resource.
     428              :     BOOST_CORE_STATIC_ASSERT((
     429              :         std::is_base_of<container::pmr::memory_resource, U>::value));
     430           23 :     return storage_ptr(new
     431              :         detail::shared_resource_impl<U>(
     432           22 :             std::forward<Args>(args)...));
     433              : }
     434              : #if defined(_MSC_VER)
     435              : # pragma warning( pop )
     436              : #endif
     437              : 
     438              : /// Overload
     439              : inline
     440              : bool
     441            5 : operator==(
     442              :     storage_ptr const& lhs,
     443              :     storage_ptr const& rhs) noexcept
     444              : {
     445            5 :     return lhs.get() == rhs.get();
     446              : }
     447              : 
     448              : /// Overload
     449              : inline
     450              : bool
     451              : operator!=(
     452              :     storage_ptr const& lhs,
     453              :     storage_ptr const& rhs) noexcept
     454              : {
     455              :     return lhs.get() != rhs.get();
     456              : }
     457              : 
     458              : } // namespace json
     459              : } // namespace boost
     460              : 
     461              : #endif
        

Generated by: LCOV version 2.1