value_stack.hpp 13.7 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//

#ifndef BOOST_JSON_VALUE_STACK_HPP
#define BOOST_JSON_VALUE_STACK_HPP

#include <boost/json/detail/config.hpp>
#include <boost/json/error.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/value.hpp>
#include <stddef.h>

BOOST_JSON_NS_BEGIN

//----------------------------------------------------------

/** A stack of @ref value elements, for building a document.

    This stack of @ref value allows iterative
    construction of a JSON document in memory.
    The implementation uses temporary internal
    storage to buffer elements so that arrays, objects,
    and strings in the document are constructed using a
    single memory allocation. This improves performance
    and makes efficient use of the @ref memory_resource
    used to create the resulting @ref value.

    Temporary storage used by the implementation
    initially comes from an optional memory buffer
    owned by the caller. If that storage is exhausted,
    then memory is obtained dynamically from the
    @ref memory_resource provided on construction.

    @par Usage

    Construct the stack with an optional initial
    temporary buffer, and a @ref storage_ptr to use for
    more storage when the initial buffer is exhausted.
    Then to build a @ref value, first call @ref reset
    and optionally specify the @ref memory_resource
    which will be used for the value. Then push elements
    onto the stack by calling the corresponding functions.
    After the document has been fully created, call
    @ref release to acquire ownership of the top-level
    @ref value.

    @par Performance

    The initial buffer and any dynamically allocated
    temporary buffers are retained until the stack
    is destroyed. This improves performance when using
    a single stack instance to produce multiple
    values.

    @par Example

    The following code constructs a @ref value which
    when serialized produces a JSON object with three
    elements. It uses a local buffer for the temporary
    storage, and a separate local buffer for the storage
    of the resulting value. No memory is dynamically
    allocated; this shows how to construct a value
    without using the heap.

    @code

    // This example builds a json::value without any dynamic memory allocations:

    // Construct the value stack using a local buffer
    unsigned char temp[4096];
    value_stack st( storage_ptr(), temp, sizeof(temp) );

    // Create a static resource with a local initial buffer
    unsigned char buf[4096];
    static_resource mr( buf, sizeof(buf) );

    // All values on the stack will use `mr`
    st.reset(&mr);

    // Push the key/value pair "a":1.
    st.push_key("a");
    st.push_int64(1);

    // Push "b":null
    st.push_key("b");
    st.push_null();

    // Push "c":"hello"
    st.push_key("c");
    st.push_string("hello");

    // Pop the three key/value pairs and push an object with those three values.
    st.push_object(3);

    // Pop the object from the stack and take ownership.
    value jv = st.release();

    assert( serialize(jv) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" );

    // At this point we could re-use the stack by calling reset

    @endcode

    @par Thread Safety

    Distinct instances may be accessed concurrently.
    Non-const member functions of a shared instance
    may not be called concurrently with any other
    member functions of that instance.
*/
class value_stack
{
    class stack
    {
        enum
        {
            min_size_ = 16
        };

        storage_ptr sp_;
        void* temp_;
        value* begin_;
        value* top_;
        value* end_;
        // string starts at top_+1
        std::size_t chars_ = 0;
        bool run_dtors_ = true;

    public:
        inline ~stack();
        inline stack(
            storage_ptr sp,
            void* temp, std::size_t size) noexcept;
        inline void run_dtors(bool b) noexcept;
        inline std::size_t size() const noexcept;
        inline bool has_chars();

        inline void clear() noexcept;
        inline void maybe_grow();
        inline void grow_one();
        inline void grow(std::size_t nchars);

        inline void append(string_view s);
        inline string_view release_string() noexcept;
        inline value* release(std::size_t n) noexcept;
        template<class... Args> value& push(Args&&... args);
        template<class Unchecked> void exchange(Unchecked&& u);
    };

    stack st_;
    storage_ptr sp_;

public:
    /// Copy constructor (deleted)
    value_stack(
        value_stack const&) = delete;

    /// Copy assignment (deleted)
    value_stack& operator=(
        value_stack const&) = delete;

    /** Destructor.

        All dynamically allocated memory and
        partial or complete elements is freed.

        @par Complexity
        Linear in the size of partial results.

        @par Exception Safety
        No-throw guarantee.
    */
    BOOST_JSON_DECL
    ~value_stack();

    /** Constructor.

        Constructs an empty stack. Before any
        @ref value can be built, the function
        @ref reset must be called.

        The `sp` parameter is only used to allocate
        intermediate storage; it will not be used
        for the @ref value returned by @ref release.

        @param sp A pointer to the @ref memory_resource
        to use for intermediate storage allocations. If
        this argument is omitted, the default memory
        resource is used.

        @param temp_buffer A pointer to a caller-owned
        buffer which will be used to store temporary
        data used while building the value. If this
        pointer is null, the builder will use the
        storage pointer to allocate temporary data.

        @param temp_size The number of valid bytes of
        storage pointed to by `temp_buffer`.
    */
    BOOST_JSON_DECL
    value_stack(
        storage_ptr sp = {},
        unsigned char* temp_buffer = nullptr,
        std::size_t temp_size = 0) noexcept;

    /** Prepare to build a new document.

        This function must be called before constructing
        a new top-level @ref value. Any previously existing
        partial or complete elements are destroyed, but
        internal dynamically allocated memory is preserved
        which may be reused to build new values.

        @par Exception Safety

        No-throw guarantee.

        @param sp A pointer to the @ref memory_resource
        to use for top-level @ref value and all child
        values. The stack will acquire shared ownership
        of the memory resource until @ref release or
        @ref reset is called, or when the stack is
        destroyed.
    */
    BOOST_JSON_DECL
    void
    reset(storage_ptr sp = {}) noexcept;

    /** Return the top-level @ref value.

        This function transfers ownership of the
        constructed top-level value to the caller.
        The behavior is undefined if there is not
        a single, top-level element.

        @par Exception Safety

        No-throw guarantee.

        @return A __value__ holding the result.
        Ownership of this value is transferred
        to the caller. Ownership of the memory
        resource used in the last call to @ref reset
        is released.
    */
    BOOST_JSON_DECL
    value
    release() noexcept;

    //--------------------------------------------

    /** Push an array formed by popping `n` values from the stack.

        This function pushes an @ref array value
        onto the stack. The array is formed by first
        popping the top `n` values from the stack.
        If the stack contains fewer than `n` values,
        or if any of the top `n` values on the stack
        is a key, the behavior is undefined.

        @par Example

        The following statements produce an array
        with the contents 1, 2, 3:

        @code

        value_stack st;

        // reset must be called first or else the behavior is undefined
        st.reset();

        // Place three values on the stack
        st.push_int64( 1 );
        st.push_int64( 2 );
        st.push_int64( 3 );

        // Remove the 3 values, and push an array with those 3 elements on the stack
        st.push_array( 3 );

        // Pop the object from the stack and take ownership.
        value jv = st.release();

        assert( serialize(jv) == "[1,2,3]" );

        // At this point, reset must be called again to use the stack

        @endcode

        @param n The number of values to pop from the
        top of the stack to form the array.
    */
    BOOST_JSON_DECL
    void
    push_array(std::size_t n);

    /** Push an object formed by popping `n` key/value pairs from the stack.

        This function pushes an @ref object value
        onto the stack. The object is formed by first
        popping the top `n` key/value pairs from the
        stack. If the stack contains fewer than `n`
        key/value pairs, or if any of the top `n` key/value
        pairs on the stack does not consist of exactly one
        key followed by one value, the behavior is undefined.

        @note

        A key/value pair is formed by pushing a key, and then
        pushing a value.

        @par Example

        The following code creates an object on the stack
        with a single element, where key is "x" and value
        is true:

        @code

        value_stack st;

        // reset must be called first or else the behavior is undefined
        st.reset();

        // Place a key/value pair onto the stack
        st.push_key( "x" );
        st.push_bool( true );

        // Replace the key/value pair with an object containing a single element
        st.push_object( 1 );

        // Pop the object from the stack and take ownership.
        value jv = st.release();

        assert( serialize(jv) == "{\"x\",true}" );

        // At this point, reset must be called again to use the stack

        @endcode

        @par Duplicate Keys

        If there are object elements with duplicate keys;
        that is, if multiple elements in an object have
        keys that compare equal, only the last equivalent
        element will be inserted.

        @param n The number of key/value pairs to pop from the
        top of the stack to form the array.
    */
    BOOST_JSON_DECL
    void
    push_object(std::size_t n);

    /** Push part of a key or string onto the stack.

        This function pushes the characters in `s` onto
        the stack, appending to any existing characters
        or creating new characters as needed. Once a
        string part is placed onto the stack, the only
        valid stack operations are:

        @li @ref push_chars to append additional
        characters to the key or string being built,

        @li @ref push_key or @ref push_string to
        finish building the key or string and place
        the value onto the stack.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param s The characters to append. This may be empty.
    */
    BOOST_JSON_DECL
    void
    push_chars(
        string_view s);

    /** Push a key onto the stack.

        This function notionally removes all the
        characters currently on the stack, then
        pushes a @ref value containing a key onto
        the stack formed by appending `s` to the
        removed characters.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param s The characters to append. This may be empty.
    */
    BOOST_JSON_DECL
    void
    push_key(
        string_view s);

    /** Place a string value onto the stack.

        This function notionally removes all the
        characters currently on the stack, then
        pushes a @ref value containing a @ref string
        onto the stack formed by appending `s` to the
        removed characters.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param s The characters to append. This may be empty.
    */
    BOOST_JSON_DECL
    void
    push_string(
        string_view s);

    /** Push a number onto the stack

        This function pushes a number value onto the stack.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param i The number to insert.
    */
    BOOST_JSON_DECL
    void
    push_int64(
        int64_t i);

    /** Push a number onto the stack

        This function pushes a number value onto the stack.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param u The number to insert.
    */
    BOOST_JSON_DECL
    void
    push_uint64(
        uint64_t u);

    /** Push a number onto the stack

        This function pushes a number value onto the stack.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param d The number to insert.
    */
    BOOST_JSON_DECL
    void
    push_double(
        double d);

    /** Push a `bool` onto the stack

        This function pushes a boolean value onto the stack.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.

        @param b The boolean to insert.
    */
    BOOST_JSON_DECL
    void
    push_bool(
        bool b);

    /** Push a null onto the stack

        This function pushes a boolean value onto the stack.

        @par Exception Safety

        Basic guarantee.
        Calls to `memory_resource::allocate` may throw.
    */
    BOOST_JSON_DECL
    void
    push_null();
};

BOOST_JSON_NS_END

#endif