boost/json/storage_ptr.hpp
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_STORAGE_PTR_HPP
#define BOOST_JSON_STORAGE_PTR_HPP
#include <boost/container/pmr/polymorphic_allocator.hpp>
#include <boost/json/detail/config.hpp>
#include <boost/json/memory_resource.hpp>
#include <boost/json/detail/shared_resource.hpp>
#include <boost/json/detail/default_resource.hpp>
#include <boost/json/is_deallocate_trivial.hpp>
#include <cstddef>
#include <new>
#include <type_traits>
#include <utility>
namespace boost {
namespace json {
/** A smart pointer to a memory resource.
This container is used to hold a pointer to a memory resource. The
pointed-to resource is always valid. Depending on the means of
construction, the ownership will be either:
@li Non-owning, when constructing from a raw pointer to
`boost::container::pmr::memory_resource` or from a
`boost::container::pmr::polymorphic_allocator`. In this case the caller is
responsible for ensuring that the lifetime of the memory resource extends
until there are no more calls to allocate or deallocate.
@li Owning, when constructing using the function
@ref make_shared_resource. In this case
ownership is shared; the lifetime of the memory
resource extends until the last copy of the
@ref storage_ptr is destroyed.
@par Examples
These statements create a memory resource on the
stack and construct a pointer from it without
taking ownership:
@code
monotonic_resource mr; // Create our memory resource on the stack
storage_ptr sp( &mr ); // Construct a non-owning pointer to the resource
@endcode
This function creates a pointer to a memory
resource using shared ownership and returns it.
The lifetime of the memory resource extends until
the last copy of the pointer is destroyed:
@code
// Create a counted memory resource and return it
storage_ptr make_storage()
{
return make_shared_resource< monotonic_resource >();
}
@endcode
@par Thread Safety
Instances of this type provide the default level of
thread safety for all C++ objects. Specifically, it
conforms to
<a href="http://eel.is/c++draft/res.on.data.races">
16.4.6.10 Data race avoidance</a>.
@see
@ref make_shared_resource,
[`boost::container::pmr::polymorphic_allocator`](https://www.boost.org/doc/libs/release/doc/html/boost/container/pmr/polymorphic_allocator.html).
[`boost::container::pmr::memory_resource`](https://www.boost.org/doc/libs/release/doc/html/boost/container/pmr/memory_resource.html).
*/
class storage_ptr
{
#ifndef BOOST_JSON_DOCS
// VFALCO doc toolchain shows this when it shouldn't
friend struct detail::shared_resource;
#endif
using shared_resource =
detail::shared_resource;
using default_resource =
detail::default_resource;
std::uintptr_t i_;
shared_resource*
get_shared() const noexcept
{
return static_cast<shared_resource*>(
reinterpret_cast<container::pmr::memory_resource*>(
i_ & ~3));
}
void
addref() const noexcept
{
if(is_shared())
get_shared()->refs.fetch_add(
1, std::memory_order_relaxed);
}
void
release() const noexcept
{
if(is_shared())
{
auto const p = get_shared();
if(p->refs.fetch_sub(1,
std::memory_order_acq_rel) == 1)
delete p;
}
}
template<class T>
storage_ptr(
detail::shared_resource_impl<T>* p) noexcept
: i_(reinterpret_cast<std::uintptr_t>(
static_cast<container::pmr::memory_resource*>(p)) + 1 +
(json::is_deallocate_trivial<T>::value ? 2 : 0))
{
BOOST_ASSERT(p);
}
public:
/** Destructor
If the pointer has shared ownership of the
resource, the shared ownership is released.
If this is the last owned copy, the memory
resource is destroyed.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
~storage_ptr() noexcept
{
release();
}
/** Constructor
This constructs a non-owning pointer that refers
to the [default memory resource].
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
storage_ptr() noexcept
: i_(0)
{
}
/** Constructor
This constructs a non-owning pointer that points to the memory resource
`r`. The caller is responsible for maintaining the lifetime of the
pointed-to `boost::container::pmr::memory_resource`.
@par Constraints
@code
std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
@endcode
@par Preconditions
@code
r != nullptr
@endcode
@par Exception Safety
No-throw guarantee.
@param r A pointer to the memory resource to use.
This may not be null.
*/
template<class T
#ifndef BOOST_JSON_DOCS
, class = typename std::enable_if<
std::is_convertible<T*,
container::pmr::memory_resource*>::value>::type
#endif
>
storage_ptr(T* r) noexcept
: i_(reinterpret_cast<std::uintptr_t>(
static_cast<container::pmr::memory_resource *>(r)) +
(json::is_deallocate_trivial<T>::value ? 2 : 0))
{
BOOST_ASSERT(r);
}
/** Constructor
This constructs a non-owning pointer that points to the same memory
resource as `alloc`, obtained by calling `alloc.resource()`. The caller
is responsible for maintaining the lifetime of the
pointed-to `boost::container::pmr::memory_resource`.
@par Constraints
@code
std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
@endcode
@par Exception Safety
No-throw guarantee.
@param alloc A `boost::container::pmr::polymorphic_allocator` to
construct from.
*/
template<class T>
storage_ptr(
container::pmr::polymorphic_allocator<T> const& alloc) noexcept
: i_(reinterpret_cast<std::uintptr_t>(
alloc.resource()))
{
}
/** Move constructor
This function constructs a pointer that
points to the same memory resource as `other`,
with the same ownership:
@li If `other` is non-owning, then `*this`
will be be non-owning.
@li If `other` has shared ownership, then
ownership will be transferred to `*this`.
After construction, `other` will point
to the [default memory resource].
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The pointer to construct from.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
storage_ptr(
storage_ptr&& other) noexcept
: i_(detail::exchange(other.i_, 0))
{
}
/** Copy constructor
This function constructs a pointer that
points to the same memory resource as `other`,
with the same ownership:
@li If `other` is non-owning, then `*this`
will be be non-owning.
@li If `other` has shared ownership, then
`*this` will acquire shared ownership.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The pointer to construct from.
*/
storage_ptr(
storage_ptr const& other) noexcept
: i_(other.i_)
{
addref();
}
/** Move assignment
This function assigns a pointer that
points to the same memory resource as `other`,
with the same ownership:
@li If `other` is non-owning, then `*this`
will be be non-owning.
@li If `other` has shared ownership, then
ownership will be transferred to `*this`.
After assignment, `other` will point
to the [default memory resource].
If `*this` previously had shared ownership,
it is released before the function returns.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to move.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
storage_ptr&
operator=(
storage_ptr&& other) noexcept
{
release();
i_ = detail::exchange(other.i_, 0);
return *this;
}
/** Copy assignment.
This function assigns a pointer that
points to the same memory resource as `other`,
with the same ownership:
@li If `other` is non-owning, then `*this`
will be be non-owning.
@li If `other` has shared ownership, then
`*this` will acquire shared ownership.
If `*this` previously had shared ownership,
it is released before the function returns.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to copy.
*/
storage_ptr&
operator=(
storage_ptr const& other) noexcept
{
other.addref();
release();
i_ = other.i_;
return *this;
}
/** Return `true` if ownership of the memory resource is shared.
This function returns true for memory resources
created using @ref make_shared_resource.
*/
bool
is_shared() const noexcept
{
return (i_ & 1) != 0;
}
/** Return `true` if calling `deallocate` on the memory resource has no effect.
This function is used to determine if the deallocate
function of the pointed to memory resource is trivial.
The value of @ref is_deallocate_trivial is evaluated
and saved when the memory resource is constructed
and the type is known, before the type is erased.
*/
bool
is_deallocate_trivial() const noexcept
{
return (i_ & 2) != 0;
}
/** Return `true` if ownership of the memory resource is not shared and deallocate is trivial.
This function is used to determine if calls to deallocate
can effectively be skipped.
@par Effects
Returns `! this->is_shared() && this->is_deallocate_trivial()`
*/
bool
is_not_shared_and_deallocate_is_trivial() const noexcept
{
return (i_ & 3) == 2;
}
/** Return a pointer to the memory resource.
This function returns a pointer to the
referenced `boost::container::pmr::memory_resource`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
container::pmr::memory_resource*
get() const noexcept
{
if(i_ != 0)
return reinterpret_cast<
container::pmr::memory_resource*>(i_ & ~3);
return default_resource::get();
}
/** Return a pointer to the memory resource.
This function returns a pointer to the
referenced `boost::container::pmr::memory_resource`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
container::pmr::memory_resource*
operator->() const noexcept
{
return get();
}
/** Return a reference to the memory resource.
This function returns a reference to the
pointed-to `boost::container::pmr::memory_resource`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
container::pmr::memory_resource&
operator*() const noexcept
{
return *get();
}
template<class U, class... Args>
friend
storage_ptr
make_shared_resource(Args&&... args);
};
#if defined(_MSC_VER)
# pragma warning( push )
# if !defined(__clang__) && _MSC_VER <= 1900
# pragma warning( disable : 4702 )
# endif
#endif
/** Return shared ownership of a new, dynamically allocated memory resource.
This function dynamically allocates a new memory resource
as if by `operator new` that uses shared ownership. The
lifetime of the memory resource will be extended until
the last @ref storage_ptr which points to it is destroyed.
@par Mandates
@code
std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
@endcode
@par Complexity
Same as `new U( std::forward<Args>(args)... )`.
@par Exception Safety
Strong guarantee.
@tparam U The type of memory resource to create.
@param args Parameters forwarded to the constructor of `U`.
*/
template<class U, class... Args>
storage_ptr
make_shared_resource(Args&&... args)
{
// If this generates an error, it means that
// `T` is not a memory resource.
BOOST_STATIC_ASSERT(
std::is_base_of<
container::pmr::memory_resource, U>::value);
return storage_ptr(new
detail::shared_resource_impl<U>(
std::forward<Args>(args)...));
}
#if defined(_MSC_VER)
# pragma warning( pop )
#endif
/** Return true if two storage pointers point to the same memory resource.
This function returns `true` if the
`boost::container::pmr::memory_resource` objects pointed to by `lhs` and
`rhs` have the same address.
*/
inline
bool
operator==(
storage_ptr const& lhs,
storage_ptr const& rhs) noexcept
{
return lhs.get() == rhs.get();
}
/** Return true if two storage pointers point to different memory resources.
This function returns `true` if the
`boost::container::pmr::memory_resource` objects pointed to by `lhs` and
`rhs` have different addresses.
*/
inline
bool
operator!=(
storage_ptr const& lhs,
storage_ptr const& rhs) noexcept
{
return lhs.get() != rhs.get();
}
} // namespace json
} // namespace boost
#endif