null_resource is a singleton

This commit is contained in:
Vinnie Falco 2020-09-16 16:45:06 -07:00
parent 7bde63b316
commit a2917e736a
9 changed files with 120 additions and 114 deletions

View File

@ -158,6 +158,14 @@ available when using the library:
[table Functions and Types [table Functions and Types
[ [Name] [Description] ] [ [Name] [Description] ]
[ [
[__get_null_resource__]
[
Returns a pointer to a memory resource instance which
always throws an exception upon allocation. This is
used to to achieve the invariant that no parsing or
container operation will dynamically allocate memory.
]
][
[__is_deallocate_trivial__] [__is_deallocate_trivial__]
[ [
A customization point allowing a memory resource type A customization point allowing a memory resource type
@ -182,13 +190,6 @@ available when using the library:
freed until the resource is destroyed, making it fast for freed until the resource is destroyed, making it fast for
parsing but not suited for performing modifications. parsing but not suited for performing modifications.
] ]
][
[__null_resource__]
[
A memory resource always throws an exception upon allocation.
This is used to to achieve the invariant that no parsing
or container operation will dynamically allocate memory.
]
][ ][
[__polymorphic_allocator__] [__polymorphic_allocator__]
[ [
@ -285,11 +286,12 @@ To use the resource, construct it with a local buffer:
[heading Null Resource] [heading Null Resource]
The __null_resource__ can only be default constructed. It offers a simple The function __get_null_resource__ returns a global instance of the
invariant: all calls to allocate will throw the exception `std::bad_alloc`. null resource. This resource offers a simple invariant: all calls to
An instance of the null resource can be used to make parsing guarantee allocate will throw the exception `std::bad_alloc`. An instance of
that allocations from the heap are never made. This is explored in more the null resource can be used to make parsing guarantee that
detail in a later section. allocations from the heap are never made. This is explored
in more detail in a later section.
[/-----------------------------------------------------------------------------] [/-----------------------------------------------------------------------------]

View File

@ -52,6 +52,7 @@
[def __error_code__ [link json.ref.boost__json__error_code `error_code`]] [def __error_code__ [link json.ref.boost__json__error_code `error_code`]]
[def __error_condition__ [link json.ref.boost__json__error_condition `error_condition`]] [def __error_condition__ [link json.ref.boost__json__error_condition `error_condition`]]
[def __get__ [link json.ref.boost__json__get `get`]] [def __get__ [link json.ref.boost__json__get `get`]]
[def __get_null_resource__ [link json.ref.boost__json__get_null_resource `get_null_resource`]]
[def __has_value_from__ [link json.ref.boost__json__has_value_from `has_value_from`]] [def __has_value_from__ [link json.ref.boost__json__has_value_from `has_value_from`]]
[def __has_value_to__ [link json.ref.boost__json__has_value_to `has_value_to`]] [def __has_value_to__ [link json.ref.boost__json__has_value_to `has_value_to`]]
[def __is_deallocate_trivial__ [link json.ref.boost__json__is_deallocate_trivial `is_deallocate_trivial`]] [def __is_deallocate_trivial__ [link json.ref.boost__json__is_deallocate_trivial `is_deallocate_trivial`]]
@ -60,7 +61,6 @@
[def __make_counted_resource__ [link json.ref.boost__json__make_counted_resource `make_counted_resource`]] [def __make_counted_resource__ [link json.ref.boost__json__make_counted_resource `make_counted_resource`]]
[def __memory_resource__ [link json.ref.boost__json__memory_resource `memory_resource`]] [def __memory_resource__ [link json.ref.boost__json__memory_resource `memory_resource`]]
[def __monotonic_resource__ [link json.ref.boost__json__monotonic_resource `monotonic_resource`]] [def __monotonic_resource__ [link json.ref.boost__json__monotonic_resource `monotonic_resource`]]
[def __null_resource__ [link json.ref.boost__json__null_resource `null_resource`]]
[def __number_cast__ [link json.ref.boost__json__number_cast `number_cast`]] [def __number_cast__ [link json.ref.boost__json__number_cast `number_cast`]]
[def __object__ [link json.ref.boost__json__object `object`]] [def __object__ [link json.ref.boost__json__object `object`]]
[def __parse__ [link json.ref.boost__json__parse `parse`]] [def __parse__ [link json.ref.boost__json__parse `parse`]]

View File

@ -24,7 +24,6 @@
<member><link linkend="json.ref.boost__json__basic_parser">basic_parser</link></member> <member><link linkend="json.ref.boost__json__basic_parser">basic_parser</link></member>
<member><link linkend="json.ref.boost__json__key_value_pair">key_value_pair</link></member> <member><link linkend="json.ref.boost__json__key_value_pair">key_value_pair</link></member>
<member><link linkend="json.ref.boost__json__monotonic_resource">monotonic_resource</link></member> <member><link linkend="json.ref.boost__json__monotonic_resource">monotonic_resource</link></member>
<member><link linkend="json.ref.boost__json__null_resource">null_resource</link></member>
<member><link linkend="json.ref.boost__json__object">object</link></member> <member><link linkend="json.ref.boost__json__object">object</link></member>
<member><link linkend="json.ref.boost__json__parser">parser</link></member> <member><link linkend="json.ref.boost__json__parser">parser</link></member>
<member><link linkend="json.ref.boost__json__parse_options">parse_options</link></member> <member><link linkend="json.ref.boost__json__parse_options">parse_options</link></member>
@ -41,6 +40,7 @@
<bridgehead renderas="sect3">Functions</bridgehead> <bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="json.ref.boost__json__get">get</link></member> <member><link linkend="json.ref.boost__json__get">get</link></member>
<member><link linkend="json.ref.boost__json__get_null_resource">get_null_resource</link></member>
<member><link linkend="json.ref.boost__json__make_counted_resource">make_counted_resource</link></member> <member><link linkend="json.ref.boost__json__make_counted_resource">make_counted_resource</link></member>
<member><link linkend="json.ref.boost__json__number_cast">number_cast</link></member> <member><link linkend="json.ref.boost__json__number_cast">number_cast</link></member>
<member><link linkend="json.ref.boost__json__parse">parse</link></member> <member><link linkend="json.ref.boost__json__parse">parse</link></member>

View File

@ -67,8 +67,7 @@ struct FuzzHelper {
void useDynLess() { void useDynLess() {
// this is on the heap because the size is chosen dynamically // this is on the heap because the size is chosen dynamically
std::unique_ptr<unsigned char[]> temp(new unsigned char[memlimit1]); std::unique_ptr<unsigned char[]> temp(new unsigned char[memlimit1]);
null_resource nr; parser p(get_null_resource(),
parser p(&nr,
opt, opt,
temp.get(), temp.get(),
memlimit1); memlimit1);

View File

@ -15,32 +15,85 @@
BOOST_JSON_NS_BEGIN BOOST_JSON_NS_BEGIN
void* namespace detail {
null_resource::
do_allocate(
std::size_t,
std::size_t)
{
detail::throw_bad_alloc(
BOOST_CURRENT_LOCATION);
}
void /** A resource which always fails.
null_resource::
do_deallocate(
void*,
std::size_t,
std::size_t)
{
// do nothing
}
bool This memory resource always throws the exception
null_resource:: `std::bad_alloc` in calls to `allocate`.
do_is_equal( */
memory_resource const& mr) const noexcept class null_resource final
: public memory_resource
{ {
return this == &mr; public:
/// Copy constructor (deleted)
null_resource(
null_resource const&) = delete;
/// Copy assignment (deleted)
null_resource& operator=(
null_resource const&) = delete;
/** Destructor
This destroys the resource.
@par Complexity
Constant.
@part Exception Safety
No-throw guarantee.
*/
~null_resource() noexcept = default;
/** Constructor
This constructs the resource.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
/** @{ */
null_resource() noexcept = default;
protected:
void*
do_allocate(
std::size_t,
std::size_t) override
{
detail::throw_bad_alloc(
BOOST_CURRENT_LOCATION);
}
void
do_deallocate(
void*,
std::size_t,
std::size_t) override
{
// do nothing
}
bool
do_is_equal(
memory_resource const& mr
) const noexcept override
{
return this == &mr;
}
};
} // detail
memory_resource*
get_null_resource() noexcept
{
static detail::null_resource mr;
return &mr;
} }
BOOST_JSON_NS_END BOOST_JSON_NS_END

View File

@ -15,77 +15,20 @@
BOOST_JSON_NS_BEGIN BOOST_JSON_NS_BEGIN
//---------------------------------------------------------- /** Return a pointer to the null resource.
/** A resource which always fails.
This memory resource always throws the exception This memory resource always throws the exception
`std::bad_alloc` in calls to `allocate`. `std::bad_alloc` in calls to `allocate`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/ */
class BOOST_JSON_CLASS_DECL BOOST_JSON_DECL
null_resource final memory_resource*
: public memory_resource get_null_resource() noexcept;
{
public:
/// Copy constructor (deleted)
null_resource(
null_resource const&) = delete;
/// Copy assignment (deleted)
null_resource& operator=(
null_resource const&) = delete;
/** Destructor
This destroys the resource.
@par Complexity
Constant.
@part Exception Safety
No-throw guarantee.
*/
~null_resource() noexcept = default;
/** Constructor
This constructs the resource.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
/** @{ */
null_resource() noexcept = default;
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
};
template<>
struct is_deallocate_trivial<
null_resource>
{
static constexpr bool value = true;
};
BOOST_JSON_NS_END BOOST_JSON_NS_END

View File

@ -1053,7 +1053,14 @@ public:
return *this = value(init, storage()); return *this = value(init, storage());
} }
/** Assignment. /** Assignment
Assigns `t` to `*this`.
@par Effects
@code
*this = value( std::forward<T>(t), this->storage() );
@endcode
@par Constraints @par Constraints
@code @code

View File

@ -195,9 +195,11 @@ parser p(
template< class Handler > template< class Handler >
void do_rpc( string_view s, Handler&& handler ) void do_rpc( string_view s, Handler&& handler )
{ {
null_resource mr1; // The null resource guarantees we will never dynamically allocate
unsigned char temp[ 4096 ]; // The parser will use this storage for its temporary needs unsigned char temp[ 4096 ]; // The parser will use this storage for its temporary needs
parser p( &mr1, parse_options(), temp ); // Construct a strict parser using the temp buffer and no dynamic memory parser p( // Construct a strict parser using the temp buffer and no dynamic memory
get_null_resource(), // The null resource guarantees we will never dynamically allocate
parse_options(), // Default constructed parse options allow only standard JSON
temp );
unsigned char buf[ 16384 ]; // Now we need a buffer to hold the actual JSON values unsigned char buf[ 16384 ]; // Now we need a buffer to hold the actual JSON values
static_resource mr2( buf ); // The static resource is monotonic, using only a caller-provided buffer static_resource mr2( buf ); // The static resource is monotonic, using only a caller-provided buffer

View File

@ -10,6 +10,8 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <boost/json/null_resource.hpp> #include <boost/json/null_resource.hpp>
#include <boost/json/storage_ptr.hpp>
#include "test_suite.hpp" #include "test_suite.hpp"
BOOST_JSON_NS_BEGIN BOOST_JSON_NS_BEGIN
@ -20,17 +22,15 @@ public:
void void
test() test()
{ {
null_resource mr; auto& mr = *get_null_resource();
BOOST_TEST_THROWS( BOOST_TEST_THROWS(
mr.allocate(16), mr.allocate(16),
std::bad_alloc); std::bad_alloc);
char buf[128]; char buf[128];
// no-op // no-op
mr.deallocate(&buf[0], 128); mr.deallocate(&buf[0], 128);
BOOST_TEST(
BOOST_TEST(mr == mr); mr == *get_null_resource());
null_resource mr2;
BOOST_TEST(mr != mr2);
} }
void void