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  

32  

33  
    This memory resource is a special-purpose resource that releases allocated
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
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
35  
    called). It has a trivial deallocate function; that is, the metafunction
36  
    @ref is_deallocate_trivial returns `true`.
36  
    @ref is_deallocate_trivial returns `true`.
37  

37  

38  
    The resource can be constructed with an initial buffer. If there is no
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
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
40  
    allocations are made from the system heap. The size of buffers obtained in
41  
    this fashion follow a geometric progression.
41  
    this fashion follow a geometric progression.
42  

42  

43  
    The purpose of this resource is to optimize the use case for performing
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
44  
    many allocations, followed by deallocating everything at once. This is
45  
    precisely the pattern of memory allocation which occurs when parsing:
45  
    precisely the pattern of memory allocation which occurs when parsing:
46  
    allocation is performed for each parsed element, and when the the resulting
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,
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;
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
49  
    reallocations waste memory, since the older buffer is not reclaimed until
50  
    the resource is destroyed.
50  
    the resource is destroyed.
51  

51  

52  
    @par Example
52  
    @par Example
53  

53  

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

56  

57  
    @code
57  
    @code
58  
    unsigned char buf[ 4000 ];
58  
    unsigned char buf[ 4000 ];
59  
    monotonic_resource mr( buf );
59  
    monotonic_resource mr( buf );
60  

60  

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

63  

64  
    // Print the JSON
64  
    // Print the JSON
65  
    std::cout << jv;
65  
    std::cout << jv;
66  
    @endcode
66  
    @endcode
67  

67  

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

70  

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

74  

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

92  

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

97  

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

104  

105  
public:
105  
public:
106  
    /** Assignment operator.
106  
    /** Assignment operator.
107  

107  

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

113  

114  
    /** Destructor.
114  
    /** Destructor.
115  

115  

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

117  

118  
        @par Effects
118  
        @par Effects
119  
        @code
119  
        @code
120  
        release();
120  
        release();
121  
        @endcode
121  
        @endcode
122  

122  

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

125  

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

130  

131  
    /** Constructors.
131  
    /** Constructors.
132  

132  

133  
        Construct the resource.
133  
        Construct the resource.
134  

134  

135  
        @li **(1)** indicates that the first internal dynamic allocation shall
135  
        @li **(1)** indicates that the first internal dynamic allocation shall
136  
            be at least `initial_size` bytes.
136  
            be at least `initial_size` bytes.
137  
        @li **(2)**--**(5)** indicate that subsequent allocations should use
137  
        @li **(2)**--**(5)** indicate that subsequent allocations should use
138  
            the specified caller-owned buffer. When this buffer is exhausted,
138  
            the specified caller-owned buffer. When this buffer is exhausted,
139  
            dynamic allocations from the upstream resource are made.
139  
            dynamic allocations from the upstream resource are made.
140  
        @li **(6)** copy constructor is deleted. This type is not copyable or
140  
        @li **(6)** copy constructor is deleted. This type is not copyable or
141  
            movable.
141  
            movable.
142  

142  

143  
        None of the constructors performs any dynamic allocations.
143  
        None of the constructors performs any dynamic allocations.
144  

144  

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

147  

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

150  

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

158  

159  
        @{
159  
        @{
160  
    */
160  
    */
161  
    explicit
161  
    explicit
162  
    monotonic_resource(
162  
    monotonic_resource(
163  
        std::size_t initial_size = 1024,
163  
        std::size_t initial_size = 1024,
164  
        storage_ptr upstream = {}) noexcept;
164  
        storage_ptr upstream = {}) noexcept;
165  

165  

166  
    /** Overload
166  
    /** Overload
167  

167  

168  
        @param buffer The buffer to use. Ownership is not transferred; the
168  
        @param buffer The buffer to use. Ownership is not transferred; the
169  
               caller is responsible for ensuring that the lifetime of the
169  
               caller is responsible for ensuring that the lifetime of the
170  
               buffer extends until the resource is destroyed.
170  
               buffer extends until the resource is destroyed.
171  
        @param size The number of valid bytes pointed to by `buffer`.
171  
        @param size The number of valid bytes pointed to by `buffer`.
172  
        @param upstream
172  
        @param upstream
173  
    */
173  
    */
174  
    monotonic_resource(
174  
    monotonic_resource(
175  
        unsigned char* buffer,
175  
        unsigned char* buffer,
176  
        std::size_t size,
176  
        std::size_t size,
177  
        storage_ptr upstream = {}) noexcept;
177  
        storage_ptr upstream = {}) noexcept;
178  

178  

179  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
179  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
180  
    /// Overload
180  
    /// Overload
181  
    monotonic_resource(
181  
    monotonic_resource(
182  
        std::byte* buffer,
182  
        std::byte* buffer,
183  
        std::size_t size,
183  
        std::size_t size,
184  
        storage_ptr upstream) noexcept
184  
        storage_ptr upstream) noexcept
185  
        : monotonic_resource(reinterpret_cast<
185  
        : monotonic_resource(reinterpret_cast<
186  
            unsigned char*>(buffer), size,
186  
            unsigned char*>(buffer), size,
187  
                std::move(upstream))
187  
                std::move(upstream))
188  
    {
188  
    {
189  
    }
189  
    }
190  
#endif
190  
#endif
191  

191  

192  
    /// Overload
192  
    /// Overload
193  
    template<std::size_t N>
193  
    template<std::size_t N>
194  
    explicit
194  
    explicit
195  
    monotonic_resource(
195  
    monotonic_resource(
196  
        unsigned char(&buffer)[N],
196  
        unsigned char(&buffer)[N],
197  
        storage_ptr upstream = {}) noexcept
197  
        storage_ptr upstream = {}) noexcept
198  
        : monotonic_resource(&buffer[0],
198  
        : monotonic_resource(&buffer[0],
199  
            N, std::move(upstream))
199  
            N, std::move(upstream))
200  
    {
200  
    {
201  
    }
201  
    }
202  

202  

203  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
203  
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
204  
    /// Overload
204  
    /// Overload
205  
    template<std::size_t N>
205  
    template<std::size_t N>
206  
    explicit
206  
    explicit
207  
    monotonic_resource(
207  
    monotonic_resource(
208  
        std::byte(&buffer)[N],
208  
        std::byte(&buffer)[N],
209  
        storage_ptr upstream = {}) noexcept
209  
        storage_ptr upstream = {}) noexcept
210  
        : monotonic_resource(&buffer[0],
210  
        : monotonic_resource(&buffer[0],
211  
            N, std::move(upstream))
211  
            N, std::move(upstream))
212  
    {
212  
    {
213  
    }
213  
    }
214  
#endif
214  
#endif
215  

215  

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

231  

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

249  

250  
    /// Overload
250  
    /// Overload
251  
    monotonic_resource(
251  
    monotonic_resource(
252  
        monotonic_resource const&) = delete;
252  
        monotonic_resource const&) = delete;
253  
    /// @}
253  
    /// @}
254  

254  

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

256  

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

263  

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

266  

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

272  

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

279  

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

285  

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

291  

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

295  

296  
template<>
296  
template<>
297  
struct is_deallocate_trivial<
297  
struct is_deallocate_trivial<
298  
    monotonic_resource>
298  
    monotonic_resource>
299  
{
299  
{
300  
    static constexpr bool value = true;
300  
    static constexpr bool value = true;
301  
};
301  
};
302  

302  

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

305  

306  
#endif
306  
#endif