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