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

9  

10  
#ifndef BOOST_JSON_IMPL_VALUE_STACK_IPP
10  
#ifndef BOOST_JSON_IMPL_VALUE_STACK_IPP
11  
#define BOOST_JSON_IMPL_VALUE_STACK_IPP
11  
#define BOOST_JSON_IMPL_VALUE_STACK_IPP
12  

12  

13  
#include <boost/json/value_stack.hpp>
13  
#include <boost/json/value_stack.hpp>
14  
#include <cstring>
14  
#include <cstring>
15  
#include <stdexcept>
15  
#include <stdexcept>
16  
#include <utility>
16  
#include <utility>
17  

17  

18  
namespace boost {
18  
namespace boost {
19  
namespace json {
19  
namespace json {
20  

20  

21  
//--------------------------------------
21  
//--------------------------------------
22  

22  

23  
value_stack::
23  
value_stack::
24  
stack::
24  
stack::
25  
~stack()
25  
~stack()
26  
{
26  
{
27  
    clear();
27  
    clear();
28  
    if( begin_ != temp_ &&
28  
    if( begin_ != temp_ &&
29  
        begin_ != nullptr)
29  
        begin_ != nullptr)
30  
        sp_->deallocate(
30  
        sp_->deallocate(
31  
            begin_,
31  
            begin_,
32  
            (end_ - begin_) *
32  
            (end_ - begin_) *
33  
                sizeof(value));
33  
                sizeof(value));
34  
}
34  
}
35  

35  

36  
value_stack::
36  
value_stack::
37  
stack::
37  
stack::
38  
stack(
38  
stack(
39  
    storage_ptr sp,
39  
    storage_ptr sp,
40  
    void* temp,
40  
    void* temp,
41  
    std::size_t size) noexcept
41  
    std::size_t size) noexcept
42  
    : sp_(std::move(sp))
42  
    : sp_(std::move(sp))
43  
    , temp_(temp)
43  
    , temp_(temp)
44  
{
44  
{
45  
    if(size >= min_size_ *
45  
    if(size >= min_size_ *
46  
        sizeof(value))
46  
        sizeof(value))
47  
    {
47  
    {
48  
        begin_ = reinterpret_cast<
48  
        begin_ = reinterpret_cast<
49  
            value*>(temp);
49  
            value*>(temp);
50  
        top_ = begin_;
50  
        top_ = begin_;
51  
        end_ = begin_ +
51  
        end_ = begin_ +
52  
            size / sizeof(value);
52  
            size / sizeof(value);
53  
    }
53  
    }
54  
    else
54  
    else
55  
    {
55  
    {
56  
        begin_ = nullptr;
56  
        begin_ = nullptr;
57  
        top_ = nullptr;
57  
        top_ = nullptr;
58  
        end_ = nullptr;
58  
        end_ = nullptr;
59  
    }
59  
    }
60  
}
60  
}
61  

61  

62  
void
62  
void
63  
value_stack::
63  
value_stack::
64  
stack::
64  
stack::
65  
run_dtors(bool b) noexcept
65  
run_dtors(bool b) noexcept
66  
{
66  
{
67  
    run_dtors_ = b;
67  
    run_dtors_ = b;
68  
}
68  
}
69  

69  

70  
std::size_t
70  
std::size_t
71  
value_stack::
71  
value_stack::
72  
stack::
72  
stack::
73  
size() const noexcept
73  
size() const noexcept
74  
{
74  
{
75  
    return top_ - begin_;
75  
    return top_ - begin_;
76  
}
76  
}
77  

77  

78  
bool
78  
bool
79  
value_stack::
79  
value_stack::
80  
stack::
80  
stack::
81  
has_chars()
81  
has_chars()
82  
{
82  
{
83  
    return chars_ != 0;
83  
    return chars_ != 0;
84  
}
84  
}
85  

85  

86  
//--------------------------------------
86  
//--------------------------------------
87  

87  

88  
// destroy the values but
88  
// destroy the values but
89  
// not the stack allocation.
89  
// not the stack allocation.
90  
void
90  
void
91  
value_stack::
91  
value_stack::
92  
stack::
92  
stack::
93  
clear() noexcept
93  
clear() noexcept
94  
{
94  
{
95  
    if(top_ != begin_)
95  
    if(top_ != begin_)
96  
    {
96  
    {
97  
        if(run_dtors_)
97  
        if(run_dtors_)
98  
            for(auto it = top_;
98  
            for(auto it = top_;
99  
                it-- != begin_;)
99  
                it-- != begin_;)
100  
                it->~value();
100  
                it->~value();
101  
        top_ = begin_;
101  
        top_ = begin_;
102  
    }
102  
    }
103  
    chars_ = 0;
103  
    chars_ = 0;
104  
}
104  
}
105  

105  

106  
void
106  
void
107  
value_stack::
107  
value_stack::
108  
stack::
108  
stack::
109  
maybe_grow()
109  
maybe_grow()
110  
{
110  
{
111  
    if(top_ >= end_)
111  
    if(top_ >= end_)
112  
        grow_one();
112  
        grow_one();
113  
}
113  
}
114  

114  

115  
// make room for at least one more value
115  
// make room for at least one more value
116  
void
116  
void
117  
value_stack::
117  
value_stack::
118  
stack::
118  
stack::
119  
grow_one()
119  
grow_one()
120  
{
120  
{
121  
    BOOST_ASSERT(chars_ == 0);
121  
    BOOST_ASSERT(chars_ == 0);
122  
    std::size_t const capacity =
122  
    std::size_t const capacity =
123  
        end_ - begin_;
123  
        end_ - begin_;
124  
    std::size_t new_cap = min_size_;
124  
    std::size_t new_cap = min_size_;
125  
    // VFALCO check overflow here
125  
    // VFALCO check overflow here
126  
    while(new_cap < capacity + 1)
126  
    while(new_cap < capacity + 1)
127  
        new_cap <<= 1;
127  
        new_cap <<= 1;
128  
    auto const begin =
128  
    auto const begin =
129  
        reinterpret_cast<value*>(
129  
        reinterpret_cast<value*>(
130  
            sp_->allocate(
130  
            sp_->allocate(
131  
                new_cap * sizeof(value)));
131  
                new_cap * sizeof(value)));
132  
    std::size_t const cur_size = top_ - begin_;
132  
    std::size_t const cur_size = top_ - begin_;
133  
    if(begin_)
133  
    if(begin_)
134  
    {
134  
    {
135  
        std::memcpy(
135  
        std::memcpy(
136  
            reinterpret_cast<char*>(begin),
136  
            reinterpret_cast<char*>(begin),
137  
            reinterpret_cast<char*>(begin_),
137  
            reinterpret_cast<char*>(begin_),
138  
            size() * sizeof(value));
138  
            size() * sizeof(value));
139  
        if(begin_ != temp_)
139  
        if(begin_ != temp_)
140  
            sp_->deallocate(begin_,
140  
            sp_->deallocate(begin_,
141  
                capacity * sizeof(value));
141  
                capacity * sizeof(value));
142  
    }
142  
    }
143  
    // book-keeping
143  
    // book-keeping
144  
    top_ = begin + cur_size;
144  
    top_ = begin + cur_size;
145  
    end_ = begin + new_cap;
145  
    end_ = begin + new_cap;
146  
    begin_ = begin;
146  
    begin_ = begin;
147  
}
147  
}
148  

148  

149  
// make room for nchars additional characters.
149  
// make room for nchars additional characters.
150  
void
150  
void
151  
value_stack::
151  
value_stack::
152  
stack::
152  
stack::
153  
grow(std::size_t nchars)
153  
grow(std::size_t nchars)
154  
{
154  
{
155  
    // needed capacity in values
155  
    // needed capacity in values
156  
    std::size_t const needed =
156  
    std::size_t const needed =
157  
        size() +
157  
        size() +
158  
        1 +
158  
        1 +
159  
        ((chars_ + nchars +
159  
        ((chars_ + nchars +
160  
            sizeof(value) - 1) /
160  
            sizeof(value) - 1) /
161  
                sizeof(value));
161  
                sizeof(value));
162  
    std::size_t const capacity =
162  
    std::size_t const capacity =
163  
        end_ - begin_;
163  
        end_ - begin_;
164  
    BOOST_ASSERT(
164  
    BOOST_ASSERT(
165  
        needed > capacity);
165  
        needed > capacity);
166  
    std::size_t new_cap = min_size_;
166  
    std::size_t new_cap = min_size_;
167  
    // VFALCO check overflow here
167  
    // VFALCO check overflow here
168  
    while(new_cap < needed)
168  
    while(new_cap < needed)
169  
        new_cap <<= 1;
169  
        new_cap <<= 1;
170  
    auto const begin =
170  
    auto const begin =
171  
        reinterpret_cast<value*>(
171  
        reinterpret_cast<value*>(
172  
            sp_->allocate(
172  
            sp_->allocate(
173  
                new_cap * sizeof(value)));
173  
                new_cap * sizeof(value)));
174  
    std::size_t const cur_size = top_ - begin_;
174  
    std::size_t const cur_size = top_ - begin_;
175  
    if(begin_)
175  
    if(begin_)
176  
    {
176  
    {
177  
        std::size_t amount =
177  
        std::size_t amount =
178  
            size() * sizeof(value);
178  
            size() * sizeof(value);
179  
        if(chars_ > 0)
179  
        if(chars_ > 0)
180  
            amount += sizeof(value) + chars_;
180  
            amount += sizeof(value) + chars_;
181  
        std::memcpy(
181  
        std::memcpy(
182  
            reinterpret_cast<char*>(begin),
182  
            reinterpret_cast<char*>(begin),
183  
            reinterpret_cast<char*>(begin_),
183  
            reinterpret_cast<char*>(begin_),
184  
            amount);
184  
            amount);
185  
        if(begin_ != temp_)
185  
        if(begin_ != temp_)
186  
            sp_->deallocate(begin_,
186  
            sp_->deallocate(begin_,
187  
                capacity * sizeof(value));
187  
                capacity * sizeof(value));
188  
    }
188  
    }
189  
    // book-keeping
189  
    // book-keeping
190  
    top_ = begin + cur_size;
190  
    top_ = begin + cur_size;
191  
    end_ = begin + new_cap;
191  
    end_ = begin + new_cap;
192  
    begin_ = begin;
192  
    begin_ = begin;
193  
}
193  
}
194  

194  

195  
//--------------------------------------
195  
//--------------------------------------
196  

196  

197  
void
197  
void
198  
value_stack::
198  
value_stack::
199  
stack::
199  
stack::
200  
append(string_view s)
200  
append(string_view s)
201  
{
201  
{
202  
    std::size_t const bytes_avail =
202  
    std::size_t const bytes_avail =
203  
        reinterpret_cast<
203  
        reinterpret_cast<
204  
            char const*>(end_) -
204  
            char const*>(end_) -
205  
        reinterpret_cast<
205  
        reinterpret_cast<
206  
            char const*>(top_);
206  
            char const*>(top_);
207  
    // make sure there is room for
207  
    // make sure there is room for
208  
    // pushing one more value without
208  
    // pushing one more value without
209  
    // clobbering the string.
209  
    // clobbering the string.
210  
    if(sizeof(value) + chars_ +
210  
    if(sizeof(value) + chars_ +
211  
            s.size() > bytes_avail)
211  
            s.size() > bytes_avail)
212  
        grow(s.size());
212  
        grow(s.size());
213  

213  

214  
    // copy the new piece
214  
    // copy the new piece
215  
    std::memcpy(
215  
    std::memcpy(
216  
        reinterpret_cast<char*>(
216  
        reinterpret_cast<char*>(
217  
            top_ + 1) + chars_,
217  
            top_ + 1) + chars_,
218  
        s.data(), s.size());
218  
        s.data(), s.size());
219  
    chars_ += s.size();
219  
    chars_ += s.size();
220  

220  

221  
    // ensure a pushed value cannot
221  
    // ensure a pushed value cannot
222  
    // clobber the released string.
222  
    // clobber the released string.
223  
    BOOST_ASSERT(
223  
    BOOST_ASSERT(
224  
        reinterpret_cast<char*>(
224  
        reinterpret_cast<char*>(
225  
            top_ + 1) + chars_ <=
225  
            top_ + 1) + chars_ <=
226  
        reinterpret_cast<char*>(
226  
        reinterpret_cast<char*>(
227  
            end_));
227  
            end_));
228  
}
228  
}
229  

229  

230  
string_view
230  
string_view
231  
value_stack::
231  
value_stack::
232  
stack::
232  
stack::
233  
release_string() noexcept
233  
release_string() noexcept
234  
{
234  
{
235  
    // ensure a pushed value cannot
235  
    // ensure a pushed value cannot
236  
    // clobber the released string.
236  
    // clobber the released string.
237  
    BOOST_ASSERT(
237  
    BOOST_ASSERT(
238  
        reinterpret_cast<char*>(
238  
        reinterpret_cast<char*>(
239  
            top_ + 1) + chars_ <=
239  
            top_ + 1) + chars_ <=
240  
        reinterpret_cast<char*>(
240  
        reinterpret_cast<char*>(
241  
            end_));
241  
            end_));
242  
    auto const n = chars_;
242  
    auto const n = chars_;
243  
    chars_ = 0;
243  
    chars_ = 0;
244  
    return { reinterpret_cast<
244  
    return { reinterpret_cast<
245  
        char const*>(top_ + 1), n };
245  
        char const*>(top_ + 1), n };
246  
}
246  
}
247  

247  

248  
// transfer ownership of the top n
248  
// transfer ownership of the top n
249  
// elements of the stack to the caller
249  
// elements of the stack to the caller
250  
value*
250  
value*
251  
value_stack::
251  
value_stack::
252  
stack::
252  
stack::
253  
release(std::size_t n) noexcept
253  
release(std::size_t n) noexcept
254  
{
254  
{
255  
    BOOST_ASSERT(n <= size());
255  
    BOOST_ASSERT(n <= size());
256  
    BOOST_ASSERT(chars_ == 0);
256  
    BOOST_ASSERT(chars_ == 0);
257  
    top_ -= n;
257  
    top_ -= n;
258  
    return top_;
258  
    return top_;
259  
}
259  
}
260  

260  

261  
template<class... Args>
261  
template<class... Args>
262  
value&
262  
value&
263  
value_stack::
263  
value_stack::
264  
stack::
264  
stack::
265  
push(Args&&... args)
265  
push(Args&&... args)
266  
{
266  
{
267  
    BOOST_ASSERT(chars_ == 0);
267  
    BOOST_ASSERT(chars_ == 0);
268  
    if(top_ >= end_)
268  
    if(top_ >= end_)
269  
        grow_one();
269  
        grow_one();
270  
    value& jv = detail::access::
270  
    value& jv = detail::access::
271  
        construct_value(top_,
271  
        construct_value(top_,
272  
            std::forward<Args>(args)...);
272  
            std::forward<Args>(args)...);
273  
    ++top_;
273  
    ++top_;
274  
    return jv;
274  
    return jv;
275  
}
275  
}
276  

276  

277  
template<class Unchecked>
277  
template<class Unchecked>
278  
void
278  
void
279  
value_stack::
279  
value_stack::
280  
stack::
280  
stack::
281  
exchange(Unchecked&& u)
281  
exchange(Unchecked&& u)
282  
{
282  
{
283  
    BOOST_ASSERT(chars_ == 0);
283  
    BOOST_ASSERT(chars_ == 0);
284  
    union U
284  
    union U
285  
    {
285  
    {
286  
        value v;
286  
        value v;
287  
        U() {}
287  
        U() {}
288  
        ~U() {}
288  
        ~U() {}
289  
    } jv;
289  
    } jv;
290  
    // construct value on the stack
290  
    // construct value on the stack
291  
    // to avoid clobbering top_[0],
291  
    // to avoid clobbering top_[0],
292  
    // which belongs to `u`.
292  
    // which belongs to `u`.
293  
    detail::access::
293  
    detail::access::
294  
        construct_value(
294  
        construct_value(
295  
            &jv.v, std::move(u));
295  
            &jv.v, std::move(u));
296  
    std::memcpy(
296  
    std::memcpy(
297  
        reinterpret_cast<
297  
        reinterpret_cast<
298  
            char*>(top_),
298  
            char*>(top_),
299  
        &jv.v, sizeof(value));
299  
        &jv.v, sizeof(value));
300  
    ++top_;
300  
    ++top_;
301  
}
301  
}
302  

302  

303  
//----------------------------------------------------------
303  
//----------------------------------------------------------
304  

304  

305  
value_stack::
305  
value_stack::
306  
~value_stack()
306  
~value_stack()
307  
{
307  
{
308  
    // default dtor is here so the
308  
    // default dtor is here so the
309  
    // definition goes in the library
309  
    // definition goes in the library
310  
    // instead of the caller's TU.
310  
    // instead of the caller's TU.
311  
}
311  
}
312  

312  

313  
value_stack::
313  
value_stack::
314  
value_stack(
314  
value_stack(
315  
    storage_ptr sp,
315  
    storage_ptr sp,
316  
    unsigned char* temp_buffer,
316  
    unsigned char* temp_buffer,
317  
    std::size_t temp_size) noexcept
317  
    std::size_t temp_size) noexcept
318  
    : st_(
318  
    : st_(
319  
        std::move(sp),
319  
        std::move(sp),
320  
        temp_buffer,
320  
        temp_buffer,
321  
        temp_size)
321  
        temp_size)
322  
{
322  
{
323  
}
323  
}
324  

324  

325  
void
325  
void
326  
value_stack::
326  
value_stack::
327  
reset(storage_ptr sp) noexcept
327  
reset(storage_ptr sp) noexcept
328  
{
328  
{
329  
    st_.clear();
329  
    st_.clear();
330  

330  

331  
    sp_.~storage_ptr();
331  
    sp_.~storage_ptr();
332  
    ::new(&sp_) storage_ptr(
332  
    ::new(&sp_) storage_ptr(
333  
        pilfer(sp));
333  
        pilfer(sp));
334  

334  

335  
    // `stack` needs this
335  
    // `stack` needs this
336  
    // to clean up correctly
336  
    // to clean up correctly
337  
    st_.run_dtors(
337  
    st_.run_dtors(
338  
        ! sp_.is_not_shared_and_deallocate_is_trivial());
338  
        ! sp_.is_not_shared_and_deallocate_is_trivial());
339  
}
339  
}
340  

340  

341  
value
341  
value
342  
value_stack::
342  
value_stack::
343  
release() noexcept
343  
release() noexcept
344  
{
344  
{
345  
    // This means the caller did not
345  
    // This means the caller did not
346  
    // cause a single top level element
346  
    // cause a single top level element
347  
    // to be produced.
347  
    // to be produced.
348  
    BOOST_ASSERT(st_.size() == 1);
348  
    BOOST_ASSERT(st_.size() == 1);
349  

349  

350  
    // give up shared ownership
350  
    // give up shared ownership
351  
    sp_ = {};
351  
    sp_ = {};
352  

352  

353  
    return pilfer(*st_.release(1));
353  
    return pilfer(*st_.release(1));
354  
}
354  
}
355  

355  

356  
//----------------------------------------------------------
356  
//----------------------------------------------------------
357  

357  

358  
void
358  
void
359  
value_stack::
359  
value_stack::
360  
push_array(std::size_t n)
360  
push_array(std::size_t n)
361  
{
361  
{
362  
    // we already have room if n > 0
362  
    // we already have room if n > 0
363  
    if(BOOST_JSON_UNLIKELY(n == 0))
363  
    if(BOOST_JSON_UNLIKELY(n == 0))
364  
        st_.maybe_grow();
364  
        st_.maybe_grow();
365  
    detail::unchecked_array ua(
365  
    detail::unchecked_array ua(
366  
        st_.release(n), n, sp_);
366  
        st_.release(n), n, sp_);
367  
    st_.exchange(std::move(ua));
367  
    st_.exchange(std::move(ua));
368  
}
368  
}
369  

369  

370  
void
370  
void
371  
value_stack::
371  
value_stack::
372  
push_object(std::size_t n)
372  
push_object(std::size_t n)
373  
{
373  
{
374  
    // we already have room if n > 0
374  
    // we already have room if n > 0
375  
    if(BOOST_JSON_UNLIKELY(n == 0))
375  
    if(BOOST_JSON_UNLIKELY(n == 0))
376  
        st_.maybe_grow();
376  
        st_.maybe_grow();
377  
    detail::unchecked_object uo(
377  
    detail::unchecked_object uo(
378  
        st_.release(n * 2), n, sp_);
378  
        st_.release(n * 2), n, sp_);
379  
    st_.exchange(std::move(uo));
379  
    st_.exchange(std::move(uo));
380  
}
380  
}
381  

381  

382  
void
382  
void
383  
value_stack::
383  
value_stack::
384  
push_chars(
384  
push_chars(
385  
    string_view s)
385  
    string_view s)
386  
{
386  
{
387  
    st_.append(s);
387  
    st_.append(s);
388  
}
388  
}
389  

389  

390  
void
390  
void
391  
value_stack::
391  
value_stack::
392  
push_key(
392  
push_key(
393  
    string_view s)
393  
    string_view s)
394  
{
394  
{
395  
    if(! st_.has_chars())
395  
    if(! st_.has_chars())
396  
    {
396  
    {
397  
        st_.push(detail::key_t{}, s, sp_);
397  
        st_.push(detail::key_t{}, s, sp_);
398  
        return;
398  
        return;
399  
    }
399  
    }
400  
    auto part = st_.release_string();
400  
    auto part = st_.release_string();
401  
    st_.push(detail::key_t{}, part, s, sp_);
401  
    st_.push(detail::key_t{}, part, s, sp_);
402  
}
402  
}
403  

403  

404  
void
404  
void
405  
value_stack::
405  
value_stack::
406  
push_string(
406  
push_string(
407  
    string_view s)
407  
    string_view s)
408  
{
408  
{
409  
    if(! st_.has_chars())
409  
    if(! st_.has_chars())
410  
    {
410  
    {
411  
        // fast path
411  
        // fast path
412  
        st_.push(s, sp_);
412  
        st_.push(s, sp_);
413  
        return;
413  
        return;
414  
    }
414  
    }
415  

415  

416  
    // VFALCO We could add a special
416  
    // VFALCO We could add a special
417  
    // private ctor to string that just
417  
    // private ctor to string that just
418  
    // creates uninitialized space,
418  
    // creates uninitialized space,
419  
    // to reduce member function calls.
419  
    // to reduce member function calls.
420  
    auto part = st_.release_string();
420  
    auto part = st_.release_string();
421  
    auto& str = st_.push(
421  
    auto& str = st_.push(
422  
        string_kind, sp_).get_string();
422  
        string_kind, sp_).get_string();
423  
    str.reserve(
423  
    str.reserve(
424  
        part.size() + s.size());
424  
        part.size() + s.size());
425  
    std::memcpy(
425  
    std::memcpy(
426  
        str.data(),
426  
        str.data(),
427  
        part.data(), part.size());
427  
        part.data(), part.size());
428  
    std::memcpy(
428  
    std::memcpy(
429  
        str.data() + part.size(),
429  
        str.data() + part.size(),
430  
        s.data(), s.size());
430  
        s.data(), s.size());
431  
    str.grow(part.size() + s.size());
431  
    str.grow(part.size() + s.size());
432  
}
432  
}
433  

433  

434  
void
434  
void
435  
value_stack::
435  
value_stack::
436  
push_int64(
436  
push_int64(
437  
    int64_t i)
437  
    int64_t i)
438  
{
438  
{
439  
    st_.push(i, sp_);
439  
    st_.push(i, sp_);
440  
}
440  
}
441  

441  

442  
void
442  
void
443  
value_stack::
443  
value_stack::
444  
push_uint64(
444  
push_uint64(
445  
    uint64_t u)
445  
    uint64_t u)
446  
{
446  
{
447  
    st_.push(u, sp_);
447  
    st_.push(u, sp_);
448  
}
448  
}
449  

449  

450  
void
450  
void
451  
value_stack::
451  
value_stack::
452  
push_double(
452  
push_double(
453  
    double d)
453  
    double d)
454  
{
454  
{
455  
    st_.push(d, sp_);
455  
    st_.push(d, sp_);
456  
}
456  
}
457  

457  

458  
void
458  
void
459  
value_stack::
459  
value_stack::
460  
push_bool(
460  
push_bool(
461  
    bool b)
461  
    bool b)
462  
{
462  
{
463  
    st_.push(b, sp_);
463  
    st_.push(b, sp_);
464  
}
464  
}
465  

465  

466  
void
466  
void
467  
value_stack::
467  
value_stack::
468  
push_null()
468  
push_null()
469  
{
469  
{
470  
    st_.push(nullptr, sp_);
470  
    st_.push(nullptr, sp_);
471  
}
471  
}
472  

472  

473  
} // namespace json
473  
} // namespace json
474  
} // namespace boost
474  
} // namespace boost
475  

475  

476  
#endif
476  
#endif