LCOV - code coverage report
Current view: top level - json - storage_ptr.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 74 74
Test Date: 2026-02-04 20:24:55 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              :     void* p_;
      87              : 
      88              :     shared_resource*
      89          302 :     get_shared() const noexcept
      90              :     {
      91          302 :         auto const i = reinterpret_cast<std::uintptr_t>(p_);
      92          302 :         auto const c_ptr = reinterpret_cast<unsigned char*>(p_) - (i & 3);
      93              :         return static_cast<shared_resource*>(
      94          302 :             reinterpret_cast<container::pmr::memory_resource*>(c_ptr));
      95              :     }
      96              : 
      97              :     void
      98      6352572 :     addref() const noexcept
      99              :     {
     100      6352572 :         if(is_shared())
     101          141 :             get_shared()->refs.fetch_add(
     102              :                 1, std::memory_order_relaxed);
     103      6352572 :     }
     104              : 
     105              :     void
     106     43989937 :     release() const noexcept
     107              :     {
     108     43989937 :         if(is_shared())
     109              :         {
     110          161 :             auto const p = get_shared();
     111          161 :             if(p->refs.fetch_sub(1,
     112          161 :                     std::memory_order_acq_rel) == 1)
     113           20 :                 delete p;
     114              :         }
     115     43989937 :     }
     116              : 
     117              :     template<class T>
     118           20 :     storage_ptr(
     119              :         detail::shared_resource_impl<T>* p) noexcept
     120           20 :         : p_(reinterpret_cast<unsigned char*>(
     121              :                 static_cast<container::pmr::memory_resource*>(p))
     122           20 :              + 1 + (json::is_deallocate_trivial<T>::value ? 2 : 0))
     123              :     {
     124           20 :         BOOST_ASSERT(p);
     125           20 :     }
     126              : 
     127              : public:
     128              :     /** Destructor.
     129              : 
     130              :         If the pointer has shared ownership of the resource, the shared
     131              :         ownership is released. If this is the last owned copy, the memory
     132              :         resource is destroyed.
     133              : 
     134              :         @par Complexity
     135              :         Constant.
     136              : 
     137              :         @par Exception Safety
     138              :         No-throw guarantee.
     139              :     */
     140     41913287 :     ~storage_ptr() noexcept
     141              :     {
     142     41913287 :         release();
     143     41913287 :     }
     144              : 
     145              :     /** Constructors.
     146              : 
     147              :         @li **(1)** constructs a non-owning pointer that refers to the
     148              :         \<\<default_memory_resource,default memory resource\>\>.
     149              : 
     150              :         @li **(2)** constructs a non-owning pointer that points to the memory
     151              :         resource `r`.
     152              : 
     153              :         @li **(3)** constructs a non-owning pointer that points to the same
     154              :         memory resource as `alloc`, obtained by calling `alloc.resource()`.
     155              : 
     156              :         @li **(4)**, **(5)** construct a pointer to the same memory resource as
     157              :         `other`, with the same ownership.
     158              : 
     159              :         After **(4)** and **(5)** if `other` was owning, then the constructed
     160              :         pointer is also owning. In particular, **(4)** transfers ownership to
     161              :         the constructed pointer while **(5)** causes it to share ownership with
     162              :         `other`. Otherwise, and with other overloads the constructed pointer
     163              :         doesn't own its memory resource and the caller is responsible for
     164              :         maintaining the lifetime of the pointed-to
     165              :         @ref boost::container::pmr::memory_resource.
     166              : 
     167              :         After **(4)**, `other` will point to the default memory resource.
     168              : 
     169              :         @par Constraints
     170              :         @code
     171              :         std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
     172              :         @endcode
     173              : 
     174              :         @pre
     175              :         @code
     176              :         r != nullptr
     177              :         @endcode
     178              : 
     179              :         @par Complexity
     180              :         Constant.
     181              : 
     182              :         @par Exception Safety
     183              :         No-throw guarantee.
     184              : 
     185              :         @{
     186              :     */
     187     14653426 :     storage_ptr() noexcept
     188     14653426 :         : p_(nullptr)
     189              :     {
     190     14653426 :     }
     191              : 
     192              :     /** Overload
     193              : 
     194              :         @tparam T The type of memory resource.
     195              :         @param r A non-null pointer to the memory resource to use.
     196              :     */
     197              :     template<class T
     198              : #ifndef BOOST_JSON_DOCS
     199              :         , class = typename std::enable_if<
     200              :             std::is_convertible<T*,
     201              :                 container::pmr::memory_resource*>::value>::type
     202              : #endif
     203              :     >
     204        57222 :     storage_ptr(T* r) noexcept
     205        57222 :         : p_(reinterpret_cast<unsigned char*>(
     206              :                 static_cast<container::pmr::memory_resource*>(r))
     207           17 :              + (json::is_deallocate_trivial<T>::value ? 2 : 0))
     208              :     {
     209        57222 :         BOOST_ASSERT(r);
     210        57222 :     }
     211              : 
     212              :     /** Overload
     213              : 
     214              :         @tparam V Any type.
     215              :         @param alloc A @ref boost::container::pmr::polymorphic_allocator to
     216              :         construct from.
     217              :     */
     218              :     template<class V>
     219           10 :     storage_ptr(
     220              :         container::pmr::polymorphic_allocator<V> const& alloc) noexcept
     221           10 :         : p_(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 :         : p_(detail::exchange(other.p_, nullptr))
     232              :     {
     233     22965263 :     }
     234              : 
     235              :     /** Overload
     236              : 
     237              :         @param other
     238              :     */
     239      6352571 :     storage_ptr(
     240              :         storage_ptr const& other) noexcept
     241      6352571 :         : p_(other.p_)
     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 :         p_ = detail::exchange(other.p_, nullptr);
     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 :         p_ = other.p_;
     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 :         auto i = reinterpret_cast<std::uintptr_t>(p_);
     304     50342510 :         return (i & 1) != 0;
     305              :     }
     306              : 
     307              :     /** Check if calling `deallocate` on the memory resource has no effect.
     308              : 
     309              :         This function is used to determine if the deallocate function of the
     310              :         pointed to memory resource is trivial. The value of @ref
     311              :         is_deallocate_trivial is evaluated and saved when the memory resource
     312              :         is constructed and the type is known, before the type is erased.
     313              :     */
     314              :     bool
     315            1 :     is_deallocate_trivial() const noexcept
     316              :     {
     317            1 :         auto i = reinterpret_cast<std::uintptr_t>(p_);
     318            1 :         return (i & 2) != 0;
     319              :     }
     320              : 
     321              :     /** Check if ownership of the memory resource is not shared and deallocate is trivial.
     322              : 
     323              :         This function is used to determine if calls to deallocate can
     324              :         effectively be skipped. Equivalent to `! is_shared() &&
     325              :         is_deallocate_trivial()`.
     326              :     */
     327              :     bool
     328      4323548 :     is_not_shared_and_deallocate_is_trivial() const noexcept
     329              :     {
     330      4323548 :         auto i = reinterpret_cast<std::uintptr_t>(p_);
     331      4323548 :         return (i & 3) == 2;
     332              :     }
     333              : 
     334              :     /** Return a pointer to the memory resource.
     335              : 
     336              :         This function returns a pointer to the
     337              :         referenced @ref boost::container::pmr::memory_resource.
     338              : 
     339              :         @par Complexity
     340              :         Constant.
     341              : 
     342              :         @par Exception Safety
     343              :         No-throw guarantee.
     344              :     */
     345              :     container::pmr::memory_resource*
     346       653237 :     get() const noexcept
     347              :     {
     348       653237 :         if(!p_)
     349       530672 :             return default_resource::get();
     350              : 
     351       122565 :         auto const i = reinterpret_cast<std::uintptr_t>(p_);
     352       122565 :         auto const c_ptr = reinterpret_cast<unsigned char*>(p_) - (i & 3);
     353       122565 :         return reinterpret_cast<container::pmr::memory_resource*>(c_ptr);
     354              :     }
     355              : 
     356              :     /** Return a pointer to the memory resource.
     357              : 
     358              :         This function returns a pointer to the referenced @ref
     359              :         boost::container::pmr::memory_resource.
     360              : 
     361              :         @par Complexity
     362              :         Constant.
     363              : 
     364              :         @par Exception Safety
     365              :         No-throw guarantee.
     366              :     */
     367              :     container::pmr::memory_resource*
     368       649749 :     operator->() const noexcept
     369              :     {
     370       649749 :         return get();
     371              :     }
     372              : 
     373              :     /** Return a reference to the memory resource.
     374              : 
     375              :         This function returns a reference to the pointed-to @ref
     376              :         boost::container::pmr::memory_resource.
     377              : 
     378              :         @par Complexity
     379              : 
     380              :         Constant.
     381              : 
     382              :         @par Exception Safety
     383              : 
     384              :         No-throw guarantee.
     385              :     */
     386              :     container::pmr::memory_resource&
     387         3456 :     operator*() const noexcept
     388              :     {
     389         3456 :         return *get();
     390              :     }
     391              : 
     392              :     template<class U, class... Args>
     393              :     friend
     394              :     storage_ptr
     395              :     make_shared_resource(Args&&... args);
     396              : };
     397              : 
     398              : #if defined(_MSC_VER)
     399              : # pragma warning( push )
     400              : # if !defined(__clang__) && _MSC_VER <= 1900
     401              : #  pragma warning( disable : 4702 )
     402              : # endif
     403              : #endif
     404              : 
     405              : /** Return a pointer that owns a new, dynamically allocated memory resource.
     406              : 
     407              :     This function dynamically allocates a new memory resource as if by
     408              :     `operator new` that uses shared ownership. The lifetime of the memory
     409              :     resource will be extended until the last @ref storage_ptr which points to
     410              :     it is destroyed.
     411              : 
     412              :     @par Constraints
     413              :     @code
     414              :     std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
     415              :     @endcode
     416              : 
     417              :     @par Complexity
     418              :     Same as `new U( std::forward<Args>(args)... )`.
     419              : 
     420              :     @par Exception Safety
     421              :     Strong guarantee.
     422              : 
     423              :     @tparam U The type of memory resource to create.
     424              : 
     425              :     @param args Parameters forwarded to the constructor of `U`.
     426              : */
     427              : template<class U, class... Args>
     428              : storage_ptr
     429           21 : make_shared_resource(Args&&... args)
     430              : {
     431              :     // If this generates an error, it means that
     432              :     // `T` is not a memory resource.
     433              :     BOOST_CORE_STATIC_ASSERT((
     434              :         std::is_base_of<container::pmr::memory_resource, U>::value));
     435           23 :     return storage_ptr(new
     436              :         detail::shared_resource_impl<U>(
     437           22 :             std::forward<Args>(args)...));
     438              : }
     439              : #if defined(_MSC_VER)
     440              : # pragma warning( pop )
     441              : #endif
     442              : 
     443              : /// Overload
     444              : inline
     445              : bool
     446            5 : operator==(
     447              :     storage_ptr const& lhs,
     448              :     storage_ptr const& rhs) noexcept
     449              : {
     450            5 :     return lhs.get() == rhs.get();
     451              : }
     452              : 
     453              : /// Overload
     454              : inline
     455              : bool
     456              : operator!=(
     457              :     storage_ptr const& lhs,
     458              :     storage_ptr const& rhs) noexcept
     459              : {
     460              :     return lhs.get() != rhs.get();
     461              : }
     462              : 
     463              : } // namespace json
     464              : } // namespace boost
     465              : 
     466              : #endif
        

Generated by: LCOV version 2.3