Coverage Report

Created: 2023-06-07 06:25

/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
11.6k
{
41
11.6k
    BOOST_ASSERT(capacity > 0);
42
11.6k
    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
11.6k
    auto p = reinterpret_cast<
48
11.6k
        table*>(sp->allocate(
49
11.6k
            sizeof(table) +
50
11.6k
                capacity * sizeof(value),
51
11.6k
            alignof(value)));
52
11.6k
    p->capacity = static_cast<
53
11.6k
        std::uint32_t>(capacity);
54
11.6k
    return p;
55
11.6k
}
56
57
void
58
array::
59
table::
60
deallocate(
61
    table* p,
62
    storage_ptr const& sp)
63
14.4k
{
64
14.4k
    if(p->capacity == 0)
65
3.99k
        return;
66
10.5k
    sp->deallocate(p,
67
10.5k
        sizeof(table) +
68
10.5k
            p->capacity * sizeof(value),
69
10.5k
        alignof(value));
70
10.5k
}
71
72
//----------------------------------------------------------
73
74
array::
75
revert_insert::
76
revert_insert(
77
    const_iterator pos,
78
    std::size_t n,
79
    array& arr)
80
    : arr_(&arr)
81
    , i_(pos - arr_->data())
82
    , 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
14.8k
{
162
14.8k
    if(sp_.is_not_shared_and_deallocate_is_trivial())
163
392
        return;
164
14.4k
    auto last = end();
165
14.4k
    auto const first = begin();
166
8.49M
    while(last-- != first)
167
8.48M
        last->~value();
168
14.4k
    table::deallocate(t_, sp_);
169
14.4k
}
170
171
//----------------------------------------------------------
172
//
173
// Special Members
174
//
175
//----------------------------------------------------------
176
177
array::
178
array(detail::unchecked_array&& ua)
179
    : sp_(ua.storage())
180
16.2k
{
181
16.2k
    BOOST_STATIC_ASSERT(
182
16.2k
        alignof(table) == alignof(value));
183
16.2k
    if(ua.size() == 0)
184
4.54k
    {
185
4.54k
        t_ = &empty_;
186
4.54k
        return;
187
4.54k
    }
188
11.6k
    t_= table::allocate(
189
11.6k
        ua.size(), sp_);
190
11.6k
    t_->size = static_cast<
191
11.6k
        std::uint32_t>(ua.size());
192
11.6k
    ua.relocate(data());
193
11.6k
}
194
195
array::
196
~array() noexcept
197
14.8k
{
198
14.8k
    destroy();
199
14.8k
}
200
201
array::
202
array(
203
    std::size_t count,
204
    value const& v,
205
    storage_ptr sp)
206
    : 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
    : 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
    : array(other, other.sp_)
251
0
{
252
0
}
253
254
array::
255
array(
256
    array const& other,
257
    storage_ptr sp)
258
    : 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
    : 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
    : 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
// Capacity
374
//
375
//----------------------------------------------------------
376
377
void
378
array::
379
shrink_to_fit() noexcept
380
0
{
381
0
    if(capacity() <= size())
382
0
        return;
383
0
    if(size() == 0)
384
0
    {
385
0
        table::deallocate(t_, sp_);
386
0
        t_ = &empty_;
387
0
        return;
388
0
    }
389
390
0
#ifndef BOOST_NO_EXCEPTIONS
391
0
    try
392
0
    {
393
0
#endif
394
0
        auto t = table::allocate(
395
0
            size(), sp_);
396
0
        relocate(
397
0
            &(*t)[0],
398
0
            data(),
399
0
            size());
400
0
        t->size = static_cast<
401
0
            std::uint32_t>(size());
402
0
        t = detail::exchange(
403
0
            t_, t);
404
0
        table::deallocate(t, sp_);
405
0
#ifndef BOOST_NO_EXCEPTIONS
406
0
    }
407
0
    catch(...)
408
0
    {
409
        // eat the exception
410
0
        return;
411
0
    }
412
0
#endif
413
0
}
414
415
//----------------------------------------------------------
416
//
417
// Modifiers
418
//
419
//----------------------------------------------------------
420
421
void
422
array::
423
clear() noexcept
424
0
{
425
0
    if(size() == 0)
426
0
        return;
427
0
    destroy(
428
0
        begin(), end());
429
0
    t_->size = 0;
430
0
}
431
432
auto
433
array::
434
insert(
435
    const_iterator pos,
436
    value const& v) ->
437
        iterator
438
0
{
439
0
    return emplace(pos, v);
440
0
}
441
442
auto
443
array::
444
insert(
445
    const_iterator pos,
446
    value&& v) ->
447
        iterator
448
0
{
449
0
    return emplace(pos, std::move(v));
450
0
}
451
452
auto
453
array::
454
insert(
455
    const_iterator pos,
456
    std::size_t count,
457
    value const& v) ->
458
        iterator
459
0
{
460
0
    revert_insert r(
461
0
        pos, count, *this);
462
0
    while(count--)
463
0
    {
464
0
        ::new(r.p) value(v, sp_);
465
0
        ++r.p;
466
0
    }
467
0
    return r.commit();
468
0
}
469
470
auto
471
array::
472
insert(
473
    const_iterator pos,
474
    std::initializer_list<
475
        value_ref> init) ->
476
    iterator
477
0
{
478
0
    revert_insert r(
479
0
        pos, init.size(), *this);
480
0
    value_ref::write_array(
481
0
        r.p, init, sp_);
482
0
    return r.commit();
483
0
}
484
485
auto
486
array::
487
erase(
488
    const_iterator pos) noexcept ->
489
    iterator
490
0
{
491
0
    BOOST_ASSERT(
492
0
        pos >= begin() &&
493
0
        pos <= end());
494
0
    return erase(pos, pos + 1);
495
0
}
496
497
auto
498
array::
499
erase(
500
    const_iterator first,
501
    const_iterator last) noexcept ->
502
        iterator
503
0
{
504
0
    BOOST_ASSERT(
505
0
        first >= begin() &&
506
0
        last >= first &&
507
0
        last <= end());
508
0
    std::size_t const n =
509
0
        last - first;
510
0
    auto const p = &(*t_)[0] +
511
0
        (first - &(*t_)[0]);
512
0
    destroy(p, p + n);
513
0
    relocate(p, p + n,
514
0
        t_->size - (last -
515
0
            &(*t_)[0]));
516
0
    t_->size = static_cast<
517
0
        std::uint32_t>(t_->size - n);
518
0
    return p;
519
0
}
520
521
void
522
array::
523
push_back(value const& v)
524
0
{
525
0
    emplace_back(v);
526
0
}
527
528
void
529
array::
530
push_back(value&& v)
531
0
{
532
0
    emplace_back(std::move(v));
533
0
}
534
535
void
536
array::
537
pop_back() noexcept
538
0
{
539
0
    auto const p = &back();
540
0
    destroy(p, p + 1);
541
0
    --t_->size;
542
0
}
543
544
void
545
array::
546
resize(std::size_t count)
547
0
{
548
0
    if(count <= t_->size)
549
0
    {
550
        // shrink
551
0
        destroy(
552
0
            &(*t_)[0] + count,
553
0
            &(*t_)[0] + t_->size);
554
0
        t_->size = static_cast<
555
0
            std::uint32_t>(count);
556
0
        return;
557
0
    }
558
559
0
    reserve(count);
560
0
    auto p = &(*t_)[t_->size];
561
0
    auto const end = &(*t_)[count];
562
0
    while(p != end)
563
0
        ::new(p++) value(sp_);
564
0
    t_->size = static_cast<
565
0
        std::uint32_t>(count);
566
0
}
567
568
void
569
array::
570
resize(
571
    std::size_t count,
572
    value const& v)
573
0
{
574
0
    if(count <= size())
575
0
    {
576
        // shrink
577
0
        destroy(
578
0
            data() + count,
579
0
            data() + size());
580
0
        t_->size = static_cast<
581
0
            std::uint32_t>(count);
582
0
        return;
583
0
    }
584
0
    count -= size();
585
0
    revert_insert r(
586
0
        end(), count, *this);
587
0
    while(count--)
588
0
    {
589
0
        ::new(r.p) value(v, sp_);
590
0
        ++r.p;
591
0
    }
592
0
    r.commit();
593
0
}
594
595
void
596
array::
597
swap(array& other)
598
0
{
599
0
    if(*sp_ == *other.sp_)
600
0
    {
601
0
        t_ = detail::exchange(
602
0
            other.t_, t_);
603
0
        return;
604
0
    }
605
0
    array temp1(
606
0
        std::move(*this),
607
0
        other.storage());
608
0
    array temp2(
609
0
        std::move(other),
610
0
        this->storage());
611
0
    this->~array();
612
0
    ::new(this) array(
613
0
        pilfer(temp2));
614
0
    other.~array();
615
0
    ::new(&other) array(
616
0
        pilfer(temp1));
617
0
}
618
619
//----------------------------------------------------------
620
//
621
// Private
622
//
623
//----------------------------------------------------------
624
625
std::size_t
626
array::
627
growth(
628
    std::size_t new_size) const
629
0
{
630
0
    if(new_size > max_size())
631
0
    {
632
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
633
0
        detail::throw_system_error( error::array_too_large, &loc );
634
0
    }
635
0
    std::size_t const old = capacity();
636
0
    if(old > max_size() - old / 2)
637
0
        return new_size;
638
0
    std::size_t const g =
639
0
        old + old / 2; // 1.5x
640
0
    if(g < new_size)
641
0
        return new_size;
642
0
    return g;
643
0
}
644
645
// precondition: new_capacity > capacity()
646
void
647
array::
648
reserve_impl(
649
    std::size_t new_capacity)
650
0
{
651
0
    BOOST_ASSERT(
652
0
        new_capacity > t_->capacity);
653
0
    auto t = table::allocate(
654
0
        growth(new_capacity), sp_);
655
0
    relocate(
656
0
        &(*t)[0],
657
0
        &(*t_)[0],
658
0
        t_->size);
659
0
    t->size = t_->size;
660
0
    t = detail::exchange(t_, t);
661
0
    table::deallocate(t, sp_);
662
0
}
663
664
// precondition: pv is not aliased
665
value&
666
array::
667
push_back(
668
    pilfered<value> pv)
669
0
{
670
0
    auto const n = t_->size;
671
0
    if(n < t_->capacity)
672
0
    {
673
        // fast path
674
0
        auto& v = *::new(
675
0
            &(*t_)[n]) value(pv);
676
0
        ++t_->size;
677
0
        return v;
678
0
    }
679
0
    auto const t =
680
0
        detail::exchange(t_,
681
0
            table::allocate(
682
0
                growth(n + 1),
683
0
                    sp_));
684
0
    auto& v = *::new(
685
0
        &(*t_)[n]) value(pv);
686
0
    relocate(
687
0
        &(*t_)[0],
688
0
        &(*t)[0],
689
0
        n);
690
0
    t_->size = n + 1;
691
0
    table::deallocate(t, sp_);
692
0
    return v;
693
0
}
694
695
// precondition: pv is not aliased
696
auto
697
array::
698
insert(
699
    const_iterator pos,
700
    pilfered<value> pv) ->
701
        iterator
702
0
{
703
0
    BOOST_ASSERT(
704
0
        pos >= begin() &&
705
0
        pos <= end());
706
0
    std::size_t const n =
707
0
        t_->size;
708
0
    std::size_t const i =
709
0
        pos - &(*t_)[0];
710
0
    if(n < t_->capacity)
711
0
    {
712
        // fast path
713
0
        auto const p =
714
0
            &(*t_)[i];
715
0
        relocate(
716
0
            p + 1,
717
0
            p,
718
0
            n - i);
719
0
        ::new(p) value(pv);
720
0
        ++t_->size;
721
0
        return p;
722
0
    }
723
0
    auto t =
724
0
        table::allocate(
725
0
            growth(n + 1), sp_);
726
0
    auto const p = &(*t)[i];
727
0
    ::new(p) value(pv);
728
0
    relocate(
729
0
        &(*t)[0],
730
0
        &(*t_)[0],
731
0
        i);
732
0
    relocate(
733
0
        p + 1,
734
0
        &(*t_)[i],
735
0
        n - i);
736
0
    t->size = static_cast<
737
0
        std::uint32_t>(size() + 1);
738
0
    t = detail::exchange(t_, t);
739
0
    table::deallocate(t, sp_);
740
0
    return p;
741
0
}
742
743
//----------------------------------------------------------
744
745
bool
746
array::
747
equal(
748
    array const& other) const noexcept
749
0
{
750
0
    if(size() != other.size())
751
0
        return false;
752
0
    for(std::size_t i = 0; i < size(); ++i)
753
0
        if((*this)[i] != other[i])
754
0
            return false;
755
0
    return true;
756
0
}
757
758
} // namespace json
759
} // namespace boost
760
761
//----------------------------------------------------------
762
//
763
// std::hash specialization
764
//
765
//----------------------------------------------------------
766
767
std::size_t
768
std::hash<::boost::json::array>::operator()(
769
    ::boost::json::array const& ja) const noexcept
770
0
{
771
0
    return ::boost::hash< ::boost::json::array >()( ja );
772
0
}
773
774
//----------------------------------------------------------
775
776
#endif