]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // |
2 | // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | // Official repository: https://github.com/boostorg/json | |
8 | // | |
9 | ||
10 | #ifndef BOOST_JSON_VALUE_STACK_HPP | |
11 | #define BOOST_JSON_VALUE_STACK_HPP | |
12 | ||
13 | #include <boost/json/detail/config.hpp> | |
14 | #include <boost/json/error.hpp> | |
15 | #include <boost/json/storage_ptr.hpp> | |
16 | #include <boost/json/value.hpp> | |
17 | #include <stddef.h> | |
18 | ||
19 | BOOST_JSON_NS_BEGIN | |
20 | ||
21 | //---------------------------------------------------------- | |
22 | ||
23 | /** A stack of @ref value elements, for building a document. | |
24 | ||
25 | This stack of @ref value allows iterative | |
26 | construction of a JSON document in memory. | |
27 | The implementation uses temporary internal | |
28 | storage to buffer elements so that arrays, objects, | |
29 | and strings in the document are constructed using a | |
30 | single memory allocation. This improves performance | |
31 | and makes efficient use of the @ref memory_resource | |
32 | used to create the resulting @ref value. | |
33 | ||
34 | Temporary storage used by the implementation | |
35 | initially comes from an optional memory buffer | |
36 | owned by the caller. If that storage is exhausted, | |
37 | then memory is obtained dynamically from the | |
38 | @ref memory_resource provided on construction. | |
39 | ||
40 | @par Usage | |
41 | ||
42 | Construct the stack with an optional initial | |
43 | temporary buffer, and a @ref storage_ptr to use for | |
44 | more storage when the initial buffer is exhausted. | |
45 | Then to build a @ref value, first call @ref reset | |
46 | and optionally specify the @ref memory_resource | |
47 | which will be used for the value. Then push elements | |
48 | onto the stack by calling the corresponding functions. | |
49 | After the document has been fully created, call | |
50 | @ref release to acquire ownership of the top-level | |
51 | @ref value. | |
52 | ||
53 | @par Performance | |
54 | ||
55 | The initial buffer and any dynamically allocated | |
56 | temporary buffers are retained until the stack | |
57 | is destroyed. This improves performance when using | |
58 | a single stack instance to produce multiple | |
59 | values. | |
60 | ||
61 | @par Example | |
62 | ||
63 | The following code constructs a @ref value which | |
64 | when serialized produces a JSON object with three | |
65 | elements. It uses a local buffer for the temporary | |
66 | storage, and a separate local buffer for the storage | |
67 | of the resulting value. No memory is dynamically | |
68 | allocated; this shows how to construct a value | |
69 | without using the heap. | |
70 | ||
71 | @code | |
72 | ||
73 | // This example builds a json::value without any dynamic memory allocations: | |
74 | ||
75 | // Construct the value stack using a local buffer | |
76 | unsigned char temp[4096]; | |
77 | value_stack st( storage_ptr(), temp, sizeof(temp) ); | |
78 | ||
79 | // Create a static resource with a local initial buffer | |
80 | unsigned char buf[4096]; | |
81 | static_resource mr( buf, sizeof(buf) ); | |
82 | ||
83 | // All values on the stack will use `mr` | |
84 | st.reset(&mr); | |
85 | ||
86 | // Push the key/value pair "a":1. | |
87 | st.push_key("a"); | |
88 | st.push_int64(1); | |
89 | ||
90 | // Push "b":null | |
91 | st.push_key("b"); | |
92 | st.push_null(); | |
93 | ||
94 | // Push "c":"hello" | |
95 | st.push_key("c"); | |
96 | st.push_string("hello"); | |
97 | ||
98 | // Pop the three key/value pairs and push an object with those three values. | |
99 | st.push_object(3); | |
100 | ||
101 | // Pop the object from the stack and take ownership. | |
102 | value jv = st.release(); | |
103 | ||
104 | assert( serialize(jv) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); | |
105 | ||
106 | // At this point we could re-use the stack by calling reset | |
107 | ||
108 | @endcode | |
109 | ||
110 | @par Thread Safety | |
111 | ||
112 | Distinct instances may be accessed concurrently. | |
113 | Non-const member functions of a shared instance | |
114 | may not be called concurrently with any other | |
115 | member functions of that instance. | |
116 | */ | |
117 | class value_stack | |
118 | { | |
119 | class stack | |
120 | { | |
121 | enum | |
122 | { | |
123 | min_size_ = 16 | |
124 | }; | |
125 | ||
126 | storage_ptr sp_; | |
127 | void* temp_; | |
128 | value* begin_; | |
129 | value* top_; | |
130 | value* end_; | |
131 | // string starts at top_+1 | |
132 | std::size_t chars_ = 0; | |
133 | bool run_dtors_ = true; | |
134 | ||
135 | public: | |
136 | inline ~stack(); | |
137 | inline stack( | |
138 | storage_ptr sp, | |
139 | void* temp, std::size_t size) noexcept; | |
140 | inline void run_dtors(bool b) noexcept; | |
141 | inline std::size_t size() const noexcept; | |
142 | inline bool has_chars(); | |
143 | ||
144 | inline void clear() noexcept; | |
145 | inline void maybe_grow(); | |
146 | inline void grow_one(); | |
147 | inline void grow(std::size_t nchars); | |
148 | ||
149 | inline void append(string_view s); | |
150 | inline string_view release_string() noexcept; | |
151 | inline value* release(std::size_t n) noexcept; | |
152 | template<class... Args> value& push(Args&&... args); | |
153 | template<class Unchecked> void exchange(Unchecked&& u); | |
154 | }; | |
155 | ||
156 | stack st_; | |
157 | storage_ptr sp_; | |
158 | ||
159 | public: | |
160 | /// Copy constructor (deleted) | |
161 | value_stack( | |
162 | value_stack const&) = delete; | |
163 | ||
164 | /// Copy assignment (deleted) | |
165 | value_stack& operator=( | |
166 | value_stack const&) = delete; | |
167 | ||
168 | /** Destructor. | |
169 | ||
170 | All dynamically allocated memory and | |
171 | partial or complete elements is freed. | |
172 | ||
173 | @par Complexity | |
174 | Linear in the size of partial results. | |
175 | ||
176 | @par Exception Safety | |
177 | No-throw guarantee. | |
178 | */ | |
179 | BOOST_JSON_DECL | |
180 | ~value_stack(); | |
181 | ||
182 | /** Constructor. | |
183 | ||
184 | Constructs an empty stack. Before any | |
185 | @ref value can be built, the function | |
186 | @ref reset must be called. | |
187 | ||
188 | The `sp` parameter is only used to allocate | |
189 | intermediate storage; it will not be used | |
190 | for the @ref value returned by @ref release. | |
191 | ||
192 | @param sp A pointer to the @ref memory_resource | |
193 | to use for intermediate storage allocations. If | |
194 | this argument is omitted, the default memory | |
195 | resource is used. | |
196 | ||
197 | @param temp_buffer A pointer to a caller-owned | |
198 | buffer which will be used to store temporary | |
199 | data used while building the value. If this | |
200 | pointer is null, the builder will use the | |
201 | storage pointer to allocate temporary data. | |
202 | ||
203 | @param temp_size The number of valid bytes of | |
204 | storage pointed to by `temp_buffer`. | |
205 | */ | |
206 | BOOST_JSON_DECL | |
207 | value_stack( | |
208 | storage_ptr sp = {}, | |
209 | unsigned char* temp_buffer = nullptr, | |
210 | std::size_t temp_size = 0) noexcept; | |
211 | ||
212 | /** Prepare to build a new document. | |
213 | ||
214 | This function must be called before constructing | |
215 | a new top-level @ref value. Any previously existing | |
216 | partial or complete elements are destroyed, but | |
217 | internal dynamically allocated memory is preserved | |
218 | which may be reused to build new values. | |
219 | ||
220 | @par Exception Safety | |
221 | ||
222 | No-throw guarantee. | |
223 | ||
224 | @param sp A pointer to the @ref memory_resource | |
225 | to use for top-level @ref value and all child | |
226 | values. The stack will acquire shared ownership | |
227 | of the memory resource until @ref release or | |
228 | @ref reset is called, or when the stack is | |
229 | destroyed. | |
230 | */ | |
231 | BOOST_JSON_DECL | |
232 | void | |
233 | reset(storage_ptr sp = {}) noexcept; | |
234 | ||
235 | /** Return the top-level @ref value. | |
236 | ||
237 | This function transfers ownership of the | |
238 | constructed top-level value to the caller. | |
239 | The behavior is undefined if there is not | |
240 | a single, top-level element. | |
241 | ||
242 | @par Exception Safety | |
243 | ||
244 | No-throw guarantee. | |
245 | ||
246 | @return A __value__ holding the result. | |
247 | Ownership of this value is transferred | |
248 | to the caller. Ownership of the memory | |
249 | resource used in the last call to @ref reset | |
250 | is released. | |
251 | */ | |
252 | BOOST_JSON_DECL | |
253 | value | |
254 | release() noexcept; | |
255 | ||
256 | //-------------------------------------------- | |
257 | ||
258 | /** Push an array formed by popping `n` values from the stack. | |
259 | ||
260 | This function pushes an @ref array value | |
261 | onto the stack. The array is formed by first | |
262 | popping the top `n` values from the stack. | |
263 | If the stack contains fewer than `n` values, | |
264 | or if any of the top `n` values on the stack | |
265 | is a key, the behavior is undefined. | |
266 | ||
267 | @par Example | |
268 | ||
269 | The following statements produce an array | |
270 | with the contents 1, 2, 3: | |
271 | ||
272 | @code | |
273 | ||
274 | value_stack st; | |
275 | ||
276 | // reset must be called first or else the behavior is undefined | |
277 | st.reset(); | |
278 | ||
279 | // Place three values on the stack | |
280 | st.push_int64( 1 ); | |
281 | st.push_int64( 2 ); | |
282 | st.push_int64( 3 ); | |
283 | ||
284 | // Remove the 3 values, and push an array with those 3 elements on the stack | |
285 | st.push_array( 3 ); | |
286 | ||
287 | // Pop the object from the stack and take ownership. | |
288 | value jv = st.release(); | |
289 | ||
290 | assert( serialize(jv) == "[1,2,3]" ); | |
291 | ||
292 | // At this point, reset must be called again to use the stack | |
293 | ||
294 | @endcode | |
295 | ||
296 | @param n The number of values to pop from the | |
297 | top of the stack to form the array. | |
298 | */ | |
299 | BOOST_JSON_DECL | |
300 | void | |
301 | push_array(std::size_t n); | |
302 | ||
303 | /** Push an object formed by popping `n` key/value pairs from the stack. | |
304 | ||
305 | This function pushes an @ref object value | |
306 | onto the stack. The object is formed by first | |
307 | popping the top `n` key/value pairs from the | |
308 | stack. If the stack contains fewer than `n` | |
309 | key/value pairs, or if any of the top `n` key/value | |
310 | pairs on the stack does not consist of exactly one | |
311 | key followed by one value, the behavior is undefined. | |
312 | ||
313 | @note | |
314 | ||
315 | A key/value pair is formed by pushing a key, and then | |
316 | pushing a value. | |
317 | ||
318 | @par Example | |
319 | ||
320 | The following code creates an object on the stack | |
321 | with a single element, where key is "x" and value | |
322 | is true: | |
323 | ||
324 | @code | |
325 | ||
326 | value_stack st; | |
327 | ||
328 | // reset must be called first or else the behavior is undefined | |
329 | st.reset(); | |
330 | ||
331 | // Place a key/value pair onto the stack | |
332 | st.push_key( "x" ); | |
333 | st.push_bool( true ); | |
334 | ||
335 | // Replace the key/value pair with an object containing a single element | |
336 | st.push_object( 1 ); | |
337 | ||
338 | // Pop the object from the stack and take ownership. | |
339 | value jv = st.release(); | |
340 | ||
341 | assert( serialize(jv) == "{\"x\",true}" ); | |
342 | ||
343 | // At this point, reset must be called again to use the stack | |
344 | ||
345 | @endcode | |
346 | ||
347 | @par Duplicate Keys | |
348 | ||
349 | If there are object elements with duplicate keys; | |
350 | that is, if multiple elements in an object have | |
351 | keys that compare equal, only the last equivalent | |
352 | element will be inserted. | |
353 | ||
354 | @param n The number of key/value pairs to pop from the | |
355 | top of the stack to form the array. | |
356 | */ | |
357 | BOOST_JSON_DECL | |
358 | void | |
359 | push_object(std::size_t n); | |
360 | ||
361 | /** Push part of a key or string onto the stack. | |
362 | ||
363 | This function pushes the characters in `s` onto | |
364 | the stack, appending to any existing characters | |
365 | or creating new characters as needed. Once a | |
366 | string part is placed onto the stack, the only | |
367 | valid stack operations are: | |
368 | ||
369 | @li @ref push_chars to append additional | |
370 | characters to the key or string being built, | |
371 | ||
372 | @li @ref push_key or @ref push_string to | |
373 | finish building the key or string and place | |
374 | the value onto the stack. | |
375 | ||
376 | @par Exception Safety | |
377 | ||
378 | Basic guarantee. | |
379 | Calls to `memory_resource::allocate` may throw. | |
380 | ||
381 | @param s The characters to append. This may be empty. | |
382 | */ | |
383 | BOOST_JSON_DECL | |
384 | void | |
385 | push_chars( | |
386 | string_view s); | |
387 | ||
388 | /** Push a key onto the stack. | |
389 | ||
390 | This function notionally removes all the | |
391 | characters currently on the stack, then | |
392 | pushes a @ref value containing a key onto | |
393 | the stack formed by appending `s` to the | |
394 | removed characters. | |
395 | ||
396 | @par Exception Safety | |
397 | ||
398 | Basic guarantee. | |
399 | Calls to `memory_resource::allocate` may throw. | |
400 | ||
401 | @param s The characters to append. This may be empty. | |
402 | */ | |
403 | BOOST_JSON_DECL | |
404 | void | |
405 | push_key( | |
406 | string_view s); | |
407 | ||
408 | /** Place a string value onto the stack. | |
409 | ||
410 | This function notionally removes all the | |
411 | characters currently on the stack, then | |
412 | pushes a @ref value containing a @ref string | |
413 | onto the stack formed by appending `s` to the | |
414 | removed characters. | |
415 | ||
416 | @par Exception Safety | |
417 | ||
418 | Basic guarantee. | |
419 | Calls to `memory_resource::allocate` may throw. | |
420 | ||
421 | @param s The characters to append. This may be empty. | |
422 | */ | |
423 | BOOST_JSON_DECL | |
424 | void | |
425 | push_string( | |
426 | string_view s); | |
427 | ||
428 | /** Push a number onto the stack | |
429 | ||
430 | This function pushes a number value onto the stack. | |
431 | ||
432 | @par Exception Safety | |
433 | ||
434 | Basic guarantee. | |
435 | Calls to `memory_resource::allocate` may throw. | |
436 | ||
437 | @param i The number to insert. | |
438 | */ | |
439 | BOOST_JSON_DECL | |
440 | void | |
441 | push_int64( | |
442 | int64_t i); | |
443 | ||
444 | /** Push a number onto the stack | |
445 | ||
446 | This function pushes a number value onto the stack. | |
447 | ||
448 | @par Exception Safety | |
449 | ||
450 | Basic guarantee. | |
451 | Calls to `memory_resource::allocate` may throw. | |
452 | ||
453 | @param u The number to insert. | |
454 | */ | |
455 | BOOST_JSON_DECL | |
456 | void | |
457 | push_uint64( | |
458 | uint64_t u); | |
459 | ||
460 | /** Push a number onto the stack | |
461 | ||
462 | This function pushes a number value onto the stack. | |
463 | ||
464 | @par Exception Safety | |
465 | ||
466 | Basic guarantee. | |
467 | Calls to `memory_resource::allocate` may throw. | |
468 | ||
469 | @param d The number to insert. | |
470 | */ | |
471 | BOOST_JSON_DECL | |
472 | void | |
473 | push_double( | |
474 | double d); | |
475 | ||
476 | /** Push a `bool` onto the stack | |
477 | ||
478 | This function pushes a boolean value onto the stack. | |
479 | ||
480 | @par Exception Safety | |
481 | ||
482 | Basic guarantee. | |
483 | Calls to `memory_resource::allocate` may throw. | |
484 | ||
485 | @param b The boolean to insert. | |
486 | */ | |
487 | BOOST_JSON_DECL | |
488 | void | |
489 | push_bool( | |
490 | bool b); | |
491 | ||
492 | /** Push a null onto the stack | |
493 | ||
494 | This function pushes a boolean value onto the stack. | |
495 | ||
496 | @par Exception Safety | |
497 | ||
498 | Basic guarantee. | |
499 | Calls to `memory_resource::allocate` may throw. | |
500 | */ | |
501 | BOOST_JSON_DECL | |
502 | void | |
503 | push_null(); | |
504 | }; | |
505 | ||
506 | BOOST_JSON_NS_END | |
507 | ||
508 | #endif |