Add null_resource

This commit is contained in:
Vinnie Falco 2020-09-11 18:39:57 -07:00
parent fc0be9173c
commit 8bf9ebfd1b
11 changed files with 349 additions and 35 deletions

View File

@ -144,6 +144,11 @@ storage, improving performance.
[doc_quick_look_11]
With strategic use of the right memory resources, parser instance,
and calculated upper limits on buffer sizes, it is possible to parse
and examine JSON without ['any] dynamic memory allocations. This is
explored in more detail in a later section.
[/-----------------------------------------------------------------------------]
[h5 Serializing]

View File

@ -112,9 +112,9 @@ It is desired to create a single type `T` with the following properties:
* `T` supports both shared ownership, and non-ownership
* `T` interoperates with code already using `std::pmr`
The __storage_ptr__ smart pointer container used in Boost.JSON builds
and improves upon C++17's memory allocation interfaces, accomplishing
the goals above. As a result, libraries which use this type compose
The __storage_ptr__ used in Boost.JSON builds and improves
upon C++17's memory allocation interfaces, accomplishing the
goals above. As a result, libraries which use this type compose
more easily and enjoy faster compilation, as container function
definitions can be out-of-line.
@ -124,28 +124,50 @@ definitions can be out-of-line.
[section:storage_ptr The __storage_ptr__]
Instances of __value__, __object__, __array__, and __string__ all use
dynamically allocated memory. To allow callers to control the strategy
used to allocate and deallocate memory, the library provides the smart
pointer container __storage_ptr__, similar in function to the C++ standard
library's polymorphic allocator but with the following additional features:
Variable-length containers in this library all use dynamically allocated
memory to store their contents. Callers can gain control over the strategy
used for allocation by specifying a __storage_ptr__ in select constructors
and function parameter lists. A __storage_ptr__ has these properties:
* __storage_ptr__ can either function as a reference counted smart pointer or
a reference wrapper around a __memory_resource__, allowing for the lifetime of
the managed object to be extended.
* A storage pointer always points to a valid,
type-erased __memory_resource__.
* An implementation can indicate to the library that deallocation
is not necessary, allowing destructor calls to be elided in certain situations.
* Default-constructed storage pointers reference the
['default resource], an implementation-defined instance
which always uses the equivalent of global operator new
and delete.
These types and functions are available:
* Storage pointers constructed from a
[link json.ref.boost__json__memory_resource `memory_resource*`]
or __polymorphic_allocator__ do not acquire ownership; the
caller is responsible for ensuring that the lifetime of
the resource extends until it is no longer referenced.
* A storage pointer obtained from __make_counted_resource__
acquires shared ownership of the memory resource; the
lifetime of the resource is extended until all copies
of the storage pointer are destroyed.
* The storage pointer remembers the value of
__is_deallocate_trivial__ before type-erasing the resource,
allowing the value to be queried at run-time.
This lists all of the allocation-related types and functions
available when using the library:
[table Functions and Types
[ [Name] [Description] ]
[
[__is_deallocate_trivial__]
[
A customization point allowing a memory resource type
to indicate that calls to deallocate are trivial.
]
][
[__make_counted_resource__]
[
A function that returns a reference-counted storage pointer
with ownership of a new, dynamically allocated memory resource.
A function returning a smart pointer with shared
ownership of a newly allocated memory resource.
]
][
[__memory_resource__]
@ -155,26 +177,41 @@ These types and functions are available:
][
[__monotonic_resource__]
[
A memory resource that allocates zero or more blocks of memory
from which allocations are made; block is twice as large as the last.
Allocated memory is not freed until the resource is destroyed, making
it fast for parsing but not ideal for mutation.
A memory resource which allocates large blocks of memory and
has a trivial deallocate function. Allocated memory is not
freed until the resource is destroyed, making it fast for
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__]
[
An __Allocator__ which uses a reference to a
__memory_resource__ to perform allocations.
]
][
[__static_resource__]
[
A memory resource that uses a single caller provided
buffer, from which individual allocations are made.
No dynamic allocations are used.
buffer. No dynamic allocations are used. This is fast for
parsing but not suited for performing modifications.
]
][
[__storage_ptr__]
[
A smart pointer container through which a __memory_resource__
A smart pointer through which a __memory_resource__
is managed and accessed.
]
]]
[heading Default Resource]
The library provides a ['default memory resource] object which wraps calls to the
global allocation and deallocation functions (`operator new` and `operator delete`).
This memory resource is not reference counted, and requires calls to deallocate to

View File

@ -58,14 +58,14 @@
[def __key_value_pair__ [link json.ref.boost__json__key_value_pair `key_value_pair`]]
[def __kind__ [link json.ref.boost__json__kind `kind`]]
[def __make_counted_resource__ [link json.ref.boost__json__make_counted_resource `make_counted_resource`]]
[def __number__ [link json.ref.boost__json__number `number`]]
[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 __null_resource__ [link json.ref.boost__json__null_resource `null_resource`]]
[def __object__ [link json.ref.boost__json__object `object`]]
[def __parse__ [link json.ref.boost__json__parse `parse`]]
[def __parser__ [link json.ref.boost__json__parser `parser`]]
[def __parse_options__ [link json.ref.boost__json__parse_options `parse_options`]]
[def __polymorphic_allocator__ [link json.ref.boost__json__polymorphic_allocator `polymorphic_allocator`]]
[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 __serialize__ [link json.ref.boost__json__serialize `serialize`]]
[def __serializer__ [link json.ref.boost__json__serializer `serializer`]]
[def __static_resource__ [link json.ref.boost__json__static_resource `static_resource`]]
@ -95,15 +95,6 @@
[/-----------------------------------------------------------------------------]
[import ../../example/pretty.cpp]
[import ../../example/validate.cpp]
[import ../../include/boost/json/impl/serialize.ipp]
[import ../../test/doc_quick_look.cpp]
[import ../../test/memory_resource.cpp]
[import ../../test/snippets.cpp]
[/-----------------------------------------------------------------------------]
Boost.JSON is a portable C++ library which implements the
[@https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf JSON Data Interchange Syntax],
providing algorithms for parsing and serialization, as well as a robust
@ -149,6 +140,16 @@ in memory.
]]
]
[/-----------------------------------------------------------------------------]
[import ../../example/pretty.cpp]
[import ../../example/validate.cpp]
[import ../../include/boost/json/impl/serialize.ipp]
[import ../../test/doc_allocators.cpp]
[import ../../test/doc_quick_look.cpp]
[import ../../test/memory_resource.cpp]
[import ../../test/snippets.cpp]
[include 01_overview.qbk]
[include 02_00_usage.qbk]
[include 03_examples.qbk]

View File

@ -24,6 +24,7 @@
<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__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__parser">parser</link></member>
<member><link linkend="json.ref.boost__json__parse_options">parse_options</link></member>

View File

@ -15,11 +15,15 @@
#include <boost/json/array.hpp>
#include <boost/json/error.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/memory_resource.hpp>
#include <boost/json/monotonic_resource.hpp>
#include <boost/json/null_resource.hpp>
#include <boost/json/number_cast.hpp>
#include <boost/json/object.hpp>
#include <boost/json/parse.hpp>
#include <boost/json/parse_options.hpp>
#include <boost/json/parser.hpp>
#include <boost/json/pilfer.hpp>
#include <boost/json/serialize.hpp>
#include <boost/json/serializer.hpp>
#include <boost/json/static_resource.hpp>
@ -28,6 +32,8 @@
#include <boost/json/system_error.hpp>
#include <boost/json/value.hpp>
#include <boost/json/value_from.hpp>
#include <boost/json/value_ref.hpp>
#include <boost/json/value_stack.hpp>
#include <boost/json/value_to.hpp>
// Intentionally excluded

View File

@ -0,0 +1,48 @@
//
// 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/cppalliance/json
//
#ifndef BOOST_JSON_IMPL_NULL_RESOURCE_IPP
#define BOOST_JSON_IMPL_NULL_RESOURCE_IPP
#include <boost/json/null_resource.hpp>
#include <boost/json/detail/except.hpp>
BOOST_JSON_NS_BEGIN
void*
null_resource::
do_allocate(
std::size_t,
std::size_t)
{
detail::throw_bad_alloc(
BOOST_CURRENT_LOCATION);
}
void
null_resource::
do_deallocate(
void*,
std::size_t,
std::size_t)
{
// do nothing
}
bool
null_resource::
do_is_equal(
memory_resource const& mr) const noexcept
{
return this == &mr;
}
BOOST_JSON_NS_END
#endif

View File

@ -0,0 +1,83 @@
//
// Copyright (c) 2020 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/cppalliance/json
//
#ifndef BOOST_JSON_NULL_RESOURCE_HPP
#define BOOST_JSON_NULL_RESOURCE_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/memory_resource.hpp>
BOOST_JSON_NS_BEGIN
//----------------------------------------------------------
/** A resource which always fails.
This memory resource always throws the exception
`std::bad_alloc` in calls to `allocate`.
*/
class BOOST_JSON_CLASS_DECL
null_resource final
: public memory_resource
{
public:
/// Copy constructor (deleted)
null_resource(
null_resource const&) = delete;
/// Copy assignment (deleted)
null_resource& operator=(
null_resource const&) = delete;
/// Destructor
~null_resource() noexcept = default;
/** Constructor
This constructors 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
#endif

View File

@ -31,6 +31,7 @@ in a translation unit of the program.
#include <boost/json/impl/array.ipp>
#include <boost/json/impl/error.ipp>
#include <boost/json/impl/monotonic_resource.ipp>
#include <boost/json/impl/null_resource.ipp>
#include <boost/json/impl/object.ipp>
#include <boost/json/impl/parse.ipp>
#include <boost/json/impl/parser.ipp>

View File

@ -27,12 +27,14 @@ else
local SOURCES =
array.cpp
basic_parser.cpp
doc_allocators.cpp
doc_quick_look.cpp
error.cpp
json.cpp
kind.cpp
monotonic_resource.cpp
natvis.cpp
null_resource.cpp
number_cast.cpp
object.cpp
parse.cpp

85
test/doc_allocators.cpp Normal file
View File

@ -0,0 +1,85 @@
//
// 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/cppalliance/json
//
#include <boost/json.hpp>
#include "test_suite.hpp"
BOOST_JSON_NS_BEGIN
//[doc_allocators_1
void do_rpc( string_view s )
{
// The parser will use this storage for its temporary needs
unsigned char temp[ 4000 ];
// The null resource guarantees we will never dynamically allocate
null_resource mr1;
// Construct a strict parser using the temp buffer and no dynamic memory
parser p( &mr1, parse_options(), temp );
// Now we need a buffer to hold the actual JSON values
unsigned char buf[ 6000 ];
// The static resource is monotonic, using only a caller-provided buffer
static_resource mr2( buf );
// We need to catch any exceptions thrown by the two memory resources
try
{
// This error code indicates errors not related to memory exhaustion
error_code ec;
// Parse the entire string we received from the network client
p.write( s, ec );
// Inform the parser that the complete input has been provided
if(! ec )
p.finish( ec );
if(! ec )
{
// Retrieve the value. It will use `buf` for storage.
value jv = p.release();
// At this point we can inspect jv and perform the requested RPC.
}
else
{
// An error occurred. A real program would report the error
// message back to the network client, indicating that the
// received JSON was invalid.
}
}
catch(std::bad_alloc const&)
{
// The memory needed to parse this JSON exceeded our statically
// define upper limits. A real program would send an error message
// back to the network client informing that their JSON is too large.
}
}
//]
//----------------------------------------------------------
class doc_allocators_test
{
public:
void
run()
{
BOOST_TEST_PASS();
}
};
TEST_SUITE(doc_allocators_test, "boost.json.doc_allocators");
BOOST_JSON_NS_END

45
test/null_resource.cpp Normal file
View File

@ -0,0 +1,45 @@
//
// Copyright (c) 2020 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/cppalliance/json
//
// Test that header file is self-contained.
#include <boost/json/null_resource.hpp>
#include "test_suite.hpp"
BOOST_JSON_NS_BEGIN
class null_resource_test
{
public:
void
test()
{
null_resource mr;
BOOST_TEST_THROWS(
mr.allocate(16),
std::bad_alloc);
char buf[128];
// no-op
mr.deallocate(&buf[0], 128);
BOOST_TEST(mr == mr);
null_resource mr2;
BOOST_TEST(mr != mr2);
}
void
run()
{
test();
}
};
TEST_SUITE(null_resource_test, "boost.json.null_resource");
BOOST_JSON_NS_END