boost/json/monotonic_resource.hpp
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@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_MONOTONIC_RESOURCE_HPP
#define BOOST_JSON_MONOTONIC_RESOURCE_HPP
#include <boost/container/pmr/memory_resource.hpp>
#include <boost/json/detail/config.hpp>
#include <boost/json/memory_resource.hpp>
#include <boost/json/storage_ptr.hpp>
#include <cstddef>
#include <utility>
namespace boost {
namespace json {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
#endif
//----------------------------------------------------------
/** A dynamically allocating resource with a trivial deallocate
This memory resource is a special-purpose resource
that releases allocated memory only when the resource
is destroyed (or when @ref release is called).
It has a trivial deallocate function; that is, the
metafunction @ref is_deallocate_trivial returns `true`.
\n
The resource can be constructed with an initial buffer.
If there is no initial buffer, or if the buffer is
exhausted, subsequent dynamic allocations are made from
the system heap. The size of buffers obtained in this
fashion follow a geometric progression.
\n
The purpose of this resource is to optimize the use
case for performing many allocations, followed by
deallocating everything at once. This is precisely the
pattern of memory allocation which occurs when parsing:
allocation is performed for each parsed element, and
when the the resulting @ref value is no longer needed,
the entire structure is destroyed. However, it is not
suited for modifying the value after parsing is
complete; reallocations waste memory, since the
older buffer is not reclaimed until the resource
is destroyed.
@par Example
This parses a JSON text into a value which uses a local
stack buffer, then prints the result.
@code
unsigned char buf[ 4000 ];
monotonic_resource mr( buf );
// Parse the string, using our memory resource
auto const jv = parse( "[1,2,3]", &mr );
// Print the JSON
std::cout << jv;
@endcode
@note The total amount of memory dynamically
allocated is monotonically increasing; That is,
it never decreases.
@par Thread Safety
Members of the same instance may not be
called concurrently.
@see
https://en.wikipedia.org/wiki/Region-based_memory_management
*/
class
BOOST_JSON_DECL
BOOST_SYMBOL_VISIBLE
monotonic_resource final
: public container::pmr::memory_resource
{
struct block;
struct block_base
{
void* p;
std::size_t avail;
std::size_t size;
block_base* next;
};
block_base buffer_;
block_base* head_ = &buffer_;
std::size_t next_size_ = 1024;
storage_ptr upstream_;
static constexpr std::size_t min_size_ = 1024;
inline static constexpr std::size_t max_size();
inline static std::size_t round_pow2(
std::size_t n) noexcept;
inline static std::size_t next_pow2(
std::size_t n) noexcept;
public:
/// Copy constructor (deleted)
monotonic_resource(
monotonic_resource const&) = delete;
/// Copy assignment (deleted)
monotonic_resource& operator=(
monotonic_resource const&) = delete;
/** Destructor
Deallocates all the memory owned by this resource.
@par Effects
@code
this->release();
@endcode
@par Complexity
Linear in the number of deallocations performed.
@par Exception Safety
No-throw guarantee.
*/
~monotonic_resource();
/** Constructor
This constructs the resource and indicates
that the first internal dynamic allocation
shall be at least `initial_size` bytes.
\n
This constructor is guaranteed not to perform
any dynamic allocations.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param initial_size The size of the first
internal dynamic allocation. If this is lower
than the implementation-defined lower limit, then
the lower limit is used instead.
@param upstream An optional upstream memory resource
to use for performing internal dynamic allocations.
If this parameter is omitted, the default resource
is used.
*/
explicit
monotonic_resource(
std::size_t initial_size = 1024,
storage_ptr upstream = {}) noexcept;
/** Constructor
This constructs the resource and indicates that
subsequent allocations should use the specified
caller-owned buffer.
When this buffer is exhausted, dynamic allocations
from the upstream resource are made.
\n
This constructor is guaranteed not to perform
any dynamic allocations.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param buffer The buffer to use.
Ownership is not transferred; the caller is
responsible for ensuring that the lifetime of
the buffer extends until the resource is destroyed.
@param size The number of valid bytes pointed
to by `buffer`.
@param upstream An optional upstream memory resource
to use for performing internal dynamic allocations.
If this parameter is omitted, the default resource
is used.
*/
/** @{ */
monotonic_resource(
unsigned char* buffer,
std::size_t size,
storage_ptr upstream = {}) noexcept;
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
monotonic_resource(
std::byte* buffer,
std::size_t size,
storage_ptr upstream) noexcept
: monotonic_resource(reinterpret_cast<
unsigned char*>(buffer), size,
std::move(upstream))
{
}
#endif
/** @} */
/** Constructor
This constructs the resource and indicates that
subsequent allocations should use the specified
caller-owned buffer.
When this buffer is exhausted, dynamic allocations
from the upstream resource are made.
\n
This constructor is guaranteed not to perform
any dynamic allocations.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param buffer The buffer to use.
Ownership is not transferred; the caller is
responsible for ensuring that the lifetime of
the buffer extends until the resource is destroyed.
@param upstream An optional upstream memory resource
to use for performing internal dynamic allocations.
If this parameter is omitted, the default resource
is used.
*/
/** @{ */
template<std::size_t N>
explicit
monotonic_resource(
unsigned char(&buffer)[N],
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
N, std::move(upstream))
{
}
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
template<std::size_t N>
explicit
monotonic_resource(
std::byte(&buffer)[N],
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
N, std::move(upstream))
{
}
#endif
/** @} */
#ifndef BOOST_JSON_DOCS
// Safety net for accidental buffer overflows
template<std::size_t N>
monotonic_resource(
unsigned char(&buffer)[N],
std::size_t n,
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
n, std::move(upstream))
{
// If this goes off, check your parameters
// closely, chances are you passed an array
// thinking it was a pointer.
BOOST_ASSERT(n <= N);
}
#ifdef __cpp_lib_byte
// Safety net for accidental buffer overflows
template<std::size_t N>
monotonic_resource(
std::byte(&buffer)[N],
std::size_t n,
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
n, std::move(upstream))
{
// If this goes off, check your parameters
// closely, chances are you passed an array
// thinking it was a pointer.
BOOST_ASSERT(n <= N);
}
#endif
#endif
/** Release all allocated memory.
This function deallocates all allocated memory.
If an initial buffer was provided upon construction,
then all of the bytes will be available again for
allocation. Allocated memory is deallocated even
if deallocate has not been called for some of
the allocated blocks.
@par Complexity
Linear in the number of deallocations performed.
@par Exception Safety
No-throw guarantee.
*/
void
release() noexcept;
protected:
#ifndef BOOST_JSON_DOCS
void*
do_allocate(
std::size_t n,
std::size_t align) override;
void
do_deallocate(
void* p,
std::size_t n,
std::size_t align) override;
bool
do_is_equal(
memory_resource const& mr) const noexcept override;
#endif
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
template<>
struct is_deallocate_trivial<
monotonic_resource>
{
static constexpr bool value = true;
};
} // namespace json
} // namespace boost
#endif