Coverage Report

Created: 2023-03-26 06:57

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