Coverage Report

Created: 2026-09-19 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boost/boost/json/impl/array.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_ARRAY_IPP
11
#define BOOST_JSON_IMPL_ARRAY_IPP
12
13
#include <boost/core/detail/static_assert.hpp>
14
#include <boost/container_hash/hash.hpp>
15
#include <boost/json/array.hpp>
16
#include <boost/json/pilfer.hpp>
17
#include <boost/json/detail/except.hpp>
18
#include <cstdlib>
19
#include <limits>
20
#include <new>
21
#include <utility>
22
23
namespace boost {
24
namespace json {
25
26
//----------------------------------------------------------
27
28
constexpr array::table::table() = default;
29
30
// empty arrays point here
31
BOOST_JSON_REQUIRE_CONST_INIT
32
array::table array::empty_;
33
34
auto
35
array::
36
table::
37
allocate(
38
    std::size_t capacity,
39
    storage_ptr const& sp) ->
40
        table*
41
23.9k
{
42
23.9k
    BOOST_ASSERT(capacity > 0);
43
23.9k
    if(capacity > array::max_size())
44
0
    {
45
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
46
0
        detail::throw_system_error( error::array_too_large, &loc );
47
0
    }
48
23.9k
    auto p = reinterpret_cast<
49
23.9k
        table*>(sp->allocate(
50
23.9k
            sizeof(table) +
51
23.9k
                capacity * sizeof(value),
52
23.9k
            alignof(value)));
53
23.9k
    p->capacity = static_cast<
54
23.9k
        std::uint32_t>(capacity);
55
23.9k
    return p;
56
23.9k
}
57
58
void
59
array::
60
table::
61
deallocate(
62
    table* p,
63
    storage_ptr const& sp)
64
19.7k
{
65
19.7k
    if(p->capacity == 0)
66
5.32k
        return;
67
14.3k
    sp->deallocate(p,
68
14.3k
        sizeof(table) +
69
14.3k
            p->capacity * sizeof(value),
70
14.3k
        alignof(value));
71
14.3k
}
72
73
//----------------------------------------------------------
74
75
array::
76
revert_insert::
77
revert_insert(
78
    const_iterator pos,
79
    std::size_t n,
80
    array& arr)
81
0
    : arr_(&arr)
82
0
    , i_(pos - arr_->data())
83
0
    , n_(n)
84
0
{
85
0
    BOOST_ASSERT(
86
0
        pos >= arr_->begin() &&
87
0
        pos <= arr_->end());
88
0
    if( n_ <= arr_->capacity() -
89
0
        arr_->size())
90
0
    {
91
        // fast path
92
0
        p = arr_->data() + i_;
93
0
        if(n_ == 0)
94
0
            return;
95
0
        relocate(
96
0
            p + n_,
97
0
            p,
98
0
            arr_->size() - i_);
99
0
        arr_->t_->size = static_cast<
100
0
            std::uint32_t>(
101
0
                arr_->t_->size + n_);
102
0
        return;
103
0
    }
104
0
    if(n_ > max_size() - arr_->size())
105
0
    {
106
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
107
0
        detail::throw_system_error( error::array_too_large, &loc );
108
0
    }
109
0
    auto t = table::allocate(
110
0
        arr_->growth(arr_->size() + n_),
111
0
            arr_->sp_);
112
0
    t->size = static_cast<std::uint32_t>(
113
0
        arr_->size() + n_);
114
0
    p = &(*t)[0] + i_;
115
0
    relocate(
116
0
        &(*t)[0],
117
0
        arr_->data(),
118
0
        i_);
119
0
    relocate(
120
0
        &(*t)[i_ + n_],
121
0
        arr_->data() + i_,
122
0
        arr_->size() - i_);
123
0
    t = detail::exchange(arr_->t_, t);
124
0
    table::deallocate(t, arr_->sp_);
125
0
}
126
127
array::
128
revert_insert::
129
~revert_insert()
130
0
{
131
0
    if(! arr_)
132
0
        return;
133
0
    BOOST_ASSERT(n_ != 0);
134
0
    auto const pos =
135
0
        arr_->data() + i_;
136
0
    arr_->destroy(pos, p);
137
0
    arr_->t_->size = static_cast<
138
0
        std::uint32_t>(
139
0
            arr_->t_->size - n_);
140
0
    relocate(
141
0
        pos,
142
0
        pos + n_,
143
0
        arr_->size() - i_);
144
0
}
145
146
//----------------------------------------------------------
147
148
void
149
array::
150
destroy(
151
    value* first, value* last) noexcept
152
0
{
153
0
    if(sp_.is_not_shared_and_deallocate_is_trivial())
154
0
        return;
155
0
    while(last-- != first)
156
0
        last->~value();
157
0
}
158
159
void
160
array::
161
destroy() noexcept
162
21.4k
{
163
21.4k
    if(sp_.is_not_shared_and_deallocate_is_trivial())
164
1.73k
        return;
165
19.7k
    auto last = end();
166
19.7k
    auto const first = begin();
167
5.73M
    while(last-- != first)
168
5.71M
        last->~value();
169
19.7k
    table::deallocate(t_, sp_);
170
19.7k
}
171
172
//----------------------------------------------------------
173
//
174
// Special Members
175
//
176
//----------------------------------------------------------
177
178
array::
179
array(detail::unchecked_array&& ua)
180
31.4k
    : sp_(ua.storage())
181
31.4k
{
182
31.4k
    BOOST_CORE_STATIC_ASSERT( alignof(table) == alignof(value) );
183
31.4k
    if(ua.size() == 0)
184
7.50k
    {
185
7.50k
        t_ = &empty_;
186
7.50k
        return;
187
7.50k
    }
188
23.9k
    t_= table::allocate(
189
23.9k
        ua.size(), sp_);
190
23.9k
    t_->size = static_cast<
191
23.9k
        std::uint32_t>(ua.size());
192
23.9k
    ua.relocate(data());
193
23.9k
}
194
195
array::
196
~array() noexcept
197
21.4k
{
198
21.4k
    destroy();
199
21.4k
}
200
201
array::
202
array(
203
    std::size_t count,
204
    value const& v,
205
    storage_ptr sp)
206
0
    : sp_(std::move(sp))
207
0
{
208
0
    if(count == 0)
209
0
    {
210
0
        t_ = &empty_;
211
0
        return;
212
0
    }
213
0
    t_= table::allocate(
214
0
        count, sp_);
215
0
    t_->size = 0;
216
0
    revert_construct r(*this);
217
0
    while(count--)
218
0
    {
219
0
        ::new(end()) value(v, sp_);
220
0
        ++t_->size;
221
0
    }
222
0
    r.commit();
223
0
}
224
225
array::
226
array(
227
    std::size_t count,
228
    storage_ptr sp)
229
0
    : sp_(std::move(sp))
230
0
{
231
0
    if(count == 0)
232
0
    {
233
0
        t_ = &empty_;
234
0
        return;
235
0
    }
236
0
    t_ = table::allocate(
237
0
        count, sp_);
238
0
    t_->size = static_cast<
239
0
        std::uint32_t>(count);
240
0
    auto p = data();
241
0
    do
242
0
    {
243
0
        ::new(p++) value(sp_);
244
0
    }
245
0
    while(--count);
246
0
}
247
248
array::
249
array(array const& other)
250
0
    : array(other, other.sp_)
251
0
{
252
0
}
253
254
array::
255
array(
256
    array const& other,
257
    storage_ptr sp)
258
0
    : sp_(std::move(sp))
259
0
{
260
0
    if(other.empty())
261
0
    {
262
0
        t_ = &empty_;
263
0
        return;
264
0
    }
265
0
    t_ = table::allocate(
266
0
        other.size(), sp_);
267
0
    t_->size = 0;
268
0
    revert_construct r(*this);
269
0
    auto src = other.data();
270
0
    auto dest = data();
271
0
    auto const n = other.size();
272
0
    do
273
0
    {
274
0
        ::new(dest++) value(
275
0
            *src++, sp_);
276
0
        ++t_->size;
277
0
    }
278
0
    while(t_->size < n);
279
0
    r.commit();
280
0
}
281
282
array::
283
array(
284
    array&& other,
285
    storage_ptr sp)
286
0
    : sp_(std::move(sp))
287
0
{
288
0
    if(*sp_ == *other.sp_)
289
0
    {
290
        // same resource
291
0
        t_ = detail::exchange(
292
0
            other.t_, &empty_);
293
0
        return;
294
0
    }
295
0
    else if(other.empty())
296
0
    {
297
0
        t_ = &empty_;
298
0
        return;
299
0
    }
300
    // copy
301
0
    t_ = table::allocate(
302
0
        other.size(), sp_);
303
0
    t_->size = 0;
304
0
    revert_construct r(*this);
305
0
    auto src = other.data();
306
0
    auto dest = data();
307
0
    auto const n = other.size();
308
0
    do
309
0
    {
310
0
        ::new(dest++) value(
311
0
            *src++, sp_);
312
0
        ++t_->size;
313
0
    }
314
0
    while(t_->size < n);
315
0
    r.commit();
316
0
}
317
318
array::
319
array(
320
    std::initializer_list<
321
        value_ref> init,
322
    storage_ptr sp)
323
0
    : sp_(std::move(sp))
324
0
{
325
0
    if(init.size() == 0)
326
0
    {
327
0
        t_ = &empty_;
328
0
        return;
329
0
    }
330
0
    t_ = table::allocate(
331
0
        init.size(), sp_);
332
0
    t_->size = 0;
333
0
    revert_construct r(*this);
334
0
    value_ref::write_array(
335
0
        data(), init, sp_);
336
0
    t_->size = static_cast<
337
0
        std::uint32_t>(init.size());
338
0
    r.commit();
339
0
}
340
341
//----------------------------------------------------------
342
343
array&
344
array::
345
operator=(array const& other)
346
0
{
347
0
    array(other,
348
0
        storage()).swap(*this);
349
0
    return *this;
350
0
}
351
352
array&
353
array::
354
operator=(array&& other)
355
0
{
356
0
    array(std::move(other),
357
0
        storage()).swap(*this);
358
0
    return *this;
359
0
}
360
361
array&
362
array::
363
operator=(
364
    std::initializer_list<value_ref> init)
365
0
{
366
0
    array(init,
367
0
        storage()).swap(*this);
368
0
    return *this;
369
0
}
370
371
//----------------------------------------------------------
372
//
373
// Element access
374
//
375
//----------------------------------------------------------
376
377
system::result<value&>
378
array::try_at(std::size_t pos) noexcept
379
0
{
380
0
    if(pos >= t_->size)
381
0
    {
382
0
        system::error_code ec;
383
0
        BOOST_JSON_FAIL(ec, error::out_of_range);
384
0
        return ec;
385
0
    }
386
0
    return (*t_)[pos];
387
0
}
388
389
system::result<value const&>
390
array::try_at(std::size_t pos) const noexcept
391
0
{
392
0
    if(pos >= t_->size)
393
0
    {
394
0
        system::error_code ec;
395
0
        BOOST_JSON_FAIL(ec, error::out_of_range);
396
0
        return ec;
397
0
    }
398
0
    return (*t_)[pos];
399
0
}
400
401
value const&
402
array::
403
array::at(std::size_t pos, source_location const& loc) const&
404
0
{
405
0
    return try_at(pos).value(loc);
406
0
}
407
408
//----------------------------------------------------------
409
//
410
// Capacity
411
//
412
//----------------------------------------------------------
413
414
void
415
array::
416
shrink_to_fit() noexcept
417
0
{
418
0
    if(capacity() <= size())
419
0
        return;
420
0
    if(size() == 0)
421
0
    {
422
0
        table::deallocate(t_, sp_);
423
0
        t_ = &empty_;
424
0
        return;
425
0
    }
426
427
0
#ifndef BOOST_NO_EXCEPTIONS
428
0
    try
429
0
    {
430
0
#endif
431
0
        auto t = table::allocate(
432
0
            size(), sp_);
433
0
        relocate(
434
0
            &(*t)[0],
435
0
            data(),
436
0
            size());
437
0
        t->size = static_cast<
438
0
            std::uint32_t>(size());
439
0
        t = detail::exchange(
440
0
            t_, t);
441
0
        table::deallocate(t, sp_);
442
0
#ifndef BOOST_NO_EXCEPTIONS
443
0
    }
444
0
    catch(...)
445
0
    {
446
        // eat the exception
447
0
        return;
448
0
    }
449
0
#endif
450
0
}
451
452
//----------------------------------------------------------
453
//
454
// Modifiers
455
//
456
//----------------------------------------------------------
457
458
void
459
array::
460
clear() noexcept
461
0
{
462
0
    if(size() == 0)
463
0
        return;
464
0
    destroy(
465
0
        begin(), end());
466
0
    t_->size = 0;
467
0
}
468
469
auto
470
array::
471
insert(
472
    const_iterator pos,
473
    value const& v) ->
474
        iterator
475
0
{
476
0
    return emplace(pos, v);
477
0
}
478
479
auto
480
array::
481
insert(
482
    const_iterator pos,
483
    value&& v) ->
484
        iterator
485
0
{
486
0
    return emplace(pos, std::move(v));
487
0
}
488
489
auto
490
array::
491
insert(
492
    const_iterator pos,
493
    std::size_t count,
494
    value const& v) ->
495
        iterator
496
0
{
497
    // v may refer to an element of this array, whose
498
    // storage revert_insert can relocate and free, so
499
    // copy it before inserting
500
0
    value const tmp(v, sp_);
501
0
    revert_insert r(
502
0
        pos, count, *this);
503
0
    while(count--)
504
0
    {
505
0
        ::new(r.p) value(tmp, sp_);
506
0
        ++r.p;
507
0
    }
508
0
    return r.commit();
509
0
}
510
511
auto
512
array::
513
insert(
514
    const_iterator pos,
515
    std::initializer_list<
516
        value_ref> init) ->
517
    iterator
518
0
{
519
    // the value_refs in init may point into this
520
    // array, whose storage revert_insert can
521
    // relocate and free, so buffer them first
522
0
    array temp(init, sp_);
523
0
    revert_insert r(
524
0
        pos, temp.size(), *this);
525
0
    relocate(
526
0
        r.p,
527
0
        temp.data(),
528
0
        temp.size());
529
0
    temp.t_->size = 0;
530
0
    return r.commit();
531
0
}
532
533
auto
534
array::
535
erase(
536
    const_iterator pos) noexcept ->
537
    iterator
538
0
{
539
0
    BOOST_ASSERT(
540
0
        pos >= begin() &&
541
0
        pos <= end());
542
0
    return erase(pos, pos + 1);
543
0
}
544
545
auto
546
array::
547
erase(
548
    const_iterator first,
549
    const_iterator last) noexcept ->
550
        iterator
551
0
{
552
0
    BOOST_ASSERT(
553
0
        first >= begin() &&
554
0
        last >= first &&
555
0
        last <= end());
556
0
    std::size_t const n =
557
0
        last - first;
558
0
    auto const p = &(*t_)[0] +
559
0
        (first - &(*t_)[0]);
560
0
    destroy(p, p + n);
561
0
    relocate(p, p + n,
562
0
        t_->size - (last -
563
0
            &(*t_)[0]));
564
0
    t_->size = static_cast<
565
0
        std::uint32_t>(t_->size - n);
566
0
    return p;
567
0
}
568
569
void
570
array::
571
push_back(value const& v)
572
0
{
573
0
    emplace_back(v);
574
0
}
575
576
void
577
array::
578
push_back(value&& v)
579
0
{
580
0
    emplace_back(std::move(v));
581
0
}
582
583
void
584
array::
585
pop_back() noexcept
586
0
{
587
0
    auto const p = &back();
588
0
    destroy(p, p + 1);
589
0
    --t_->size;
590
0
}
591
592
void
593
array::
594
resize(std::size_t count)
595
0
{
596
0
    if(count <= t_->size)
597
0
    {
598
        // shrink
599
0
        destroy(
600
0
            &(*t_)[0] + count,
601
0
            &(*t_)[0] + t_->size);
602
0
        t_->size = static_cast<
603
0
            std::uint32_t>(count);
604
0
        return;
605
0
    }
606
607
0
    reserve(count);
608
0
    auto p = &(*t_)[t_->size];
609
0
    auto const end = &(*t_)[count];
610
0
    while(p != end)
611
0
        ::new(p++) value(sp_);
612
0
    t_->size = static_cast<
613
0
        std::uint32_t>(count);
614
0
}
615
616
void
617
array::
618
resize(
619
    std::size_t count,
620
    value const& v)
621
0
{
622
0
    if(count <= size())
623
0
    {
624
        // shrink
625
0
        destroy(
626
0
            data() + count,
627
0
            data() + size());
628
0
        t_->size = static_cast<
629
0
            std::uint32_t>(count);
630
0
        return;
631
0
    }
632
0
    count -= size();
633
    // v may refer to an element of this array, whose
634
    // storage revert_insert can relocate and free, so
635
    // copy it before inserting
636
0
    value const tmp(v, sp_);
637
0
    revert_insert r(
638
0
        end(), count, *this);
639
0
    while(count--)
640
0
    {
641
0
        ::new(r.p) value(tmp, sp_);
642
0
        ++r.p;
643
0
    }
644
0
    r.commit();
645
0
}
646
647
void
648
array::
649
swap(array& other)
650
0
{
651
0
    if(*sp_ == *other.sp_)
652
0
    {
653
0
        t_ = detail::exchange(
654
0
            other.t_, t_);
655
0
        return;
656
0
    }
657
0
    array temp1(
658
0
        std::move(*this),
659
0
        other.storage());
660
0
    array temp2(
661
0
        std::move(other),
662
0
        this->storage());
663
0
    this->~array();
664
0
    ::new(this) array(
665
0
        pilfer(temp2));
666
0
    other.~array();
667
0
    ::new(&other) array(
668
0
        pilfer(temp1));
669
0
}
670
671
//----------------------------------------------------------
672
//
673
// Private
674
//
675
//----------------------------------------------------------
676
677
std::size_t
678
array::
679
growth(
680
    std::size_t new_size) const
681
0
{
682
0
    if(new_size > max_size())
683
0
    {
684
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
685
0
        detail::throw_system_error( error::array_too_large, &loc );
686
0
    }
687
0
    std::size_t const old = capacity();
688
0
    if(old > max_size() - old / 2)
689
0
        return new_size;
690
0
    std::size_t const g =
691
0
        old + old / 2; // 1.5x
692
0
    if(g < new_size)
693
0
        return new_size;
694
0
    return g;
695
0
}
696
697
// precondition: new_capacity > capacity()
698
void
699
array::
700
reserve_impl(
701
    std::size_t new_capacity)
702
0
{
703
0
    BOOST_ASSERT(
704
0
        new_capacity > t_->capacity);
705
0
    auto t = table::allocate(
706
0
        growth(new_capacity), sp_);
707
0
    relocate(
708
0
        &(*t)[0],
709
0
        &(*t_)[0],
710
0
        t_->size);
711
0
    t->size = t_->size;
712
0
    t = detail::exchange(t_, t);
713
0
    table::deallocate(t, sp_);
714
0
}
715
716
// precondition: pv is not aliased
717
value&
718
array::
719
push_back(
720
    pilfered<value> pv)
721
0
{
722
0
    auto const n = t_->size;
723
0
    if(n < t_->capacity)
724
0
    {
725
        // fast path
726
0
        auto& v = *::new(
727
0
            &(*t_)[n]) value(pv);
728
0
        ++t_->size;
729
0
        return v;
730
0
    }
731
0
    auto const t =
732
0
        detail::exchange(t_,
733
0
            table::allocate(
734
0
                growth(n + 1),
735
0
                    sp_));
736
0
    auto& v = *::new(
737
0
        &(*t_)[n]) value(pv);
738
0
    relocate(
739
0
        &(*t_)[0],
740
0
        &(*t)[0],
741
0
        n);
742
0
    t_->size = n + 1;
743
0
    table::deallocate(t, sp_);
744
0
    return v;
745
0
}
746
747
// precondition: pv is not aliased
748
auto
749
array::
750
insert(
751
    const_iterator pos,
752
    pilfered<value> pv) ->
753
        iterator
754
0
{
755
0
    BOOST_ASSERT(
756
0
        pos >= begin() &&
757
0
        pos <= end());
758
0
    std::size_t const n =
759
0
        t_->size;
760
0
    std::size_t const i =
761
0
        pos - &(*t_)[0];
762
0
    if(n < t_->capacity)
763
0
    {
764
        // fast path
765
0
        auto const p =
766
0
            &(*t_)[i];
767
0
        relocate(
768
0
            p + 1,
769
0
            p,
770
0
            n - i);
771
0
        ::new(p) value(pv);
772
0
        ++t_->size;
773
0
        return p;
774
0
    }
775
0
    auto t =
776
0
        table::allocate(
777
0
            growth(n + 1), sp_);
778
0
    auto const p = &(*t)[i];
779
0
    ::new(p) value(pv);
780
0
    relocate(
781
0
        &(*t)[0],
782
0
        &(*t_)[0],
783
0
        i);
784
0
    relocate(
785
0
        p + 1,
786
0
        &(*t_)[i],
787
0
        n - i);
788
0
    t->size = static_cast<
789
0
        std::uint32_t>(size() + 1);
790
0
    t = detail::exchange(t_, t);
791
0
    table::deallocate(t, sp_);
792
0
    return p;
793
0
}
794
795
//----------------------------------------------------------
796
797
bool
798
array::
799
equal(
800
    array const& other) const noexcept
801
0
{
802
0
    if(size() != other.size())
803
0
        return false;
804
0
    for(std::size_t i = 0; i < size(); ++i)
805
0
        if((*this)[i] != other[i])
806
0
            return false;
807
0
    return true;
808
0
}
809
810
} // namespace json
811
} // namespace boost
812
813
//----------------------------------------------------------
814
//
815
// std::hash specialization
816
//
817
//----------------------------------------------------------
818
819
std::size_t
820
std::hash<::boost::json::array>::operator()(
821
    ::boost::json::array const& ja) const noexcept
822
0
{
823
0
    return ::boost::hash< ::boost::json::array >()( ja );
824
0
}
825
826
//----------------------------------------------------------
827
828
#endif