Coverage Report

Created: 2025-06-13 06:26

/src/boost/boost/json/impl/array.ipp
Line
Count
Source (jump to first uncovered line)
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/container_hash/hash.hpp>
14
#include <boost/json/array.hpp>
15
#include <boost/json/pilfer.hpp>
16
#include <boost/json/detail/except.hpp>
17
#include <cstdlib>
18
#include <limits>
19
#include <new>
20
#include <utility>
21
22
namespace boost {
23
namespace json {
24
25
//----------------------------------------------------------
26
27
constexpr array::table::table() = default;
28
29
// empty arrays point here
30
BOOST_JSON_REQUIRE_CONST_INIT
31
array::table array::empty_;
32
33
auto
34
array::
35
table::
36
allocate(
37
    std::size_t capacity,
38
    storage_ptr const& sp) ->
39
        table*
40
19.2k
{
41
19.2k
    BOOST_ASSERT(capacity > 0);
42
19.2k
    if(capacity > array::max_size())
43
0
    {
44
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
45
0
        detail::throw_system_error( error::array_too_large, &loc );
46
0
    }
47
19.2k
    auto p = reinterpret_cast<
48
19.2k
        table*>(sp->allocate(
49
19.2k
            sizeof(table) +
50
19.2k
                capacity * sizeof(value),
51
19.2k
            alignof(value)));
52
19.2k
    p->capacity = static_cast<
53
19.2k
        std::uint32_t>(capacity);
54
19.2k
    return p;
55
19.2k
}
56
57
void
58
array::
59
table::
60
deallocate(
61
    table* p,
62
    storage_ptr const& sp)
63
14.9k
{
64
14.9k
    if(p->capacity == 0)
65
1.01k
        return;
66
13.8k
    sp->deallocate(p,
67
13.8k
        sizeof(table) +
68
13.8k
            p->capacity * sizeof(value),
69
13.8k
        alignof(value));
70
13.8k
}
71
72
//----------------------------------------------------------
73
74
array::
75
revert_insert::
76
revert_insert(
77
    const_iterator pos,
78
    std::size_t n,
79
    array& arr)
80
0
    : arr_(&arr)
81
0
    , i_(pos - arr_->data())
82
0
    , n_(n)
83
0
{
84
0
    BOOST_ASSERT(
85
0
        pos >= arr_->begin() &&
86
0
        pos <= arr_->end());
87
0
    if( n_ <= arr_->capacity() -
88
0
        arr_->size())
89
0
    {
90
        // fast path
91
0
        p = arr_->data() + i_;
92
0
        if(n_ == 0)
93
0
            return;
94
0
        relocate(
95
0
            p + n_,
96
0
            p,
97
0
            arr_->size() - i_);
98
0
        arr_->t_->size = static_cast<
99
0
            std::uint32_t>(
100
0
                arr_->t_->size + n_);
101
0
        return;
102
0
    }
103
0
    if(n_ > max_size() - arr_->size())
104
0
    {
105
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
106
0
        detail::throw_system_error( error::array_too_large, &loc );
107
0
    }
108
0
    auto t = table::allocate(
109
0
        arr_->growth(arr_->size() + n_),
110
0
            arr_->sp_);
111
0
    t->size = static_cast<std::uint32_t>(
112
0
        arr_->size() + n_);
113
0
    p = &(*t)[0] + i_;
114
0
    relocate(
115
0
        &(*t)[0],
116
0
        arr_->data(),
117
0
        i_);
118
0
    relocate(
119
0
        &(*t)[i_ + n_],
120
0
        arr_->data() + i_,
121
0
        arr_->size() - i_);
122
0
    t = detail::exchange(arr_->t_, t);
123
0
    table::deallocate(t, arr_->sp_);
124
0
}
125
126
array::
127
revert_insert::
128
~revert_insert()
129
0
{
130
0
    if(! arr_)
131
0
        return;
132
0
    BOOST_ASSERT(n_ != 0);
133
0
    auto const pos =
134
0
        arr_->data() + i_;
135
0
    arr_->destroy(pos, p);
136
0
    arr_->t_->size = static_cast<
137
0
        std::uint32_t>(
138
0
            arr_->t_->size - n_);
139
0
    relocate(
140
0
        pos,
141
0
        pos + n_,
142
0
        arr_->size() - i_);
143
0
}
144
145
//----------------------------------------------------------
146
147
void
148
array::
149
destroy(
150
    value* first, value* last) noexcept
151
0
{
152
0
    if(sp_.is_not_shared_and_deallocate_is_trivial())
153
0
        return;
154
0
    while(last-- != first)
155
0
        last->~value();
156
0
}
157
158
void
159
array::
160
destroy() noexcept
161
15.6k
{
162
15.6k
    if(sp_.is_not_shared_and_deallocate_is_trivial())
163
687
        return;
164
14.9k
    auto last = end();
165
14.9k
    auto const first = begin();
166
3.25M
    while(last-- != first)
167
3.24M
        last->~value();
168
14.9k
    table::deallocate(t_, sp_);
169
14.9k
}
170
171
//----------------------------------------------------------
172
//
173
// Special Members
174
//
175
//----------------------------------------------------------
176
177
array::
178
array(detail::unchecked_array&& ua)
179
21.6k
    : sp_(ua.storage())
180
21.6k
{
181
21.6k
    BOOST_STATIC_ASSERT(
182
21.6k
        alignof(table) == alignof(value));
183
21.6k
    if(ua.size() == 0)
184
2.32k
    {
185
2.32k
        t_ = &empty_;
186
2.32k
        return;
187
2.32k
    }
188
19.2k
    t_= table::allocate(
189
19.2k
        ua.size(), sp_);
190
19.2k
    t_->size = static_cast<
191
19.2k
        std::uint32_t>(ua.size());
192
19.2k
    ua.relocate(data());
193
19.2k
}
194
195
array::
196
~array() noexcept
197
15.6k
{
198
15.6k
    destroy();
199
15.6k
}
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
0
    revert_insert r(
498
0
        pos, count, *this);
499
0
    while(count--)
500
0
    {
501
0
        ::new(r.p) value(v, sp_);
502
0
        ++r.p;
503
0
    }
504
0
    return r.commit();
505
0
}
506
507
auto
508
array::
509
insert(
510
    const_iterator pos,
511
    std::initializer_list<
512
        value_ref> init) ->
513
    iterator
514
0
{
515
0
    revert_insert r(
516
0
        pos, init.size(), *this);
517
0
    value_ref::write_array(
518
0
        r.p, init, sp_);
519
0
    return r.commit();
520
0
}
521
522
auto
523
array::
524
erase(
525
    const_iterator pos) noexcept ->
526
    iterator
527
0
{
528
0
    BOOST_ASSERT(
529
0
        pos >= begin() &&
530
0
        pos <= end());
531
0
    return erase(pos, pos + 1);
532
0
}
533
534
auto
535
array::
536
erase(
537
    const_iterator first,
538
    const_iterator last) noexcept ->
539
        iterator
540
0
{
541
0
    BOOST_ASSERT(
542
0
        first >= begin() &&
543
0
        last >= first &&
544
0
        last <= end());
545
0
    std::size_t const n =
546
0
        last - first;
547
0
    auto const p = &(*t_)[0] +
548
0
        (first - &(*t_)[0]);
549
0
    destroy(p, p + n);
550
0
    relocate(p, p + n,
551
0
        t_->size - (last -
552
0
            &(*t_)[0]));
553
0
    t_->size = static_cast<
554
0
        std::uint32_t>(t_->size - n);
555
0
    return p;
556
0
}
557
558
void
559
array::
560
push_back(value const& v)
561
0
{
562
0
    emplace_back(v);
563
0
}
564
565
void
566
array::
567
push_back(value&& v)
568
0
{
569
0
    emplace_back(std::move(v));
570
0
}
571
572
void
573
array::
574
pop_back() noexcept
575
0
{
576
0
    auto const p = &back();
577
0
    destroy(p, p + 1);
578
0
    --t_->size;
579
0
}
580
581
void
582
array::
583
resize(std::size_t count)
584
0
{
585
0
    if(count <= t_->size)
586
0
    {
587
        // shrink
588
0
        destroy(
589
0
            &(*t_)[0] + count,
590
0
            &(*t_)[0] + t_->size);
591
0
        t_->size = static_cast<
592
0
            std::uint32_t>(count);
593
0
        return;
594
0
    }
595
596
0
    reserve(count);
597
0
    auto p = &(*t_)[t_->size];
598
0
    auto const end = &(*t_)[count];
599
0
    while(p != end)
600
0
        ::new(p++) value(sp_);
601
0
    t_->size = static_cast<
602
0
        std::uint32_t>(count);
603
0
}
604
605
void
606
array::
607
resize(
608
    std::size_t count,
609
    value const& v)
610
0
{
611
0
    if(count <= size())
612
0
    {
613
        // shrink
614
0
        destroy(
615
0
            data() + count,
616
0
            data() + size());
617
0
        t_->size = static_cast<
618
0
            std::uint32_t>(count);
619
0
        return;
620
0
    }
621
0
    count -= size();
622
0
    revert_insert r(
623
0
        end(), count, *this);
624
0
    while(count--)
625
0
    {
626
0
        ::new(r.p) value(v, sp_);
627
0
        ++r.p;
628
0
    }
629
0
    r.commit();
630
0
}
631
632
void
633
array::
634
swap(array& other)
635
0
{
636
0
    if(*sp_ == *other.sp_)
637
0
    {
638
0
        t_ = detail::exchange(
639
0
            other.t_, t_);
640
0
        return;
641
0
    }
642
0
    array temp1(
643
0
        std::move(*this),
644
0
        other.storage());
645
0
    array temp2(
646
0
        std::move(other),
647
0
        this->storage());
648
0
    this->~array();
649
0
    ::new(this) array(
650
0
        pilfer(temp2));
651
0
    other.~array();
652
0
    ::new(&other) array(
653
0
        pilfer(temp1));
654
0
}
655
656
//----------------------------------------------------------
657
//
658
// Private
659
//
660
//----------------------------------------------------------
661
662
std::size_t
663
array::
664
growth(
665
    std::size_t new_size) const
666
0
{
667
0
    if(new_size > max_size())
668
0
    {
669
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
670
0
        detail::throw_system_error( error::array_too_large, &loc );
671
0
    }
672
0
    std::size_t const old = capacity();
673
0
    if(old > max_size() - old / 2)
674
0
        return new_size;
675
0
    std::size_t const g =
676
0
        old + old / 2; // 1.5x
677
0
    if(g < new_size)
678
0
        return new_size;
679
0
    return g;
680
0
}
681
682
// precondition: new_capacity > capacity()
683
void
684
array::
685
reserve_impl(
686
    std::size_t new_capacity)
687
0
{
688
0
    BOOST_ASSERT(
689
0
        new_capacity > t_->capacity);
690
0
    auto t = table::allocate(
691
0
        growth(new_capacity), sp_);
692
0
    relocate(
693
0
        &(*t)[0],
694
0
        &(*t_)[0],
695
0
        t_->size);
696
0
    t->size = t_->size;
697
0
    t = detail::exchange(t_, t);
698
0
    table::deallocate(t, sp_);
699
0
}
700
701
// precondition: pv is not aliased
702
value&
703
array::
704
push_back(
705
    pilfered<value> pv)
706
0
{
707
0
    auto const n = t_->size;
708
0
    if(n < t_->capacity)
709
0
    {
710
        // fast path
711
0
        auto& v = *::new(
712
0
            &(*t_)[n]) value(pv);
713
0
        ++t_->size;
714
0
        return v;
715
0
    }
716
0
    auto const t =
717
0
        detail::exchange(t_,
718
0
            table::allocate(
719
0
                growth(n + 1),
720
0
                    sp_));
721
0
    auto& v = *::new(
722
0
        &(*t_)[n]) value(pv);
723
0
    relocate(
724
0
        &(*t_)[0],
725
0
        &(*t)[0],
726
0
        n);
727
0
    t_->size = n + 1;
728
0
    table::deallocate(t, sp_);
729
0
    return v;
730
0
}
731
732
// precondition: pv is not aliased
733
auto
734
array::
735
insert(
736
    const_iterator pos,
737
    pilfered<value> pv) ->
738
        iterator
739
0
{
740
0
    BOOST_ASSERT(
741
0
        pos >= begin() &&
742
0
        pos <= end());
743
0
    std::size_t const n =
744
0
        t_->size;
745
0
    std::size_t const i =
746
0
        pos - &(*t_)[0];
747
0
    if(n < t_->capacity)
748
0
    {
749
        // fast path
750
0
        auto const p =
751
0
            &(*t_)[i];
752
0
        relocate(
753
0
            p + 1,
754
0
            p,
755
0
            n - i);
756
0
        ::new(p) value(pv);
757
0
        ++t_->size;
758
0
        return p;
759
0
    }
760
0
    auto t =
761
0
        table::allocate(
762
0
            growth(n + 1), sp_);
763
0
    auto const p = &(*t)[i];
764
0
    ::new(p) value(pv);
765
0
    relocate(
766
0
        &(*t)[0],
767
0
        &(*t_)[0],
768
0
        i);
769
0
    relocate(
770
0
        p + 1,
771
0
        &(*t_)[i],
772
0
        n - i);
773
0
    t->size = static_cast<
774
0
        std::uint32_t>(size() + 1);
775
0
    t = detail::exchange(t_, t);
776
0
    table::deallocate(t, sp_);
777
0
    return p;
778
0
}
779
780
//----------------------------------------------------------
781
782
bool
783
array::
784
equal(
785
    array const& other) const noexcept
786
0
{
787
0
    if(size() != other.size())
788
0
        return false;
789
0
    for(std::size_t i = 0; i < size(); ++i)
790
0
        if((*this)[i] != other[i])
791
0
            return false;
792
0
    return true;
793
0
}
794
795
} // namespace json
796
} // namespace boost
797
798
//----------------------------------------------------------
799
//
800
// std::hash specialization
801
//
802
//----------------------------------------------------------
803
804
std::size_t
805
std::hash<::boost::json::array>::operator()(
806
    ::boost::json::array const& ja) const noexcept
807
0
{
808
0
    return ::boost::hash< ::boost::json::array >()( ja );
809
0
}
810
811
//----------------------------------------------------------
812
813
#endif