1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
3  
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/boostorg/json
8  
// Official repository: https://github.com/boostorg/json
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_JSON_MONOTONIC_RESOURCE_HPP
11  
#ifndef BOOST_JSON_MONOTONIC_RESOURCE_HPP
12  
#define BOOST_JSON_MONOTONIC_RESOURCE_HPP
12  
#define BOOST_JSON_MONOTONIC_RESOURCE_HPP
13  

13  

14  
#include <boost/container/pmr/memory_resource.hpp>
14  
#include <boost/container/pmr/memory_resource.hpp>
15  
#include <boost/json/detail/config.hpp>
15  
#include <boost/json/detail/config.hpp>
16  
#include <boost/json/storage_ptr.hpp>
16  
#include <boost/json/storage_ptr.hpp>
17  
#include <cstddef>
17  
#include <cstddef>
18  
#include <utility>
18  
#include <utility>
19  

19  

20  
namespace boost {
20  
namespace boost {
21  
namespace json {
21  
namespace json {
22  

22  

23  
#ifdef _MSC_VER
23  
#ifdef _MSC_VER
24  
#pragma warning(push)
24  
#pragma warning(push)
25  
#pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
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
26  
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
27  
#endif
27  
#endif
28  

28  

29  
//----------------------------------------------------------
29  
//----------------------------------------------------------
30  

30  

31 -
/** A dynamically allocating resource with a trivial deallocate.
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  

32  

43 -
    The purpose of this resource is to optimize the use case for performing
33 +
    This memory resource is a special-purpose resource
44 -
    many allocations, followed by deallocating everything at once. This is
34 +
    that releases allocated memory only when the resource
45 -
    precisely the pattern of memory allocation which occurs when parsing:
35 +
    is destroyed (or when @ref release is called).
46 -
    allocation is performed for each parsed element, and when the the resulting
36 +
    It has a trivial deallocate function; that is, the
47 -
    @ref value is no longer needed, the entire structure is destroyed. However,
37 +
    metafunction @ref is_deallocate_trivial returns `true`.
48 -
    it is not suited for modifying the value after parsing is complete;
38 +
\n
49 -
    reallocations waste memory, since the older buffer is not reclaimed until
39 +
    The resource can be constructed with an initial buffer.
50 -
    the resource is destroyed.
40 +
    If there is no initial buffer, or if the buffer is
 
41 +
    exhausted, subsequent dynamic allocations are made from
 
42 +
    the system heap. The size of buffers obtained in this
 
43 +
    fashion follow a geometric progression.
 
44 +
\n
 
45 +
    The purpose of this resource is to optimize the use
 
46 +
    case for performing many allocations, followed by
 
47 +
    deallocating everything at once. This is precisely the
 
48 +
    pattern of memory allocation which occurs when parsing:
 
49 +
    allocation is performed for each parsed element, and
 
50 +
    when the the resulting @ref value is no longer needed,
 
51 +
    the entire structure is destroyed. However, it is not
 
52 +
    suited for modifying the value after parsing is
 
53 +
    complete; reallocations waste memory, since the
 
54 +
    older buffer is not reclaimed until the resource
 
55 +
    is destroyed.
51  

56  

52  
    @par Example
57  
    @par Example
53  

58  

54 -
    This parses a JSON text into a value which uses a local stack buffer, then
59 +
    This parses a JSON text into a value which uses a local
55 -
    prints the result.
60 +
    stack buffer, then prints the result.
56  

61  

57  
    @code
62  
    @code
 
63 +

58  
    unsigned char buf[ 4000 ];
64  
    unsigned char buf[ 4000 ];
59  
    monotonic_resource mr( buf );
65  
    monotonic_resource mr( buf );
60  

66  

61  
    // Parse the string, using our memory resource
67  
    // Parse the string, using our memory resource
62  
    auto const jv = parse( "[1,2,3]", &mr );
68  
    auto const jv = parse( "[1,2,3]", &mr );
63  

69  

64  
    // Print the JSON
70  
    // Print the JSON
65  
    std::cout << jv;
71  
    std::cout << jv;
 
72 +

66  
    @endcode
73  
    @endcode
67  

74  

68 -
    @note The total amount of memory dynamically allocated is monotonically
75 +
    @note The total amount of memory dynamically
69 -
    increasing; That is, it never decreases.
76 +
    allocated is monotonically increasing; That is,
 
77 +
    it never decreases.
70  

78  

71  
    @par Thread Safety
79  
    @par Thread Safety
72  
    Members of the same instance may not be
80  
    Members of the same instance may not be
73  
    called concurrently.
81  
    called concurrently.
74  

82  

75  
    @see
83  
    @see
76  
        https://en.wikipedia.org/wiki/Region-based_memory_management
84  
        https://en.wikipedia.org/wiki/Region-based_memory_management
77  
*/
85  
*/
78  
class
86  
class
79  
    BOOST_JSON_DECL
87  
    BOOST_JSON_DECL
80  
    BOOST_SYMBOL_VISIBLE
88  
    BOOST_SYMBOL_VISIBLE
81  
monotonic_resource final
89  
monotonic_resource final
82  
    : public container::pmr::memory_resource
90  
    : public container::pmr::memory_resource
83  
{
91  
{
84  
    struct block;
92  
    struct block;
85  
    struct block_base
93  
    struct block_base
86  
    {
94  
    {
87  
        void* p;
95  
        void* p;
88  
        std::size_t avail;
96  
        std::size_t avail;
89  
        std::size_t size;
97  
        std::size_t size;
90  
        block_base* next;
98  
        block_base* next;
91  
    };
99  
    };
92  

100  

93  
    block_base buffer_;
101  
    block_base buffer_;
94  
    block_base* head_ = &buffer_;
102  
    block_base* head_ = &buffer_;
95  
    std::size_t next_size_ = 1024;
103  
    std::size_t next_size_ = 1024;
96  
    storage_ptr upstream_;
104  
    storage_ptr upstream_;
97  

105  

98  
    static constexpr std::size_t min_size_ = 1024;
106  
    static constexpr std::size_t min_size_ = 1024;
99  
    inline static constexpr std::size_t max_size();
107  
    inline static constexpr std::size_t max_size();
100  
    inline static std::size_t round_pow2(
108  
    inline static std::size_t round_pow2(
101  
        std::size_t n) noexcept;
109  
        std::size_t n) noexcept;
102  
    inline static std::size_t next_pow2(
110  
    inline static std::size_t next_pow2(
103  
        std::size_t n) noexcept;
111  
        std::size_t n) noexcept;
104  

112  

105  
public:
113  
public:
106 -
    /** Assignment operator.
114 +
    /// Copy constructor (deleted)
 
115 +
    monotonic_resource(
 
116 +
        monotonic_resource const&) = delete;
107  

117  

108 -
        Copy assignment operator is deleted. This type is not copyable or
118 +
    /// Copy assignment (deleted)
109 -
        movable.
 
110 -
    */
 
111  
    monotonic_resource& operator=(
119  
    monotonic_resource& operator=(
112  
        monotonic_resource const&) = delete;
120  
        monotonic_resource const&) = delete;
113  

121  

114 -
    /** Destructor.
122 +
    /** Destructor
115  

123  

116  
        Deallocates all the memory owned by this resource.
124  
        Deallocates all the memory owned by this resource.
117  

125  

118  
        @par Effects
126  
        @par Effects
119  
        @code
127  
        @code
120 -
        release();
128 +
        this->release();
121  
        @endcode
129  
        @endcode
122  

130  

123  
        @par Complexity
131  
        @par Complexity
124  
        Linear in the number of deallocations performed.
132  
        Linear in the number of deallocations performed.
125  

133  

126  
        @par Exception Safety
134  
        @par Exception Safety
127  
        No-throw guarantee.
135  
        No-throw guarantee.
128  
    */
136  
    */
129  
    ~monotonic_resource();
137  
    ~monotonic_resource();
130  

138  

131 -
    /** Constructors.
139 +
    /** Constructor
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  

140  

143 -
        None of the constructors performs any dynamic allocations.
141 +
        This constructs the resource and indicates
 
142 +
        that the first internal dynamic allocation
 
143 +
        shall be at least `initial_size` bytes.
 
144 +
    \n
 
145 +
        This constructor is guaranteed not to perform
 
146 +
        any dynamic allocations.
144  

147  

145  
        @par Complexity
148  
        @par Complexity
146  
        Constant.
149  
        Constant.
147  

150  

148  
        @par Exception Safety
151  
        @par Exception Safety
149  
        No-throw guarantee.
152  
        No-throw guarantee.
150  

153  

151 -
        @param initial_size The size of the first internal dynamic allocation.
154 +
        @param initial_size The size of the first
152 -
               If this is lower than the implementation-defined lower limit,
155 +
        internal dynamic allocation. If this is lower
153 -
               then the lower limit is used instead.
156 +
        than the implementation-defined lower limit, then
154 -
        @param upstream An optional upstream memory resource to use for
157 +
        the lower limit is used instead.
155 -
               performing internal dynamic allocations. If this parameter is
 
156 -
               omitted, the \<\<default_memory_resource,default resource\>\> is
 
157 -
               used.
 
158  

158  

159 -
        @{
159 +
        @param upstream An optional upstream memory resource
 
160 +
        to use for performing internal dynamic allocations.
 
161 +
        If this parameter is omitted, the default resource
 
162 +
        is used.
160  
    */
163  
    */
161  
    explicit
164  
    explicit
162  
    monotonic_resource(
165  
    monotonic_resource(
163  
        std::size_t initial_size = 1024,
166  
        std::size_t initial_size = 1024,
164  
        storage_ptr upstream = {}) noexcept;
167  
        storage_ptr upstream = {}) noexcept;
165  

168  

166 -
    /** Overload
169 +
    /** Constructor
167  

170  

168 -
        @param buffer The buffer to use. Ownership is not transferred; the
171 +
        This constructs the resource and indicates that
169 -
               caller is responsible for ensuring that the lifetime of the
172 +
        subsequent allocations should use the specified
170 -
               buffer extends until the resource is destroyed.
173 +
        caller-owned buffer.
171 -
        @param size The number of valid bytes pointed to by `buffer`.
174 +
        When this buffer is exhausted, dynamic allocations
172 -
        @param upstream
175 +
        from the upstream resource are made.
 
176 +
    \n
 
177 +
        This constructor is guaranteed not to perform
 
178 +
        any dynamic allocations.
 
179 +

 
180 +
        @par Complexity
 
181 +
        Constant.
 
182 +

 
183 +
        @par Exception Safety
 
184 +
        No-throw guarantee.
 
185 +

 
186 +
        @param buffer The buffer to use.
 
187 +
        Ownership is not transferred; the caller is
 
188 +
        responsible for ensuring that the lifetime of
 
189 +
        the buffer extends until the resource is destroyed.
 
190 +

 
191 +
        @param size The number of valid bytes pointed
 
192 +
        to by `buffer`.
 
193 +

 
194 +
        @param upstream An optional upstream memory resource
 
195 +
        to use for performing internal dynamic allocations.
 
196 +
        If this parameter is omitted, the default resource
 
197 +
        is used.
173  
    */
198  
    */
 
199 +
    /** @{ */
174  
    monotonic_resource(
200  
    monotonic_resource(
175  
        unsigned char* buffer,
201  
        unsigned char* buffer,
176  
        std::size_t size,
202  
        std::size_t size,
177  
        storage_ptr upstream = {}) noexcept;
203  
        storage_ptr upstream = {}) noexcept;
178  

204  

179 -
    /// Overload
 
180  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
205  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
181  
    monotonic_resource(
206  
    monotonic_resource(
182  
        std::byte* buffer,
207  
        std::byte* buffer,
183  
        std::size_t size,
208  
        std::size_t size,
184  
        storage_ptr upstream) noexcept
209  
        storage_ptr upstream) noexcept
185  
        : monotonic_resource(reinterpret_cast<
210  
        : monotonic_resource(reinterpret_cast<
186  
            unsigned char*>(buffer), size,
211  
            unsigned char*>(buffer), size,
187  
                std::move(upstream))
212  
                std::move(upstream))
188  
    {
213  
    {
189  
    }
214  
    }
190  
#endif
215  
#endif
 
216 +
    /** @} */
191  

217  

192 -
    /// Overload
218 +
    /** Constructor
 
219 +

 
220 +
        This constructs the resource and indicates that
 
221 +
        subsequent allocations should use the specified
 
222 +
        caller-owned buffer.
 
223 +
        When this buffer is exhausted, dynamic allocations
 
224 +
        from the upstream resource are made.
 
225 +
    \n
 
226 +
        This constructor is guaranteed not to perform
 
227 +
        any dynamic allocations.
 
228 +

 
229 +
        @par Complexity
 
230 +
        Constant.
 
231 +

 
232 +
        @par Exception Safety
 
233 +
        No-throw guarantee.
 
234 +

 
235 +
        @param buffer The buffer to use.
 
236 +
        Ownership is not transferred; the caller is
 
237 +
        responsible for ensuring that the lifetime of
 
238 +
        the buffer extends until the resource is destroyed.
 
239 +

 
240 +
        @param upstream An optional upstream memory resource
 
241 +
        to use for performing internal dynamic allocations.
 
242 +
        If this parameter is omitted, the default resource
 
243 +
        is used.
 
244 +
    */
 
245 +
    /** @{ */
193  
    template<std::size_t N>
246  
    template<std::size_t N>
194  
    explicit
247  
    explicit
195  
    monotonic_resource(
248  
    monotonic_resource(
196  
        unsigned char(&buffer)[N],
249  
        unsigned char(&buffer)[N],
197  
        storage_ptr upstream = {}) noexcept
250  
        storage_ptr upstream = {}) noexcept
198  
        : monotonic_resource(&buffer[0],
251  
        : monotonic_resource(&buffer[0],
199  
            N, std::move(upstream))
252  
            N, std::move(upstream))
200  
    {
253  
    {
201  
    }
254  
    }
202  

255  

203 -
    /// Overload
 
204  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
256  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
205  
    template<std::size_t N>
257  
    template<std::size_t N>
206  
    explicit
258  
    explicit
207  
    monotonic_resource(
259  
    monotonic_resource(
208  
        std::byte(&buffer)[N],
260  
        std::byte(&buffer)[N],
209  
        storage_ptr upstream = {}) noexcept
261  
        storage_ptr upstream = {}) noexcept
210  
        : monotonic_resource(&buffer[0],
262  
        : monotonic_resource(&buffer[0],
211  
            N, std::move(upstream))
263  
            N, std::move(upstream))
212  
    {
264  
    {
213  
    }
265  
    }
214  
#endif
266  
#endif
 
267 +
    /** @} */
215  

268  

216  
#ifndef BOOST_JSON_DOCS
269  
#ifndef BOOST_JSON_DOCS
217  
    // Safety net for accidental buffer overflows
270  
    // Safety net for accidental buffer overflows
218  
    template<std::size_t N>
271  
    template<std::size_t N>
219  
    monotonic_resource(
272  
    monotonic_resource(
220  
        unsigned char(&buffer)[N],
273  
        unsigned char(&buffer)[N],
221  
        std::size_t n,
274  
        std::size_t n,
222  
        storage_ptr upstream = {}) noexcept
275  
        storage_ptr upstream = {}) noexcept
223  
        : monotonic_resource(&buffer[0],
276  
        : monotonic_resource(&buffer[0],
224  
            n, std::move(upstream))
277  
            n, std::move(upstream))
225  
    {
278  
    {
226  
        // If this goes off, check your parameters
279  
        // If this goes off, check your parameters
227  
        // closely, chances are you passed an array
280  
        // closely, chances are you passed an array
228  
        // thinking it was a pointer.
281  
        // thinking it was a pointer.
229  
        BOOST_ASSERT(n <= N);
282  
        BOOST_ASSERT(n <= N);
230  
    }
283  
    }
231  

284  

232  
#ifdef __cpp_lib_byte
285  
#ifdef __cpp_lib_byte
233  
    // Safety net for accidental buffer overflows
286  
    // Safety net for accidental buffer overflows
234  
    template<std::size_t N>
287  
    template<std::size_t N>
235  
    monotonic_resource(
288  
    monotonic_resource(
236  
        std::byte(&buffer)[N],
289  
        std::byte(&buffer)[N],
237  
        std::size_t n,
290  
        std::size_t n,
238  
        storage_ptr upstream = {}) noexcept
291  
        storage_ptr upstream = {}) noexcept
239  
        : monotonic_resource(&buffer[0],
292  
        : monotonic_resource(&buffer[0],
240  
            n, std::move(upstream))
293  
            n, std::move(upstream))
241  
    {
294  
    {
242  
        // If this goes off, check your parameters
295  
        // If this goes off, check your parameters
243  
        // closely, chances are you passed an array
296  
        // closely, chances are you passed an array
244  
        // thinking it was a pointer.
297  
        // thinking it was a pointer.
245  
        BOOST_ASSERT(n <= N);
298  
        BOOST_ASSERT(n <= N);
246  
    }
299  
    }
247  
#endif
300  
#endif
248 -

 
249 -
    /// Overload
 
250 -
    monotonic_resource(
 
251 -
        monotonic_resource const&) = delete;
 
252 -
    /// @}
 
253  
#endif
301  
#endif
254  

302  

255  
    /** Release all allocated memory.
303  
    /** Release all allocated memory.
256  

304  

257  
        This function deallocates all allocated memory.
305  
        This function deallocates all allocated memory.
258  
        If an initial buffer was provided upon construction,
306  
        If an initial buffer was provided upon construction,
259  
        then all of the bytes will be available again for
307  
        then all of the bytes will be available again for
260  
        allocation. Allocated memory is deallocated even
308  
        allocation. Allocated memory is deallocated even
261  
        if deallocate has not been called for some of
309  
        if deallocate has not been called for some of
262  
        the allocated blocks.
310  
        the allocated blocks.
263  

311  

264  
        @par Complexity
312  
        @par Complexity
265  
        Linear in the number of deallocations performed.
313  
        Linear in the number of deallocations performed.
266  

314  

267  
        @par Exception Safety
315  
        @par Exception Safety
268  
        No-throw guarantee.
316  
        No-throw guarantee.
269  
    */
317  
    */
270  
    void
318  
    void
271  
    release() noexcept;
319  
    release() noexcept;
272  

320  

273  
protected:
321  
protected:
274  
#ifndef BOOST_JSON_DOCS
322  
#ifndef BOOST_JSON_DOCS
275  
    void*
323  
    void*
276  
    do_allocate(
324  
    do_allocate(
277  
        std::size_t n,
325  
        std::size_t n,
278  
        std::size_t align) override;
326  
        std::size_t align) override;
279  

327  

280  
    void
328  
    void
281  
    do_deallocate(
329  
    do_deallocate(
282  
        void* p,
330  
        void* p,
283  
        std::size_t n,
331  
        std::size_t n,
284  
        std::size_t align) override;
332  
        std::size_t align) override;
285  

333  

286  
    bool
334  
    bool
287  
    do_is_equal(
335  
    do_is_equal(
288  
        memory_resource const& mr) const noexcept override;
336  
        memory_resource const& mr) const noexcept override;
289  
#endif
337  
#endif
290  
};
338  
};
291  

339  

292  
#ifdef _MSC_VER
340  
#ifdef _MSC_VER
293  
#pragma warning(pop)
341  
#pragma warning(pop)
294  
#endif
342  
#endif
295  

343  

296  
template<>
344  
template<>
297  
struct is_deallocate_trivial<
345  
struct is_deallocate_trivial<
298  
    monotonic_resource>
346  
    monotonic_resource>
299  
{
347  
{
300  
    static constexpr bool value = true;
348  
    static constexpr bool value = true;
301  
};
349  
};
302  

350  

303  
} // namespace json
351  
} // namespace json
304  
} // namespace boost
352  
} // namespace boost
305  

353  

306  
#endif
354  
#endif