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 2603 : array::
36 : table::
37 : allocate(
38 : std::size_t capacity,
39 : storage_ptr const& sp) ->
40 : table*
41 : {
42 2603 : BOOST_ASSERT(capacity > 0);
43 2603 : 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 2601 : table*>(sp->allocate(
50 : sizeof(table) +
51 2601 : capacity * sizeof(value),
52 : alignof(value)));
53 2462 : p->capacity = static_cast<
54 : std::uint32_t>(capacity);
55 2462 : return p;
56 : }
57 :
58 : void
59 4478 : array::
60 : table::
61 : deallocate(
62 : table* p,
63 : storage_ptr const& sp)
64 : {
65 4478 : if(p->capacity == 0)
66 2020 : return;
67 2458 : sp->deallocate(p,
68 : sizeof(table) +
69 2458 : p->capacity * sizeof(value),
70 : alignof(value));
71 : }
72 :
73 : //----------------------------------------------------------
74 :
75 37 : array::
76 : revert_insert::
77 : revert_insert(
78 : const_iterator pos,
79 : std::size_t n,
80 37 : array& arr)
81 37 : : arr_(&arr)
82 37 : , i_(pos - arr_->data())
83 37 : , n_(n)
84 : {
85 37 : BOOST_ASSERT(
86 : pos >= arr_->begin() &&
87 : pos <= arr_->end());
88 74 : if( n_ <= arr_->capacity() -
89 37 : 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 35 : 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 34 : auto t = table::allocate(
110 34 : arr_->growth(arr_->size() + n_),
111 34 : arr_->sp_);
112 24 : t->size = static_cast<std::uint32_t>(
113 24 : arr_->size() + n_);
114 24 : p = &(*t)[0] + i_;
115 24 : relocate(
116 24 : &(*t)[0],
117 24 : arr_->data(),
118 24 : i_);
119 24 : relocate(
120 24 : &(*t)[i_ + n_],
121 24 : arr_->data() + i_,
122 24 : arr_->size() - i_);
123 24 : t = detail::exchange(arr_->t_, t);
124 24 : table::deallocate(t, arr_->sp_);
125 : }
126 :
127 26 : array::
128 : revert_insert::
129 8 : ~revert_insert()
130 : {
131 26 : if(! arr_)
132 18 : return;
133 8 : BOOST_ASSERT(n_ != 0);
134 : auto const pos =
135 8 : arr_->data() + i_;
136 8 : arr_->destroy(pos, p);
137 8 : arr_->t_->size = static_cast<
138 : std::uint32_t>(
139 8 : arr_->t_->size - n_);
140 8 : relocate(
141 : pos,
142 8 : pos + n_,
143 8 : arr_->size() - i_);
144 26 : }
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 52 : while(last-- != first)
156 27 : last->~value();
157 : }
158 :
159 : void
160 3745 : array::
161 : destroy() noexcept
162 : {
163 3745 : if(sp_.is_not_shared_and_deallocate_is_trivial())
164 5 : return;
165 3740 : auto last = end();
166 3740 : auto const first = begin();
167 20962 : while(last-- != first)
168 17222 : last->~value();
169 3740 : table::deallocate(t_, sp_);
170 : }
171 :
172 : //----------------------------------------------------------
173 : //
174 : // Special Members
175 : //
176 : //----------------------------------------------------------
177 :
178 2150 : array::
179 2150 : array(detail::unchecked_array&& ua)
180 2150 : : sp_(ua.storage())
181 : {
182 : BOOST_CORE_STATIC_ASSERT( alignof(table) == alignof(value) );
183 2150 : if(ua.size() == 0)
184 : {
185 819 : t_ = &empty_;
186 819 : return;
187 : }
188 1331 : t_= table::allocate(
189 1331 : ua.size(), sp_);
190 1293 : t_->size = static_cast<
191 1293 : std::uint32_t>(ua.size());
192 1293 : ua.relocate(data());
193 38 : }
194 :
195 3681 : array::
196 : ~array() noexcept
197 : {
198 3681 : destroy();
199 3681 : }
200 :
201 35 : array::
202 : array(
203 : std::size_t count,
204 : value const& v,
205 35 : storage_ptr sp)
206 35 : : sp_(std::move(sp))
207 : {
208 35 : if(count == 0)
209 : {
210 1 : t_ = &empty_;
211 1 : return;
212 : }
213 63 : t_= table::allocate(
214 34 : count, sp_);
215 29 : t_->size = 0;
216 29 : revert_construct r(*this);
217 98 : while(count--)
218 : {
219 101 : ::new(end()) value(v, sp_);
220 69 : ++t_->size;
221 : }
222 13 : r.commit();
223 50 : }
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 186 : array::
255 : array(
256 : array const& other,
257 186 : storage_ptr sp)
258 186 : : sp_(std::move(sp))
259 : {
260 186 : if(other.empty())
261 : {
262 14 : t_ = &empty_;
263 14 : return;
264 : }
265 172 : t_ = table::allocate(
266 172 : other.size(), sp_);
267 153 : t_->size = 0;
268 153 : revert_construct r(*this);
269 153 : auto src = other.data();
270 153 : auto dest = data();
271 153 : auto const n = other.size();
272 : do
273 : {
274 13 : ::new(dest++) value(
275 2462 : *src++, sp_);
276 2423 : ++t_->size;
277 : }
278 2423 : while(t_->size < n);
279 140 : r.commit();
280 185 : }
281 :
282 262 : array::
283 : array(
284 : array&& other,
285 262 : storage_ptr sp)
286 262 : : sp_(std::move(sp))
287 : {
288 262 : if(*sp_ == *other.sp_)
289 : {
290 : // same resource
291 478 : t_ = detail::exchange(
292 239 : other.t_, &empty_);
293 243 : 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 244 : array::
319 : array(
320 : std::initializer_list<
321 : value_ref> init,
322 244 : storage_ptr sp)
323 244 : : sp_(std::move(sp))
324 : {
325 244 : if(init.size() == 0)
326 : {
327 5 : t_ = &empty_;
328 5 : return;
329 : }
330 239 : t_ = table::allocate(
331 239 : init.size(), sp_);
332 212 : t_->size = 0;
333 212 : revert_construct r(*this);
334 212 : value_ref::write_array(
335 212 : data(), init, sp_);
336 197 : t_->size = static_cast<
337 197 : std::uint32_t>(init.size());
338 197 : r.commit();
339 254 : }
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 10 : array::
491 : insert(
492 : const_iterator pos,
493 : std::size_t count,
494 : value const& v) ->
495 : iterator
496 : {
497 : revert_insert r(
498 10 : pos, count, *this);
499 17 : while(count--)
500 : {
501 16 : ::new(r.p) value(v, sp_);
502 10 : ++r.p;
503 : }
504 8 : return r.commit();
505 7 : }
506 :
507 : auto
508 3 : array::
509 : insert(
510 : const_iterator pos,
511 : std::initializer_list<
512 : value_ref> init) ->
513 : iterator
514 : {
515 : revert_insert r(
516 3 : pos, init.size(), *this);
517 2 : value_ref::write_array(
518 2 : r.p, init, sp_);
519 2 : return r.commit();
520 2 : }
521 :
522 : auto
523 8 : array::
524 : erase(
525 : const_iterator pos) noexcept ->
526 : iterator
527 : {
528 8 : BOOST_ASSERT(
529 : pos >= begin() &&
530 : pos <= end());
531 8 : return erase(pos, pos + 1);
532 : }
533 :
534 : auto
535 9 : array::
536 : erase(
537 : const_iterator first,
538 : const_iterator last) noexcept ->
539 : iterator
540 : {
541 9 : BOOST_ASSERT(
542 : first >= begin() &&
543 : last >= first &&
544 : last <= end());
545 9 : std::size_t const n =
546 9 : last - first;
547 9 : auto const p = &(*t_)[0] +
548 9 : (first - &(*t_)[0]);
549 9 : destroy(p, p + n);
550 9 : relocate(p, p + n,
551 9 : t_->size - (last -
552 9 : &(*t_)[0]));
553 9 : t_->size = static_cast<
554 9 : std::uint32_t>(t_->size - n);
555 9 : return p;
556 : }
557 :
558 : void
559 4 : array::
560 : push_back(value const& v)
561 : {
562 4 : emplace_back(v);
563 2 : }
564 :
565 : void
566 9 : array::
567 : push_back(value&& v)
568 : {
569 9 : emplace_back(std::move(v));
570 7 : }
571 :
572 : void
573 3 : array::
574 : pop_back() noexcept
575 : {
576 3 : auto const p = &back();
577 3 : destroy(p, p + 1);
578 3 : --t_->size;
579 3 : }
580 :
581 : void
582 15 : array::
583 : resize(std::size_t count)
584 : {
585 15 : if(count <= t_->size)
586 : {
587 : // shrink
588 4 : destroy(
589 2 : &(*t_)[0] + count,
590 2 : &(*t_)[0] + t_->size);
591 2 : t_->size = static_cast<
592 : std::uint32_t>(count);
593 2 : return;
594 : }
595 :
596 13 : reserve(count);
597 12 : auto p = &(*t_)[t_->size];
598 12 : auto const end = &(*t_)[count];
599 32 : while(p != end)
600 20 : ::new(p++) value(sp_);
601 12 : t_->size = static_cast<
602 : std::uint32_t>(count);
603 : }
604 :
605 : void
606 7 : array::
607 : resize(
608 : std::size_t count,
609 : value const& v)
610 : {
611 7 : if(count <= size())
612 : {
613 : // shrink
614 2 : destroy(
615 1 : data() + count,
616 1 : data() + size());
617 1 : t_->size = static_cast<
618 : std::uint32_t>(count);
619 1 : return;
620 : }
621 6 : count -= size();
622 : revert_insert r(
623 6 : end(), count, *this);
624 9 : while(count--)
625 : {
626 12 : ::new(r.p) value(v, sp_);
627 4 : ++r.p;
628 : }
629 1 : r.commit();
630 5 : }
631 :
632 : void
633 28 : array::
634 : swap(array& other)
635 : {
636 28 : if(*sp_ == *other.sp_)
637 : {
638 48 : t_ = detail::exchange(
639 24 : other.t_, t_);
640 24 : return;
641 : }
642 : array temp1(
643 4 : std::move(*this),
644 9 : other.storage());
645 : array temp2(
646 3 : std::move(other),
647 7 : this->storage());
648 2 : this->~array();
649 6 : ::new(this) array(
650 2 : pilfer(temp2));
651 2 : other.~array();
652 6 : ::new(&other) array(
653 2 : pilfer(temp1));
654 3 : }
655 :
656 : //----------------------------------------------------------
657 : //
658 : // Private
659 : //
660 : //----------------------------------------------------------
661 :
662 : std::size_t
663 776 : array::
664 : growth(
665 : std::size_t new_size) const
666 : {
667 776 : if(new_size > max_size())
668 : {
669 : BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
670 1 : detail::throw_system_error( error::array_too_large, &loc );
671 : }
672 775 : std::size_t const old = capacity();
673 775 : if(old > max_size() - old / 2)
674 1 : return new_size;
675 774 : std::size_t const g =
676 774 : old + old / 2; // 1.5x
677 774 : if(g < new_size)
678 694 : return new_size;
679 80 : return g;
680 : }
681 :
682 : // precondition: new_capacity > capacity()
683 : void
684 664 : array::
685 : reserve_impl(
686 : std::size_t new_capacity)
687 : {
688 664 : BOOST_ASSERT(
689 : new_capacity > t_->capacity);
690 663 : auto t = table::allocate(
691 664 : growth(new_capacity), sp_);
692 643 : relocate(
693 643 : &(*t)[0],
694 643 : &(*t_)[0],
695 643 : t_->size);
696 643 : t->size = t_->size;
697 643 : t = detail::exchange(t_, t);
698 643 : table::deallocate(t, sp_);
699 643 : }
700 :
701 : // precondition: pv is not aliased
702 : value&
703 7572 : array::
704 : push_back(
705 : pilfered<value> pv)
706 : {
707 7572 : auto const n = t_->size;
708 7572 : if(n < t_->capacity)
709 : {
710 : // fast path
711 : auto& v = *::new(
712 7505 : &(*t_)[n]) value(pv);
713 7505 : ++t_->size;
714 7505 : return v;
715 : }
716 : auto const t =
717 67 : detail::exchange(t_,
718 : table::allocate(
719 67 : growth(n + 1),
720 67 : sp_));
721 : auto& v = *::new(
722 62 : &(*t_)[n]) value(pv);
723 62 : relocate(
724 62 : &(*t_)[0],
725 62 : &(*t)[0],
726 : n);
727 62 : t_->size = n + 1;
728 62 : table::deallocate(t, sp_);
729 62 : return v;
730 : }
731 :
732 : // precondition: pv is not aliased
733 : auto
734 12 : array::
735 : insert(
736 : const_iterator pos,
737 : pilfered<value> pv) ->
738 : iterator
739 : {
740 12 : BOOST_ASSERT(
741 : pos >= begin() &&
742 : pos <= end());
743 12 : std::size_t const n =
744 12 : t_->size;
745 : std::size_t const i =
746 12 : pos - &(*t_)[0];
747 12 : if(n < t_->capacity)
748 : {
749 : // fast path
750 : auto const p =
751 1 : &(*t_)[i];
752 1 : relocate(
753 : p + 1,
754 : p,
755 : n - i);
756 1 : ::new(p) value(pv);
757 1 : ++t_->size;
758 1 : return p;
759 : }
760 : auto t =
761 11 : table::allocate(
762 11 : growth(n + 1), sp_);
763 6 : auto const p = &(*t)[i];
764 6 : ::new(p) value(pv);
765 6 : relocate(
766 6 : &(*t)[0],
767 6 : &(*t_)[0],
768 : i);
769 6 : relocate(
770 : p + 1,
771 6 : &(*t_)[i],
772 : n - i);
773 6 : t->size = static_cast<
774 6 : std::uint32_t>(size() + 1);
775 6 : t = detail::exchange(t_, t);
776 6 : table::deallocate(t, sp_);
777 6 : return p;
778 : }
779 :
780 : //----------------------------------------------------------
781 :
782 : bool
783 96 : array::
784 : equal(
785 : array const& other) const noexcept
786 : {
787 96 : if(size() != other.size())
788 2 : return false;
789 3282 : for(std::size_t i = 0; i < size(); ++i)
790 3194 : if((*this)[i] != other[i])
791 6 : return false;
792 88 : return true;
793 : }
794 :
795 : } // namespace json
796 : } // namespace boost
797 :
798 : //----------------------------------------------------------
799 : //
800 : // std::hash specialization
801 : //
802 : //----------------------------------------------------------
803 :
804 : std::size_t
805 12 : std::hash<::boost::json::array>::operator()(
806 : ::boost::json::array const& ja) const noexcept
807 : {
808 12 : return ::boost::hash< ::boost::json::array >()( ja );
809 : }
810 :
811 : //----------------------------------------------------------
812 :
813 : #endif
|