Line data Source code
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
34 : that releases allocated memory only when the resource
35 : is destroyed (or when @ref release is called).
36 : It has a trivial deallocate function; that is, the
37 : metafunction @ref is_deallocate_trivial returns `true`.
38 : \n
39 : The resource can be constructed with an initial buffer.
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.
56 :
57 : @par Example
58 :
59 : This parses a JSON text into a value which uses a local
60 : stack buffer, then prints the result.
61 :
62 : @code
63 :
64 : unsigned char buf[ 4000 ];
65 : monotonic_resource mr( buf );
66 :
67 : // Parse the string, using our memory resource
68 : auto const jv = parse( "[1,2,3]", &mr );
69 :
70 : // Print the JSON
71 : std::cout << jv;
72 :
73 : @endcode
74 :
75 : @note The total amount of memory dynamically
76 : allocated is monotonically increasing; That is,
77 : it never decreases.
78 :
79 : @par Thread Safety
80 : Members of the same instance may not be
81 : called concurrently.
82 :
83 : @see
84 : https://en.wikipedia.org/wiki/Region-based_memory_management
85 : */
86 : class
87 : BOOST_JSON_DECL
88 : BOOST_SYMBOL_VISIBLE
89 : monotonic_resource final
90 : : public container::pmr::memory_resource
91 : {
92 : struct block;
93 : struct block_base
94 : {
95 : void* p;
96 : std::size_t avail;
97 : std::size_t size;
98 : block_base* next;
99 : };
100 :
101 : block_base buffer_;
102 : block_base* head_ = &buffer_;
103 : std::size_t next_size_ = 1024;
104 : storage_ptr upstream_;
105 :
106 : static constexpr std::size_t min_size_ = 1024;
107 : inline static constexpr std::size_t max_size();
108 : inline static std::size_t round_pow2(
109 : std::size_t n) noexcept;
110 : inline static std::size_t next_pow2(
111 : std::size_t n) noexcept;
112 :
113 : public:
114 : /// Copy constructor (deleted)
115 : monotonic_resource(
116 : monotonic_resource const&) = delete;
117 :
118 : /// Copy assignment (deleted)
119 : monotonic_resource& operator=(
120 : monotonic_resource const&) = delete;
121 :
122 : /** Destructor
123 :
124 : Deallocates all the memory owned by this resource.
125 :
126 : @par Effects
127 : @code
128 : this->release();
129 : @endcode
130 :
131 : @par Complexity
132 : Linear in the number of deallocations performed.
133 :
134 : @par Exception Safety
135 : No-throw guarantee.
136 : */
137 : ~monotonic_resource();
138 :
139 : /** Constructor
140 :
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.
147 :
148 : @par Complexity
149 : Constant.
150 :
151 : @par Exception Safety
152 : No-throw guarantee.
153 :
154 : @param initial_size The size of the first
155 : internal dynamic allocation. If this is lower
156 : than the implementation-defined lower limit, then
157 : the lower limit is used instead.
158 :
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.
163 : */
164 : explicit
165 : monotonic_resource(
166 : std::size_t initial_size = 1024,
167 : storage_ptr upstream = {}) noexcept;
168 :
169 : /** Constructor
170 :
171 : This constructs the resource and indicates that
172 : subsequent allocations should use the specified
173 : caller-owned buffer.
174 : When this buffer is exhausted, dynamic allocations
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.
198 : */
199 : /** @{ */
200 : monotonic_resource(
201 : unsigned char* buffer,
202 : std::size_t size,
203 : storage_ptr upstream = {}) noexcept;
204 :
205 : #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
206 : monotonic_resource(
207 : std::byte* buffer,
208 : std::size_t size,
209 : storage_ptr upstream) noexcept
210 : : monotonic_resource(reinterpret_cast<
211 : unsigned char*>(buffer), size,
212 : std::move(upstream))
213 : {
214 : }
215 : #endif
216 : /** @} */
217 :
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 : /** @{ */
246 : template<std::size_t N>
247 : explicit
248 2 : monotonic_resource(
249 : unsigned char(&buffer)[N],
250 : storage_ptr upstream = {}) noexcept
251 : : monotonic_resource(&buffer[0],
252 2 : N, std::move(upstream))
253 : {
254 2 : }
255 :
256 : #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
257 : template<std::size_t N>
258 : explicit
259 : monotonic_resource(
260 : std::byte(&buffer)[N],
261 : storage_ptr upstream = {}) noexcept
262 : : monotonic_resource(&buffer[0],
263 : N, std::move(upstream))
264 : {
265 : }
266 : #endif
267 : /** @} */
268 :
269 : #ifndef BOOST_JSON_DOCS
270 : // Safety net for accidental buffer overflows
271 : template<std::size_t N>
272 : monotonic_resource(
273 : unsigned char(&buffer)[N],
274 : std::size_t n,
275 : storage_ptr upstream = {}) noexcept
276 : : monotonic_resource(&buffer[0],
277 : n, std::move(upstream))
278 : {
279 : // If this goes off, check your parameters
280 : // closely, chances are you passed an array
281 : // thinking it was a pointer.
282 : BOOST_ASSERT(n <= N);
283 : }
284 :
285 : #ifdef __cpp_lib_byte
286 : // Safety net for accidental buffer overflows
287 : template<std::size_t N>
288 : monotonic_resource(
289 : std::byte(&buffer)[N],
290 : std::size_t n,
291 : storage_ptr upstream = {}) noexcept
292 : : monotonic_resource(&buffer[0],
293 : n, std::move(upstream))
294 : {
295 : // If this goes off, check your parameters
296 : // closely, chances are you passed an array
297 : // thinking it was a pointer.
298 : BOOST_ASSERT(n <= N);
299 : }
300 : #endif
301 : #endif
302 :
303 : /** Release all allocated memory.
304 :
305 : This function deallocates all allocated memory.
306 : If an initial buffer was provided upon construction,
307 : then all of the bytes will be available again for
308 : allocation. Allocated memory is deallocated even
309 : if deallocate has not been called for some of
310 : the allocated blocks.
311 :
312 : @par Complexity
313 : Linear in the number of deallocations performed.
314 :
315 : @par Exception Safety
316 : No-throw guarantee.
317 : */
318 : void
319 : release() noexcept;
320 :
321 : protected:
322 : #ifndef BOOST_JSON_DOCS
323 : void*
324 : do_allocate(
325 : std::size_t n,
326 : std::size_t align) override;
327 :
328 : void
329 : do_deallocate(
330 : void* p,
331 : std::size_t n,
332 : std::size_t align) override;
333 :
334 : bool
335 : do_is_equal(
336 : memory_resource const& mr) const noexcept override;
337 : #endif
338 : };
339 :
340 : #ifdef _MSC_VER
341 : #pragma warning(pop)
342 : #endif
343 :
344 : template<>
345 : struct is_deallocate_trivial<
346 : monotonic_resource>
347 : {
348 : static constexpr bool value = true;
349 : };
350 :
351 : } // namespace json
352 : } // namespace boost
353 :
354 : #endif
|