diff --git a/doc/qbk/02_01_quick_look.qbk b/doc/qbk/02_01_quick_look.qbk
index 99a0a2ac..fba5650f 100644
--- a/doc/qbk/02_01_quick_look.qbk
+++ b/doc/qbk/02_01_quick_look.qbk
@@ -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]
diff --git a/doc/qbk/02_07_allocators.qbk b/doc/qbk/02_07_allocators.qbk
index 3c7e5f1c..569b1c2d 100644
--- a/doc/qbk/02_07_allocators.qbk
+++ b/doc/qbk/02_07_allocators.qbk
@@ -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
diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk
index bd08667f..94c5f8ae 100644
--- a/doc/qbk/main.qbk
+++ b/doc/qbk/main.qbk
@@ -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]
diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml
index 93da45b0..14a5dbb1 100644
--- a/doc/qbk/quickref.xml
+++ b/doc/qbk/quickref.xml
@@ -24,6 +24,7 @@
basic_parserkey_value_pairmonotonic_resource
+ null_resourceobjectparserparse_options
diff --git a/include/boost/json.hpp b/include/boost/json.hpp
index 42b3a692..0d02df75 100644
--- a/include/boost/json.hpp
+++ b/include/boost/json.hpp
@@ -15,11 +15,15 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -28,6 +32,8 @@
#include
#include
#include
+#include
+#include
#include
// Intentionally excluded
diff --git a/include/boost/json/impl/null_resource.ipp b/include/boost/json/impl/null_resource.ipp
new file mode 100644
index 00000000..05355ee0
--- /dev/null
+++ b/include/boost/json/impl/null_resource.ipp
@@ -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
+#include
+
+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
diff --git a/include/boost/json/null_resource.hpp b/include/boost/json/null_resource.hpp
new file mode 100644
index 00000000..5d317e33
--- /dev/null
+++ b/include/boost/json/null_resource.hpp
@@ -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
+#include
+
+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
diff --git a/include/boost/json/src.hpp b/include/boost/json/src.hpp
index ba821dba..216b82b1 100644
--- a/include/boost/json/src.hpp
+++ b/include/boost/json/src.hpp
@@ -31,6 +31,7 @@ in a translation unit of the program.
#include
#include
#include
+#include
#include
#include
#include
diff --git a/test/Jamfile b/test/Jamfile
index 430b5650..5a58c7c7 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -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
diff --git a/test/doc_allocators.cpp b/test/doc_allocators.cpp
new file mode 100644
index 00000000..9a1f9f95
--- /dev/null
+++ b/test/doc_allocators.cpp
@@ -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
+
+#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
diff --git a/test/null_resource.cpp b/test/null_resource.cpp
new file mode 100644
index 00000000..eedeb972
--- /dev/null
+++ b/test/null_resource.cpp
@@ -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
+
+#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