1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
////
Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru)
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
////
= `storage_ptr`
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 <<ref_storage_ptr>> in select constructors and
function parameter lists. A <<ref_storage_ptr>> has these properties:
* A storage pointer always points to a valid,
type-erased {ref_memory_resource}.
* Default-constructed storage pointers reference the
<<default_memory_resource,default resource>>, an implementation-defined
instance which always uses the equivalent of global operator new and delete.
* Storage pointers constructed from a {ref_memory_resource} or
{ref_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 <<ref_make_shared_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 <<ref_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:
.Functions and Types
|===
|Name|Description
| <<ref_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.
| <<ref_is_deallocate_trivial>>
| A customization point allowing a memory resource type to indicate that calls
to deallocate are trivial.
| <<ref_make_shared_resource>>
| A function returning a smart pointer with shared ownership of a newly
allocated memory resource.
| {ref_memory_resource}
| The abstract base class representing an allocator.
| <<ref_monotonic_resource>>
| 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.
| {ref_polymorphic_allocator}
| An {req_Allocator} which uses a reference to a {ref_memory_resource} to
perform allocations.
| <<ref_static_resource>>
| A memory resource that uses a single caller provided buffer. No dynamic
allocations are used. This is fast for parsing but not suited for
performing modifications.
| <<ref_storage_ptr>>
| A smart pointer through which a {ref_memory_resource} is managed and
accessed.
|===
== Default Memory Resource
The default memory resource uses the global `operator new` and `operator
delete` to allocate memory. This resource is not reference counted and has
a non-trivial deallocate function. All default-constructed <<ref_storage_ptr>>
objects reference the same memory resource:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_1,indent=0]
----
Default-constructed library containers use the default memory resource:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_2,indent=0]
----
The default memory resource is well suited for general usage. It offers
reasonable performance for parsing, and conservative memory usage for
modification of the contents of containers.
[NOTE]
This memory resource is not guaranteed to be the same as the result of
`boost::container::pmr::get_default_resource`. It also cannot be changed with
`boost::container::pmr::set_default_resource`.
== Monotonic Resource
Consider the pattern of memory allocation during parsing: when an array,
object, or string is encountered the parser accumulates elements in its
temporary storage area. When all of the elements are known, a single memory
allocation is requested from the resource when constructing the value. Thus,
parsing only allocates and constructs containers at their final size. Memory is
not reallocated; that is, a memory buffer never needs to grow by allocating
a new larger buffer and deallocating the previous buffer.
The <<ref_monotonic_resource>> optimizes this memory allocation pattern by
allocating increasingly large blocks of global memory internally and parceling
those blocks out in smaller pieces to fulfill allocation requests. It has
a trivial deallocate function. The monotonic resource does not actually
deallocate memory until the resource is destroyed. Thus, it is ideally suited
for the use-case where JSON is parsed, and the resulting value is then
inspected but not modified.
The resource to use when constructing values may be specified in calls to
<<ref_parse>> as shown here:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_3,indent=0]
----
Or, to parse into a value with shared ownership of the memory resource:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_4,indent=0]
----
A monotonic resource may be optionally constructed with an initial buffer to
use first, before going to the heap. This allows the caller to use stack space
and avoid dynamic allocations for most parsed JSON, falling back to dynamic
allocation from the heap if the input JSON is larger than average, as shown
here:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_5,indent=0]
----
== Static Resource
A <<ref_static_resource>> constructs from a caller-provided buffer, and
satisfies all memory allocation requests from the buffer. Once the buffer is
exhausted, subsequent calls to allocate throw the exception `std::bad_alloc`.
The resource offers a simple invariant: dynamic heap allocations are never
performed.
To use the resource, construct it with a local buffer:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_6,indent=0]
----
== Null Resource
The function <<ref_get_null_resource>> returns a global instance of the null
resource. This resource offers a simple invariant: all calls to allocate will
throw the exception `std::bad_alloc`. An instance of the null resource can be
used to make parsing guarantee that allocations from the heap are never made.
This is explored in more detail in a later section.
== Allocator Propagation
The containers <<ref_array>>, <<ref_object>>, and <<ref_value>> all propagate
the memory resource they were constructed with to child elements:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_7,indent=0]
----
This propagation acts recursively, containers within containers will
all have the resource propagated. Once a container is constructed,
its memory resource can never be changed.
== Resource Lifetime
It is important to note that <<ref_storage_ptr>> supports both shared-ownership
and reference lifetime models. Construction from a memory resource pointer does
not transfer ownership:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_8,indent=0]
----
When using a memory resource in this fashion, including the case where
a storage pointer or container is constructed from
a {ref_polymorphic_allocator}, the caller must ensure that the lifetime of the
resource is extended until it is no longer referenced by any variables;
otherwise, undefined behavior is possible.
Shared ownership is achieved using the function <<ref_make_shared_resource>>,
which creates a new, reference-counted memory resource using a dynamic memory
allocation and returns it as a <<ref_storage_ptr>>:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_9,indent=0]
----
When a storage pointer is constructed this way, the lifetime of the referenced
memory resource is extended until all variables which reference it are
destroyed.
== User-Defined Resource
To implement custom memory allocation strategies, derive your class from
{ref_memory_resource} and implement the functions `do_allocate`,
`do_deallocate`, and `do_is_equal` as seen in this example below, which logs
each operation it performs to the console:
[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_10,indent=0]
----
|