Coverage Report

Created: 2026-03-23 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boost/boost/json/impl/value_stack.ipp
Line
Count
Source
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_IMPL_VALUE_STACK_IPP
11
#define BOOST_JSON_IMPL_VALUE_STACK_IPP
12
13
#include <boost/json/value_stack.hpp>
14
#include <cstring>
15
#include <stdexcept>
16
#include <utility>
17
18
namespace boost {
19
namespace json {
20
21
//--------------------------------------
22
23
value_stack::
24
stack::
25
~stack()
26
22.1k
{
27
22.1k
    clear();
28
22.1k
    if( begin_ != temp_ &&
29
8.70k
        begin_ != nullptr)
30
8.50k
        sp_->deallocate(
31
8.50k
            begin_,
32
8.50k
            (end_ - begin_) *
33
8.50k
                sizeof(value));
34
22.1k
}
35
36
value_stack::
37
stack::
38
stack(
39
    storage_ptr sp,
40
    void* temp,
41
    std::size_t size) noexcept
42
22.1k
    : sp_(std::move(sp))
43
22.1k
    , temp_(temp)
44
22.1k
{
45
22.1k
    if(size >= min_size_ *
46
22.1k
        sizeof(value))
47
13.2k
    {
48
13.2k
        begin_ = reinterpret_cast<
49
13.2k
            value*>(temp);
50
13.2k
        top_ = begin_;
51
13.2k
        end_ = begin_ +
52
13.2k
            size / sizeof(value);
53
13.2k
    }
54
8.94k
    else
55
8.94k
    {
56
8.94k
        begin_ = nullptr;
57
8.94k
        top_ = nullptr;
58
8.94k
        end_ = nullptr;
59
8.94k
    }
60
22.1k
}
61
62
void
63
value_stack::
64
stack::
65
run_dtors(bool b) noexcept
66
35.9k
{
67
35.9k
    run_dtors_ = b;
68
35.9k
}
69
70
std::size_t
71
value_stack::
72
stack::
73
size() const noexcept
74
9.73k
{
75
9.73k
    return top_ - begin_;
76
9.73k
}
77
78
bool
79
value_stack::
80
stack::
81
has_chars()
82
1.82M
{
83
1.82M
    return chars_ != 0;
84
1.82M
}
85
86
//--------------------------------------
87
88
// destroy the values but
89
// not the stack allocation.
90
void
91
value_stack::
92
stack::
93
clear() noexcept
94
58.0k
{
95
58.0k
    if(top_ != begin_)
96
8.28k
    {
97
8.28k
        if(run_dtors_)
98
5.63k
            for(auto it = top_;
99
1.16M
                it-- != begin_;)
100
1.16M
                it->~value();
101
8.28k
        top_ = begin_;
102
8.28k
    }
103
58.0k
    chars_ = 0;
104
58.0k
}
105
106
void
107
value_stack::
108
stack::
109
maybe_grow()
110
21.1k
{
111
21.1k
    if(top_ >= end_)
112
148
        grow_one();
113
21.1k
}
114
115
// make room for at least one more value
116
void
117
value_stack::
118
stack::
119
grow_one()
120
14.2k
{
121
14.2k
    BOOST_ASSERT(chars_ == 0);
122
14.2k
    std::size_t const capacity =
123
14.2k
        end_ - begin_;
124
14.2k
    std::size_t new_cap = min_size_;
125
    // VFALCO check overflow here
126
39.8k
    while(new_cap < capacity + 1)
127
25.6k
        new_cap <<= 1;
128
14.2k
    auto const begin =
129
14.2k
        reinterpret_cast<value*>(
130
14.2k
            sp_->allocate(
131
14.2k
                new_cap * sizeof(value)));
132
14.2k
    std::size_t const cur_size = top_ - begin_;
133
14.2k
    if(begin_)
134
6.57k
    {
135
6.57k
        std::memcpy(
136
6.57k
            reinterpret_cast<char*>(begin),
137
6.57k
            reinterpret_cast<char*>(begin_),
138
6.57k
            size() * sizeof(value));
139
6.57k
        if(begin_ != temp_)
140
6.35k
            sp_->deallocate(begin_,
141
6.35k
                capacity * sizeof(value));
142
6.57k
    }
143
    // book-keeping
144
14.2k
    top_ = begin + cur_size;
145
14.2k
    end_ = begin + new_cap;
146
14.2k
    begin_ = begin;
147
14.2k
}
148
149
// make room for nchars additional characters.
150
void
151
value_stack::
152
stack::
153
grow(std::size_t nchars)
154
1.79k
{
155
    // needed capacity in values
156
1.79k
    std::size_t const needed =
157
1.79k
        size() +
158
1.79k
        1 +
159
1.79k
        ((chars_ + nchars +
160
1.79k
            sizeof(value) - 1) /
161
1.79k
                sizeof(value));
162
1.79k
    std::size_t const capacity =
163
1.79k
        end_ - begin_;
164
1.79k
    BOOST_ASSERT(
165
1.79k
        needed > capacity);
166
1.79k
    std::size_t new_cap = min_size_;
167
    // VFALCO check overflow here
168
9.78k
    while(new_cap < needed)
169
7.99k
        new_cap <<= 1;
170
1.79k
    auto const begin =
171
1.79k
        reinterpret_cast<value*>(
172
1.79k
            sp_->allocate(
173
1.79k
                new_cap * sizeof(value)));
174
1.79k
    std::size_t const cur_size = top_ - begin_;
175
1.79k
    if(begin_)
176
1.37k
    {
177
1.37k
        std::size_t amount =
178
1.37k
            size() * sizeof(value);
179
1.37k
        if(chars_ > 0)
180
1.00k
            amount += sizeof(value) + chars_;
181
1.37k
        std::memcpy(
182
1.37k
            reinterpret_cast<char*>(begin),
183
1.37k
            reinterpret_cast<char*>(begin_),
184
1.37k
            amount);
185
1.37k
        if(begin_ != temp_)
186
1.00k
            sp_->deallocate(begin_,
187
1.00k
                capacity * sizeof(value));
188
1.37k
    }
189
    // book-keeping
190
1.79k
    top_ = begin + cur_size;
191
1.79k
    end_ = begin + new_cap;
192
1.79k
    begin_ = begin;
193
1.79k
}
194
195
//--------------------------------------
196
197
void
198
value_stack::
199
stack::
200
append(string_view s)
201
155k
{
202
155k
    std::size_t const bytes_avail =
203
155k
        reinterpret_cast<
204
155k
            char const*>(end_) -
205
155k
        reinterpret_cast<
206
155k
            char const*>(top_);
207
    // make sure there is room for
208
    // pushing one more value without
209
    // clobbering the string.
210
155k
    if(sizeof(value) + chars_ +
211
155k
            s.size() > bytes_avail)
212
1.79k
        grow(s.size());
213
214
    // copy the new piece
215
155k
    std::memcpy(
216
155k
        reinterpret_cast<char*>(
217
155k
            top_ + 1) + chars_,
218
155k
        s.data(), s.size());
219
155k
    chars_ += s.size();
220
221
    // ensure a pushed value cannot
222
    // clobber the released string.
223
155k
    BOOST_ASSERT(
224
155k
        reinterpret_cast<char*>(
225
155k
            top_ + 1) + chars_ <=
226
155k
        reinterpret_cast<char*>(
227
155k
            end_));
228
155k
}
229
230
string_view
231
value_stack::
232
stack::
233
release_string() noexcept
234
17.5k
{
235
    // ensure a pushed value cannot
236
    // clobber the released string.
237
17.5k
    BOOST_ASSERT(
238
17.5k
        reinterpret_cast<char*>(
239
17.5k
            top_ + 1) + chars_ <=
240
17.5k
        reinterpret_cast<char*>(
241
17.5k
            end_));
242
17.5k
    auto const n = chars_;
243
17.5k
    chars_ = 0;
244
17.5k
    return { reinterpret_cast<
245
17.5k
        char const*>(top_ + 1), n };
246
17.5k
}
247
248
// transfer ownership of the top n
249
// elements of the stack to the caller
250
value*
251
value_stack::
252
stack::
253
release(std::size_t n) noexcept
254
60.0k
{
255
60.0k
    BOOST_ASSERT(n <= size());
256
60.0k
    BOOST_ASSERT(chars_ == 0);
257
60.0k
    top_ -= n;
258
60.0k
    return top_;
259
60.0k
}
260
261
template<class... Args>
262
value&
263
value_stack::
264
stack::
265
push(Args&&... args)
266
14.4M
{
267
14.4M
    BOOST_ASSERT(chars_ == 0);
268
14.4M
    if(top_ >= end_)
269
14.0k
        grow_one();
270
14.4M
    value& jv = detail::access::
271
14.4M
        construct_value(top_,
272
14.4M
            std::forward<Args>(args)...);
273
14.4M
    ++top_;
274
14.4M
    return jv;
275
14.4M
}
boost::json::value& boost::json::value_stack::stack::push<boost::json::detail::key_t, boost::core::basic_string_view<char>&, boost::json::storage_ptr&>(boost::json::detail::key_t&&, boost::core::basic_string_view<char>&, boost::json::storage_ptr&)
Line
Count
Source
266
1.76M
{
267
1.76M
    BOOST_ASSERT(chars_ == 0);
268
1.76M
    if(top_ >= end_)
269
1.48k
        grow_one();
270
1.76M
    value& jv = detail::access::
271
1.76M
        construct_value(top_,
272
1.76M
            std::forward<Args>(args)...);
273
1.76M
    ++top_;
274
1.76M
    return jv;
275
1.76M
}
boost::json::value& boost::json::value_stack::stack::push<boost::json::detail::key_t, boost::core::basic_string_view<char>&, boost::core::basic_string_view<char>&, boost::json::storage_ptr&>(boost::json::detail::key_t&&, boost::core::basic_string_view<char>&, boost::core::basic_string_view<char>&, boost::json::storage_ptr&)
Line
Count
Source
266
8.26k
{
267
8.26k
    BOOST_ASSERT(chars_ == 0);
268
8.26k
    if(top_ >= end_)
269
0
        grow_one();
270
8.26k
    value& jv = detail::access::
271
8.26k
        construct_value(top_,
272
8.26k
            std::forward<Args>(args)...);
273
8.26k
    ++top_;
274
8.26k
    return jv;
275
8.26k
}
boost::json::value& boost::json::value_stack::stack::push<boost::core::basic_string_view<char>&, boost::json::storage_ptr&>(boost::core::basic_string_view<char>&, boost::json::storage_ptr&)
Line
Count
Source
266
36.4k
{
267
36.4k
    BOOST_ASSERT(chars_ == 0);
268
36.4k
    if(top_ >= end_)
269
290
        grow_one();
270
36.4k
    value& jv = detail::access::
271
36.4k
        construct_value(top_,
272
36.4k
            std::forward<Args>(args)...);
273
36.4k
    ++top_;
274
36.4k
    return jv;
275
36.4k
}
boost::json::value& boost::json::value_stack::stack::push<boost::json::string_kind_t const&, boost::json::storage_ptr&>(boost::json::string_kind_t const&, boost::json::storage_ptr&)
Line
Count
Source
266
9.28k
{
267
9.28k
    BOOST_ASSERT(chars_ == 0);
268
9.28k
    if(top_ >= end_)
269
0
        grow_one();
270
9.28k
    value& jv = detail::access::
271
9.28k
        construct_value(top_,
272
9.28k
            std::forward<Args>(args)...);
273
9.28k
    ++top_;
274
9.28k
    return jv;
275
9.28k
}
boost::json::value& boost::json::value_stack::stack::push<long&, boost::json::storage_ptr&>(long&, boost::json::storage_ptr&)
Line
Count
Source
266
7.09M
{
267
7.09M
    BOOST_ASSERT(chars_ == 0);
268
7.09M
    if(top_ >= end_)
269
2.97k
        grow_one();
270
7.09M
    value& jv = detail::access::
271
7.09M
        construct_value(top_,
272
7.09M
            std::forward<Args>(args)...);
273
7.09M
    ++top_;
274
7.09M
    return jv;
275
7.09M
}
boost::json::value& boost::json::value_stack::stack::push<unsigned long&, boost::json::storage_ptr&>(unsigned long&, boost::json::storage_ptr&)
Line
Count
Source
266
28.3k
{
267
28.3k
    BOOST_ASSERT(chars_ == 0);
268
28.3k
    if(top_ >= end_)
269
309
        grow_one();
270
28.3k
    value& jv = detail::access::
271
28.3k
        construct_value(top_,
272
28.3k
            std::forward<Args>(args)...);
273
28.3k
    ++top_;
274
28.3k
    return jv;
275
28.3k
}
boost::json::value& boost::json::value_stack::stack::push<double&, boost::json::storage_ptr&>(double&, boost::json::storage_ptr&)
Line
Count
Source
266
5.40M
{
267
5.40M
    BOOST_ASSERT(chars_ == 0);
268
5.40M
    if(top_ >= end_)
269
8.74k
        grow_one();
270
5.40M
    value& jv = detail::access::
271
5.40M
        construct_value(top_,
272
5.40M
            std::forward<Args>(args)...);
273
5.40M
    ++top_;
274
5.40M
    return jv;
275
5.40M
}
boost::json::value& boost::json::value_stack::stack::push<bool&, boost::json::storage_ptr&>(bool&, boost::json::storage_ptr&)
Line
Count
Source
266
48.6k
{
267
48.6k
    BOOST_ASSERT(chars_ == 0);
268
48.6k
    if(top_ >= end_)
269
145
        grow_one();
270
48.6k
    value& jv = detail::access::
271
48.6k
        construct_value(top_,
272
48.6k
            std::forward<Args>(args)...);
273
48.6k
    ++top_;
274
48.6k
    return jv;
275
48.6k
}
boost::json::value& boost::json::value_stack::stack::push<decltype(nullptr), boost::json::storage_ptr&>(decltype(nullptr)&&, boost::json::storage_ptr&)
Line
Count
Source
266
15.6k
{
267
15.6k
    BOOST_ASSERT(chars_ == 0);
268
15.6k
    if(top_ >= end_)
269
109
        grow_one();
270
15.6k
    value& jv = detail::access::
271
15.6k
        construct_value(top_,
272
15.6k
            std::forward<Args>(args)...);
273
15.6k
    ++top_;
274
15.6k
    return jv;
275
15.6k
}
276
277
template<class Unchecked>
278
void
279
value_stack::
280
stack::
281
exchange(Unchecked&& u)
282
50.9k
{
283
50.9k
    BOOST_ASSERT(chars_ == 0);
284
50.9k
    union U
285
50.9k
    {
286
50.9k
        value v;
287
50.9k
        U() {}
boost::json::value_stack::stack::exchange<boost::json::detail::unchecked_array>(boost::json::detail::unchecked_array&&)::U::U()
Line
Count
Source
287
27.5k
        U() {}
boost::json::value_stack::stack::exchange<boost::json::detail::unchecked_object>(boost::json::detail::unchecked_object&&)::U::U()
Line
Count
Source
287
23.4k
        U() {}
288
50.9k
        ~U() {}
boost::json::value_stack::stack::exchange<boost::json::detail::unchecked_array>(boost::json::detail::unchecked_array&&)::U::~U()
Line
Count
Source
288
27.5k
        ~U() {}
boost::json::value_stack::stack::exchange<boost::json::detail::unchecked_object>(boost::json::detail::unchecked_object&&)::U::~U()
Line
Count
Source
288
23.4k
        ~U() {}
289
50.9k
    } jv;
290
    // construct value on the stack
291
    // to avoid clobbering top_[0],
292
    // which belongs to `u`.
293
50.9k
    detail::access::
294
50.9k
        construct_value(
295
50.9k
            &jv.v, std::move(u));
296
50.9k
    std::memcpy(
297
50.9k
        reinterpret_cast<
298
50.9k
            char*>(top_),
299
50.9k
        &jv.v, sizeof(value));
300
50.9k
    ++top_;
301
50.9k
}
void boost::json::value_stack::stack::exchange<boost::json::detail::unchecked_array>(boost::json::detail::unchecked_array&&)
Line
Count
Source
282
27.5k
{
283
27.5k
    BOOST_ASSERT(chars_ == 0);
284
27.5k
    union U
285
27.5k
    {
286
27.5k
        value v;
287
27.5k
        U() {}
288
27.5k
        ~U() {}
289
27.5k
    } jv;
290
    // construct value on the stack
291
    // to avoid clobbering top_[0],
292
    // which belongs to `u`.
293
27.5k
    detail::access::
294
27.5k
        construct_value(
295
27.5k
            &jv.v, std::move(u));
296
27.5k
    std::memcpy(
297
27.5k
        reinterpret_cast<
298
27.5k
            char*>(top_),
299
27.5k
        &jv.v, sizeof(value));
300
27.5k
    ++top_;
301
27.5k
}
void boost::json::value_stack::stack::exchange<boost::json::detail::unchecked_object>(boost::json::detail::unchecked_object&&)
Line
Count
Source
282
23.4k
{
283
23.4k
    BOOST_ASSERT(chars_ == 0);
284
23.4k
    union U
285
23.4k
    {
286
23.4k
        value v;
287
23.4k
        U() {}
288
23.4k
        ~U() {}
289
23.4k
    } jv;
290
    // construct value on the stack
291
    // to avoid clobbering top_[0],
292
    // which belongs to `u`.
293
23.4k
    detail::access::
294
23.4k
        construct_value(
295
23.4k
            &jv.v, std::move(u));
296
23.4k
    std::memcpy(
297
23.4k
        reinterpret_cast<
298
23.4k
            char*>(top_),
299
23.4k
        &jv.v, sizeof(value));
300
23.4k
    ++top_;
301
23.4k
}
302
303
//----------------------------------------------------------
304
305
value_stack::
306
~value_stack()
307
22.1k
{
308
    // default dtor is here so the
309
    // definition goes in the library
310
    // instead of the caller's TU.
311
22.1k
}
312
313
value_stack::
314
value_stack(
315
    storage_ptr sp,
316
    unsigned char* temp_buffer,
317
    std::size_t temp_size) noexcept
318
22.1k
    : st_(
319
22.1k
        std::move(sp),
320
22.1k
        temp_buffer,
321
22.1k
        temp_size)
322
22.1k
{
323
22.1k
}
324
325
void
326
value_stack::
327
reset(storage_ptr sp) noexcept
328
35.9k
{
329
35.9k
    st_.clear();
330
331
35.9k
    sp_.~storage_ptr();
332
35.9k
    ::new(&sp_) storage_ptr(
333
35.9k
        pilfer(sp));
334
335
    // `stack` needs this
336
    // to clean up correctly
337
35.9k
    st_.run_dtors(
338
35.9k
        ! sp_.is_not_shared_and_deallocate_is_trivial());
339
35.9k
}
340
341
value
342
value_stack::
343
release() noexcept
344
9.14k
{
345
    // This means the caller did not
346
    // cause a single top level element
347
    // to be produced.
348
9.14k
    BOOST_ASSERT(st_.size() == 1);
349
350
    // give up shared ownership
351
9.14k
    sp_ = {};
352
353
9.14k
    return pilfer(*st_.release(1));
354
9.14k
}
355
356
//----------------------------------------------------------
357
358
void
359
value_stack::
360
push_array(std::size_t n)
361
27.5k
{
362
    // we already have room if n > 0
363
27.5k
    if(BOOST_JSON_UNLIKELY(n == 0))
364
6.77k
        st_.maybe_grow();
365
27.5k
    detail::unchecked_array ua(
366
27.5k
        st_.release(n), n, sp_);
367
27.5k
    st_.exchange(std::move(ua));
368
27.5k
}
369
370
void
371
value_stack::
372
push_object(std::size_t n)
373
23.4k
{
374
    // we already have room if n > 0
375
23.4k
    if(BOOST_JSON_UNLIKELY(n == 0))
376
14.4k
        st_.maybe_grow();
377
23.4k
    detail::unchecked_object uo(
378
23.4k
        st_.release(n * 2), n, sp_);
379
23.4k
    st_.exchange(std::move(uo));
380
23.4k
}
381
382
void
383
value_stack::
384
push_chars(
385
    string_view s)
386
155k
{
387
155k
    st_.append(s);
388
155k
}
389
390
void
391
value_stack::
392
push_key(
393
    string_view s)
394
1.77M
{
395
1.77M
    if(! st_.has_chars())
396
1.76M
    {
397
1.76M
        st_.push(detail::key_t{}, s, sp_);
398
1.76M
        return;
399
1.76M
    }
400
8.26k
    auto part = st_.release_string();
401
8.26k
    st_.push(detail::key_t{}, part, s, sp_);
402
8.26k
}
403
404
void
405
value_stack::
406
push_string(
407
    string_view s)
408
45.7k
{
409
45.7k
    if(! st_.has_chars())
410
36.4k
    {
411
        // fast path
412
36.4k
        st_.push(s, sp_);
413
36.4k
        return;
414
36.4k
    }
415
416
    // VFALCO We could add a special
417
    // private ctor to string that just
418
    // creates uninitialized space,
419
    // to reduce member function calls.
420
9.28k
    auto part = st_.release_string();
421
9.28k
    auto& str = st_.push(
422
9.28k
        string_kind, sp_).get_string();
423
9.28k
    str.reserve(
424
9.28k
        part.size() + s.size());
425
9.28k
    std::memcpy(
426
9.28k
        str.data(),
427
9.28k
        part.data(), part.size());
428
9.28k
    std::memcpy(
429
9.28k
        str.data() + part.size(),
430
9.28k
        s.data(), s.size());
431
9.28k
    str.grow(part.size() + s.size());
432
9.28k
}
433
434
void
435
value_stack::
436
push_int64(
437
    int64_t i)
438
7.09M
{
439
7.09M
    st_.push(i, sp_);
440
7.09M
}
441
442
void
443
value_stack::
444
push_uint64(
445
    uint64_t u)
446
28.3k
{
447
28.3k
    st_.push(u, sp_);
448
28.3k
}
449
450
void
451
value_stack::
452
push_double(
453
    double d)
454
5.40M
{
455
5.40M
    st_.push(d, sp_);
456
5.40M
}
457
458
void
459
value_stack::
460
push_bool(
461
    bool b)
462
48.6k
{
463
48.6k
    st_.push(b, sp_);
464
48.6k
}
465
466
void
467
value_stack::
468
push_null()
469
15.6k
{
470
15.6k
    st_.push(nullptr, sp_);
471
15.6k
}
472
473
} // namespace json
474
} // namespace boost
475
476
#endif