////
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` 本库中的可变长度容器均使用动态分配的内存来存储其内容。调用方可通过在特定构造函数和函数参数列表中指定 &lt;<ref_storage_ptr>&gt; ，以控制所使用的内存分配策略。&lt;<ref_storage_ptr>&gt; 具有以下特性：</ref_storage_ptr></ref_storage_ptr>

* 存储指针始终指向一个有效的、
类型擦除后的 {ref_memory_resource}。

* 默认构造的存储指针引用
&lt;<default_memory_resource,default resource="">&gt;（默认资源），这是一个由实现定义的实例，始终使用等效于全局 operator new 和 operator delete 的方式。</default_memory_resource,default>

* 从 {ref_memory_resource} 构造的存储指针，或
从 {ref_polymorphic_allocator} 构造的存储指针，不获取所有权；调用方负责确保资源的生命周期持续到不再被引用为止。

* 通过 &lt;<ref_make_shared_resource>&gt; 获取的存储指针会获得</ref_make_shared_resource>
对内存资源的共享所有权；该资源的生命周期将延长，直至所有该存储指针的副本均被销毁。

* 存储指针会记录 &lt;<ref_is_deallocate_trivial>&gt; 的值，</ref_is_deallocate_trivial>
（&lt;&gt; 值的记录是）在对资源进行类型擦除之前进行的,从而允许在运行时查询该值。

此列表列出了在使用该库时可用的所有与分配相关的类型和函数：

.Functions and Types
|===
|Name|Description

| <<ref_get_null_resource>>
| Returns a pointer to a memory resource instance which always throws an
分配内存时抛出异常。此机制用于确保解析或容器操作不会进行动态内存分配，从而维持该不变性。

| <<ref_is_deallocate_trivial>>
| A customization point allowing a memory resource type to indicate that calls
deallocate 的调用是平凡的。

| <<ref_make_shared_resource>>
| A function returning a smart pointer with shared ownership of a newly
分配的内存资源具有共享所有权。

| {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 函数。所分配的内存不会被释放，直到该资源被销毁，因此在解析场景下速度很快，但不适合执行修改操作。

| {ref_polymorphic_allocator}
| An {req_Allocator} which uses a reference to a {ref_memory_resource} to
执行内存分配。

| <<ref_static_resource>>
| A memory resource that uses a single caller provided buffer. No dynamic
分配。该资源在解析时速度很快，但不适合执行修改操作。

| <<ref_storage_ptr>>
| A smart pointer through which a {ref_memory_resource} is managed and
访问. |===

== 默认内存资源
默认内存资源使用全局 `operator new` 和 `operator delete` 来进行内存分配。该资源不采用引用计数，且其 deallocate 函数是非平凡的。所有默认构造的 &lt;<ref_storage_ptr>&gt; 对象均引用同一个内存资源：</ref_storage_ptr>

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_1,indent=0]
----

默认构造的库容器使用默认内存资源：

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_2,indent=0]
----

默认内存资源适用于一般用途。它在解析时提供合理的性能，在修改容器内容时则具有保守的内存使用特性。

[NOTE]
该内存资源不保证与 `boost::container::pmr::get_default_resource` 的返回结果相同，也无法通过 `boost::container::pmr::set_default_resource` 进行更改。

== 单调资源
考虑解析过程中的内存分配模式：当遇到数组、对象或字符串时，解析器会将其元素累积在临时存储区中。当所有元素都已知后，在构造值时会向内存资源发起一次内存分配请求。因此，解析过程仅在容器最终大小确定时进行一次分配和构造，不会发生内存重分配；也就是说，内存缓冲区无需通过分配更大的新缓冲区并释放旧缓冲区的方式来扩容。

&lt;<ref_monotonic_resource>&gt; 通过在内部分配逐渐增大的全局内存块，并将这些块切分为更小的部分以满足分配请求，从而优化了该内存分配模式。它具有平凡的 deallocate 函数，实际上不会释放内存，直到该资源被销毁。因此，它非常适合用于解析 JSON 后仅检查结果值而不对其进行修改的场景。</ref_monotonic_resource>

在调用 &lt;<ref_parse>&gt; 时，可按如下方式指定构造值所使用的内存资源：</ref_parse>

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_3,indent=0]
----

或者，可按如下方式解析到一个对内存资源具有共享所有权的值：

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_4,indent=0]
----

单调资源在构造时可选择性地指定一个初始缓冲区，优先使用该缓冲区，之后才会转而使用堆内存。这使得调用方可以利用栈空间，避免对大多数 JSON 解析进行动态分配；仅当输入的 JSON 大于平均水平时，才会退回到从堆中进行动态分配，如下所示：

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_5,indent=0]
----

== 静态资源
&lt;<ref_static_resource>&gt; 由调用方提供的缓冲区构造而成，并从该缓冲区满足所有内存分配请求。一旦缓冲区耗尽，后续的分配调用将抛出 `std::bad_alloc` 异常。该资源提供一个简单的不变式：永不执行动态堆分配。</ref_static_resource>

要使用该资源，请用一个局部缓冲区对其进行构造：

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_6,indent=0]
----

== 空资源
函数 &lt;<ref_get_null_resource>&gt; 返回一个全局的空资源实例。该资源提供一个简单的不变式：所有内存分配调用都会抛出 `std::bad_alloc` 异常。通过使用空资源实例，可确保解析过程绝不会进行堆内存分配。这一点将在后文进一步详细说明。</ref_get_null_resource>

== 分配器传播
容器 &lt;<ref_array>&gt;, &lt;<ref_object>&gt; 和 &lt;<ref_value>&gt; 在构造时所使用的内存资源会自动传播给其子元素：</ref_value></ref_object></ref_array>

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_7,indent=0]
----

这种传播是递归进行的：嵌套容器中的所有子容器都会获得相同的内存资源。一旦容器被构造，其内存资源就永远无法更改。

== 资源生命周期
需要注意的是，&lt;<ref_storage_ptr>&gt; 同时支持共享所有权和引用生命周期模型。从内存资源指针构造时不会转移所有权：</ref_storage_ptr>

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_8,indent=0]
----

以这种方式使用内存资源时（包括从 {ref_polymorphic_allocator} 构造存储指针或容器的情况），调用方必须确保资源的生命周期延长至不再被任何变量引用为止；否则，可能会出现未定义行为。

通过函数 &lt;<ref_make_shared_resource>&gt; 可实现共享所有权，该函数会动态分配内存以创建一个新的、带引用计数的内存资源，并将其以 &lt;<ref_storage_ptr>&gt; 的形式返回：</ref_storage_ptr></ref_make_shared_resource>

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_9,indent=0]
----

当存储指针以这种方式构造时，其所引用的内存资源的生命周期将延长，直至所有引用它的变量均被销毁为止。

== 用户定义资源
要实现自定义内存分配策略，请从 {ref_memory_resource} 派生类，并实现函数 `do_allocate`、`do_deallocate` 和 `do_is_equal`，如下例所示，该示例将其执行的每个操作记录到控制台：

[source]
----
include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_10,indent=0]
----
