Coverage Report

Created: 2026-01-25 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boost/boost/json/monotonic_resource.hpp
Line
Count
Source
1
//
2
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
4
//
5
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7
//
8
// Official repository: https://github.com/boostorg/json
9
//
10
11
#ifndef BOOST_JSON_MONOTONIC_RESOURCE_HPP
12
#define BOOST_JSON_MONOTONIC_RESOURCE_HPP
13
14
#include <boost/container/pmr/memory_resource.hpp>
15
#include <boost/json/detail/config.hpp>
16
#include <boost/json/storage_ptr.hpp>
17
#include <cstddef>
18
#include <utility>
19
20
namespace boost {
21
namespace json {
22
23
#ifdef _MSC_VER
24
#pragma warning(push)
25
#pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
26
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
27
#endif
28
29
//----------------------------------------------------------
30
31
/** A dynamically allocating resource with a trivial deallocate.
32
33
    This memory resource is a special-purpose resource that releases allocated
34
    memory only when the resource is destroyed (or when @ref release is
35
    called). It has a trivial deallocate function; that is, the metafunction
36
    @ref is_deallocate_trivial returns `true`.
37
38
    The resource can be constructed with an initial buffer. If there is no
39
    initial buffer, or if the buffer is exhausted, subsequent dynamic
40
    allocations are made from the system heap. The size of buffers obtained in
41
    this fashion follow a geometric progression.
42
43
    The purpose of this resource is to optimize the use case for performing
44
    many allocations, followed by deallocating everything at once. This is
45
    precisely the pattern of memory allocation which occurs when parsing:
46
    allocation is performed for each parsed element, and when the the resulting
47
    @ref value is no longer needed, the entire structure is destroyed. However,
48
    it is not suited for modifying the value after parsing is complete;
49
    reallocations waste memory, since the older buffer is not reclaimed until
50
    the resource is destroyed.
51
52
    @par Example
53
54
    This parses a JSON text into a value which uses a local stack buffer, then
55
    prints the result.
56
57
    @code
58
    unsigned char buf[ 4000 ];
59
    monotonic_resource mr( buf );
60
61
    // Parse the string, using our memory resource
62
    auto const jv = parse( "[1,2,3]", &mr );
63
64
    // Print the JSON
65
    std::cout << jv;
66
    @endcode
67
68
    @note The total amount of memory dynamically allocated is monotonically
69
    increasing; That is, it never decreases.
70
71
    @par Thread Safety
72
    Members of the same instance may not be
73
    called concurrently.
74
75
    @see
76
        https://en.wikipedia.org/wiki/Region-based_memory_management
77
*/
78
class
79
    BOOST_JSON_DECL
80
    BOOST_SYMBOL_VISIBLE
81
monotonic_resource final
82
    : public container::pmr::memory_resource
83
{
84
    struct block;
85
    struct block_base
86
    {
87
        void* p;
88
        std::size_t avail;
89
        std::size_t size;
90
        block_base* next;
91
    };
92
93
    block_base buffer_;
94
    block_base* head_ = &buffer_;
95
    std::size_t next_size_ = 1024;
96
    storage_ptr upstream_;
97
98
    static constexpr std::size_t min_size_ = 1024;
99
    inline static constexpr std::size_t max_size();
100
    inline static std::size_t round_pow2(
101
        std::size_t n) noexcept;
102
    inline static std::size_t next_pow2(
103
        std::size_t n) noexcept;
104
105
public:
106
    /** Assignment operator.
107
108
        Copy assignment operator is deleted. This type is not copyable or
109
        movable.
110
    */
111
    monotonic_resource& operator=(
112
        monotonic_resource const&) = delete;
113
114
    /** Destructor.
115
116
        Deallocates all the memory owned by this resource.
117
118
        @par Effects
119
        @code
120
        release();
121
        @endcode
122
123
        @par Complexity
124
        Linear in the number of deallocations performed.
125
126
        @par Exception Safety
127
        No-throw guarantee.
128
    */
129
    ~monotonic_resource();
130
131
    /** Constructors.
132
133
        Construct the resource.
134
135
        @li **(1)** indicates that the first internal dynamic allocation shall
136
            be at least `initial_size` bytes.
137
        @li **(2)**--**(5)** indicate that subsequent allocations should use
138
            the specified caller-owned buffer. When this buffer is exhausted,
139
            dynamic allocations from the upstream resource are made.
140
        @li **(6)** copy constructor is deleted. This type is not copyable or
141
            movable.
142
143
        None of the constructors performs any dynamic allocations.
144
145
        @par Complexity
146
        Constant.
147
148
        @par Exception Safety
149
        No-throw guarantee.
150
151
        @param initial_size The size of the first internal dynamic allocation.
152
               If this is lower than the implementation-defined lower limit,
153
               then the lower limit is used instead.
154
        @param upstream An optional upstream memory resource to use for
155
               performing internal dynamic allocations. If this parameter is
156
               omitted, the \<\<default_memory_resource,default resource\>\> is
157
               used.
158
159
        @{
160
    */
161
    explicit
162
    monotonic_resource(
163
        std::size_t initial_size = 1024,
164
        storage_ptr upstream = {}) noexcept;
165
166
    /** Overload
167
168
        @param buffer The buffer to use. Ownership is not transferred; the
169
               caller is responsible for ensuring that the lifetime of the
170
               buffer extends until the resource is destroyed.
171
        @param size The number of valid bytes pointed to by `buffer`.
172
        @param upstream
173
    */
174
    monotonic_resource(
175
        unsigned char* buffer,
176
        std::size_t size,
177
        storage_ptr upstream = {}) noexcept;
178
179
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
180
    /// Overload
181
    monotonic_resource(
182
        std::byte* buffer,
183
        std::size_t size,
184
        storage_ptr upstream) noexcept
185
        : monotonic_resource(reinterpret_cast<
186
            unsigned char*>(buffer), size,
187
                std::move(upstream))
188
0
    {
189
0
    }
190
#endif
191
192
    /// Overload
193
    template<std::size_t N>
194
    explicit
195
    monotonic_resource(
196
        unsigned char(&buffer)[N],
197
        storage_ptr upstream = {}) noexcept
198
        : monotonic_resource(&buffer[0],
199
            N, std::move(upstream))
200
    {
201
    }
202
203
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
204
    /// Overload
205
    template<std::size_t N>
206
    explicit
207
    monotonic_resource(
208
        std::byte(&buffer)[N],
209
        storage_ptr upstream = {}) noexcept
210
        : monotonic_resource(&buffer[0],
211
            N, std::move(upstream))
212
    {
213
    }
214
#endif
215
216
#ifndef BOOST_JSON_DOCS
217
    // Safety net for accidental buffer overflows
218
    template<std::size_t N>
219
    monotonic_resource(
220
        unsigned char(&buffer)[N],
221
        std::size_t n,
222
        storage_ptr upstream = {}) noexcept
223
        : monotonic_resource(&buffer[0],
224
            n, std::move(upstream))
225
    {
226
        // If this goes off, check your parameters
227
        // closely, chances are you passed an array
228
        // thinking it was a pointer.
229
        BOOST_ASSERT(n <= N);
230
    }
231
232
#ifdef __cpp_lib_byte
233
    // Safety net for accidental buffer overflows
234
    template<std::size_t N>
235
    monotonic_resource(
236
        std::byte(&buffer)[N],
237
        std::size_t n,
238
        storage_ptr upstream = {}) noexcept
239
        : monotonic_resource(&buffer[0],
240
            n, std::move(upstream))
241
    {
242
        // If this goes off, check your parameters
243
        // closely, chances are you passed an array
244
        // thinking it was a pointer.
245
        BOOST_ASSERT(n <= N);
246
    }
247
#endif
248
#endif
249
250
    /// Overload
251
    monotonic_resource(
252
        monotonic_resource const&) = delete;
253
    /// @}
254
255
    /** Release all allocated memory.
256
257
        This function deallocates all allocated memory.
258
        If an initial buffer was provided upon construction,
259
        then all of the bytes will be available again for
260
        allocation. Allocated memory is deallocated even
261
        if deallocate has not been called for some of
262
        the allocated blocks.
263
264
        @par Complexity
265
        Linear in the number of deallocations performed.
266
267
        @par Exception Safety
268
        No-throw guarantee.
269
    */
270
    void
271
    release() noexcept;
272
273
protected:
274
#ifndef BOOST_JSON_DOCS
275
    void*
276
    do_allocate(
277
        std::size_t n,
278
        std::size_t align) override;
279
280
    void
281
    do_deallocate(
282
        void* p,
283
        std::size_t n,
284
        std::size_t align) override;
285
286
    bool
287
    do_is_equal(
288
        memory_resource const& mr) const noexcept override;
289
#endif
290
};
291
292
#ifdef _MSC_VER
293
#pragma warning(pop)
294
#endif
295
296
template<>
297
struct is_deallocate_trivial<
298
    monotonic_resource>
299
{
300
    static constexpr bool value = true;
301
};
302
303
} // namespace json
304
} // namespace boost
305
306
#endif