Coverage Report

Created: 2025-12-08 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boost/boost/json/impl/object.hpp
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_OBJECT_HPP
11
#define BOOST_JSON_IMPL_OBJECT_HPP
12
13
#include <boost/core/detail/static_assert.hpp>
14
#include <boost/json/value.hpp>
15
#include <iterator>
16
#include <cmath>
17
#include <type_traits>
18
#include <utility>
19
20
namespace boost {
21
namespace json {
22
23
namespace detail {
24
25
// Objects with size less than or equal
26
// to this number will use a linear search
27
// instead of the more expensive hash function.
28
static
29
constexpr
30
std::size_t
31
small_object_size_ = 18;
32
33
BOOST_CORE_STATIC_ASSERT(
34
    small_object_size_ < BOOST_JSON_MAX_STRUCTURED_SIZE);
35
36
} // detail
37
38
//----------------------------------------------------------
39
40
struct alignas(key_value_pair)
41
    object::table
42
{
43
    std::uint32_t size = 0;
44
    std::uint32_t capacity = 0;
45
    std::uintptr_t salt = 0;
46
47
#if defined(_MSC_VER) && BOOST_JSON_ARCH == 32
48
    // VFALCO If we make key_value_pair smaller,
49
    //        then we might want to revisit this
50
    //        padding.
51
    BOOST_CORE_STATIC_ASSERT( sizeof(key_value_pair) == 32 );
52
    char pad[4] = {}; // silence warnings
53
#endif
54
55
    constexpr table();
56
57
    // returns true if we use a linear
58
    // search instead of the hash table.
59
    bool is_small() const noexcept
60
26.8k
    {
61
26.8k
        return capacity <=
62
26.8k
            detail::small_object_size_;
63
26.8k
    }
64
65
    key_value_pair&
66
    operator[](
67
        std::size_t pos) noexcept
68
2.42M
    {
69
2.42M
        return reinterpret_cast<
70
2.42M
            key_value_pair*>(
71
2.42M
                this + 1)[pos];
72
2.42M
    }
73
74
    // VFALCO This is exported for tests
75
    BOOST_JSON_DECL
76
    std::size_t
77
    digest(string_view key) const noexcept;
78
79
    inline
80
    index_t&
81
    bucket(std::size_t hash) noexcept;
82
83
    inline
84
    index_t&
85
    bucket(string_view key) noexcept;
86
87
    inline
88
    void
89
    clear() noexcept;
90
91
    static
92
    inline
93
    table*
94
    allocate(
95
        std::size_t capacity,
96
        std::uintptr_t salt,
97
        storage_ptr const& sp);
98
99
    static
100
    void
101
    deallocate(
102
        table* p,
103
        storage_ptr const& sp) noexcept
104
5.07k
    {
105
5.07k
        if(p->capacity == 0)
106
0
            return;
107
5.07k
        if(! p->is_small())
108
930
            sp->deallocate(p,
109
930
                sizeof(table) + p->capacity * (
110
930
                    sizeof(key_value_pair) +
111
930
                    sizeof(index_t)));
112
4.14k
        else
113
4.14k
            sp->deallocate(p,
114
4.14k
                sizeof(table) + p->capacity *
115
4.14k
                    sizeof(key_value_pair));
116
5.07k
    }
117
};
118
119
//----------------------------------------------------------
120
121
class object::revert_construct
122
{
123
    object* obj_;
124
125
    BOOST_JSON_DECL
126
    void
127
    destroy() noexcept;
128
129
public:
130
    explicit
131
    revert_construct(
132
        object& obj) noexcept
133
0
        : obj_(&obj)
134
0
    {
135
0
    }
136
137
    ~revert_construct()
138
0
    {
139
0
        if(! obj_)
140
0
            return;
141
0
        destroy();
142
0
    }
143
144
    void
145
    commit() noexcept
146
0
    {
147
0
        obj_ = nullptr;
148
0
    }
149
};
150
151
//----------------------------------------------------------
152
153
class object::revert_insert
154
{
155
    object* obj_;
156
    table* t_ = nullptr;
157
    std::size_t size_;
158
159
    BOOST_JSON_DECL
160
    void
161
    destroy() noexcept;
162
163
public:
164
    explicit
165
    revert_insert(
166
        object& obj,
167
        std::size_t capacity)
168
0
        : obj_(&obj)
169
0
        , size_(obj_->size())
170
0
    {
171
0
        if( capacity > obj_->capacity() )
172
0
            t_ = obj_->reserve_impl(capacity);
173
0
    }
174
175
    ~revert_insert()
176
0
    {
177
0
        if(! obj_)
178
0
            return;
179
180
0
        destroy();
181
0
        if( t_ )
182
0
        {
183
0
            table::deallocate( obj_->t_, obj_->sp_ );
184
0
            obj_->t_ = t_;
185
0
        }
186
0
        else
187
0
        {
188
0
            obj_->t_->size = static_cast<index_t>(size_);
189
0
        }
190
0
    }
191
192
    void
193
    commit() noexcept
194
0
    {
195
0
        BOOST_ASSERT(obj_);
196
0
        if( t_ )
197
0
            table::deallocate( t_, obj_->sp_ );
198
0
        obj_ = nullptr;
199
0
    }
200
};
201
202
//----------------------------------------------------------
203
//
204
// Iterators
205
//
206
//----------------------------------------------------------
207
208
auto
209
object::
210
begin() noexcept ->
211
    iterator
212
35.7k
{
213
35.7k
    return &(*t_)[0];
214
35.7k
}
215
216
auto
217
object::
218
begin() const noexcept ->
219
    const_iterator
220
7.65k
{
221
7.65k
    return &(*t_)[0];
222
7.65k
}
223
224
auto
225
object::
226
cbegin() const noexcept ->
227
    const_iterator
228
0
{
229
0
    return &(*t_)[0];
230
0
}
231
232
auto
233
object::
234
end() noexcept ->
235
    iterator
236
5.07k
{
237
5.07k
    return &(*t_)[t_->size];
238
5.07k
}
239
240
auto
241
object::
242
end() const noexcept ->
243
    const_iterator
244
9.66k
{
245
9.66k
    return &(*t_)[t_->size];
246
9.66k
}
247
248
auto
249
object::
250
cend() const noexcept ->
251
    const_iterator
252
0
{
253
0
    return &(*t_)[t_->size];
254
0
}
255
256
auto
257
object::
258
rbegin() noexcept ->
259
    reverse_iterator
260
0
{
261
0
    return reverse_iterator(end());
262
0
}
263
264
auto
265
object::
266
rbegin() const noexcept ->
267
    const_reverse_iterator
268
0
{
269
0
    return const_reverse_iterator(end());
270
0
}
271
272
auto
273
object::
274
crbegin() const noexcept ->
275
    const_reverse_iterator
276
0
{
277
0
    return const_reverse_iterator(end());
278
0
}
279
280
auto
281
object::
282
rend() noexcept ->
283
    reverse_iterator
284
0
{
285
0
    return reverse_iterator(begin());
286
0
}
287
288
auto
289
object::
290
rend() const noexcept ->
291
    const_reverse_iterator
292
0
{
293
0
    return const_reverse_iterator(begin());
294
0
}
295
296
auto
297
object::
298
crend() const noexcept ->
299
    const_reverse_iterator
300
0
{
301
0
    return const_reverse_iterator(begin());
302
0
}
303
304
//----------------------------------------------------------
305
//
306
// Capacity
307
//
308
//----------------------------------------------------------
309
310
bool
311
object::
312
empty() const noexcept
313
0
{
314
0
    return t_->size == 0;
315
0
}
316
317
auto
318
object::
319
size() const noexcept ->
320
    std::size_t
321
0
{
322
0
    return t_->size;
323
0
}
324
325
constexpr
326
std::size_t
327
object::
328
max_size() noexcept
329
0
{
330
    // max_size depends on the address model
331
0
    using min = std::integral_constant<std::size_t,
332
0
        (std::size_t(-1) - sizeof(table)) /
333
0
            (sizeof(key_value_pair) + sizeof(index_t))>;
334
0
    return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
335
0
        min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
336
0
}
337
338
auto
339
object::
340
capacity() const noexcept ->
341
    std::size_t
342
0
{
343
0
    return t_->capacity;
344
0
}
345
346
void
347
object::
348
reserve(std::size_t new_capacity)
349
0
{
350
0
    if( new_capacity <= capacity() )
351
0
        return;
352
0
    table* const old_table = reserve_impl(new_capacity);
353
0
    table::deallocate( old_table, sp_ );
354
0
}
355
356
//----------------------------------------------------------
357
//
358
// Lookup
359
//
360
//----------------------------------------------------------
361
362
value&
363
object::
364
at(string_view key, source_location const& loc) &
365
0
{
366
0
    auto const& self = *this;
367
0
    return const_cast< value& >( self.at(key, loc) );
368
0
}
369
370
value&&
371
object::
372
at(string_view key, source_location const& loc) &&
373
0
{
374
0
    return std::move( at(key, loc) );
375
0
}
376
377
//----------------------------------------------------------
378
379
template<class P, class>
380
auto
381
object::
382
insert(P&& p) ->
383
    std::pair<iterator, bool>
384
{
385
    key_value_pair v(
386
        std::forward<P>(p), sp_);
387
    return emplace_impl( v.key(), pilfer(v) );
388
}
389
390
template<class M>
391
auto
392
object::
393
insert_or_assign(
394
    string_view key, M&& m) ->
395
        std::pair<iterator, bool>
396
{
397
    std::pair<iterator, bool> result = emplace_impl(
398
        key, key, static_cast<M&&>(m) );
399
    if( !result.second )
400
    {
401
        value(static_cast<M>(m), sp_).swap(
402
            result.first->value());
403
    }
404
    return result;
405
}
406
407
template<class Arg>
408
auto
409
object::
410
emplace(
411
    string_view key,
412
    Arg&& arg) ->
413
        std::pair<iterator, bool>
414
0
{
415
0
    return emplace_impl( key, key, static_cast<Arg&&>(arg) );
416
0
}
Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace<decltype(nullptr)>(boost::core::basic_string_view<char>, decltype(nullptr)&&)
Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace<boost::json::value>(boost::core::basic_string_view<char>, boost::json::value&&)
417
418
//----------------------------------------------------------
419
//
420
// (private)
421
//
422
//----------------------------------------------------------
423
424
template<class InputIt>
425
void
426
object::
427
construct(
428
    InputIt first,
429
    InputIt last,
430
    std::size_t min_capacity,
431
    std::input_iterator_tag)
432
{
433
    reserve(min_capacity);
434
    revert_construct r(*this);
435
    while(first != last)
436
    {
437
        insert(*first);
438
        ++first;
439
    }
440
    r.commit();
441
}
442
443
template<class InputIt>
444
void
445
object::
446
construct(
447
    InputIt first,
448
    InputIt last,
449
    std::size_t min_capacity,
450
    std::forward_iterator_tag)
451
{
452
    auto n = static_cast<
453
        std::size_t>(std::distance(
454
            first, last));
455
    if( n < min_capacity)
456
        n = min_capacity;
457
    reserve(n);
458
    revert_construct r(*this);
459
    while(first != last)
460
    {
461
        insert(*first);
462
        ++first;
463
    }
464
    r.commit();
465
}
466
467
template<class InputIt>
468
void
469
object::
470
insert(
471
    InputIt first,
472
    InputIt last,
473
    std::input_iterator_tag)
474
{
475
    // Since input iterators cannot be rewound,
476
    // we keep inserted elements on an exception.
477
    //
478
    while(first != last)
479
    {
480
        insert(*first);
481
        ++first;
482
    }
483
}
484
485
template<class InputIt>
486
void
487
object::
488
insert(
489
    InputIt first,
490
    InputIt last,
491
    std::forward_iterator_tag)
492
{
493
    auto const n =
494
        static_cast<std::size_t>(
495
            std::distance(first, last));
496
    auto const n0 = size();
497
    if(n > max_size() - n0)
498
    {
499
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
500
        detail::throw_system_error( error::object_too_large, &loc );
501
    }
502
    revert_insert r( *this, n0 + n );
503
    while(first != last)
504
    {
505
        insert(*first);
506
        ++first;
507
    }
508
    r.commit();
509
}
510
511
template< class... Args >
512
std::pair<object::iterator, bool>
513
object::
514
emplace_impl( string_view key, Args&& ... args )
515
0
{
516
0
    std::pair<iterator, std::size_t> search_result(nullptr, 0);
517
0
    if( !empty() )
518
0
    {
519
0
        search_result = detail::find_in_object(*this, key);
520
0
        if( search_result.first )
521
0
            return { search_result.first, false };
522
0
    }
523
524
    // we create the new value before reserving, in case it is a reference to
525
    // a subobject of the current object
526
0
    key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
527
    // the key might get deallocated too
528
0
    key = kv.key();
529
530
0
    std::size_t const old_capacity = capacity();
531
0
    reserve(size() + 1);
532
0
    if( (empty() && capacity() > detail::small_object_size_)
533
0
            || (capacity() != old_capacity) )
534
0
        search_result.second = detail::digest(
535
0
            key.begin(), key.end(), t_->salt);
536
537
0
    BOOST_ASSERT(
538
0
        t_->is_small() ||
539
0
        (search_result.second ==
540
0
            detail::digest(key.begin(), key.end(), t_->salt)) );
541
542
0
    return { insert_impl(pilfer(kv), search_result.second), true };
543
0
}
Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace_impl<boost::core::basic_string_view<char>&, decltype(nullptr)>(boost::core::basic_string_view<char>, boost::core::basic_string_view<char>&, decltype(nullptr)&&)
Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace_impl<boost::core::basic_string_view<char>&, boost::json::value>(boost::core::basic_string_view<char>, boost::core::basic_string_view<char>&, boost::json::value&&)
544
545
//----------------------------------------------------------
546
547
namespace detail {
548
549
unchecked_object::
550
~unchecked_object()
551
20.1k
{
552
20.1k
    if(! data_)
553
8.11k
        return;
554
12.0k
    if(sp_.is_not_shared_and_deallocate_is_trivial())
555
1.82k
        return;
556
10.2k
    value* p = data_;
557
10.2k
    while(size_--)
558
0
    {
559
0
        p[0].~value();
560
0
        p[1].~value();
561
0
        p += 2;
562
0
    }
563
10.2k
}
564
565
} // detail
566
567
} // namespace json
568
} // namespace boost
569
570
#endif