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_OBJECT_IPP
11 : #define BOOST_JSON_IMPL_OBJECT_IPP
12 :
13 : #include <boost/container_hash/hash.hpp>
14 : #include <boost/json/object.hpp>
15 : #include <boost/json/detail/digest.hpp>
16 : #include <boost/json/detail/except.hpp>
17 : #include <algorithm>
18 : #include <cmath>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <new>
22 : #include <stdexcept>
23 : #include <type_traits>
24 :
25 : namespace boost {
26 : namespace json {
27 : namespace detail {
28 :
29 : template<class CharRange>
30 : std::pair<key_value_pair*, std::size_t>
31 42770 : find_in_object(
32 : object const& obj,
33 : CharRange key) noexcept
34 : {
35 42770 : BOOST_ASSERT(obj.t_->capacity > 0);
36 42770 : if(obj.t_->is_small())
37 : {
38 41040 : auto it = &(*obj.t_)[0];
39 : auto const last =
40 41040 : &(*obj.t_)[obj.t_->size];
41 75334 : for(;it != last; ++it)
42 35032 : if( key == it->key() )
43 738 : return { it, 0 };
44 40302 : return { nullptr, 0 };
45 : }
46 : std::pair<
47 : key_value_pair*,
48 1730 : std::size_t> result;
49 1730 : BOOST_ASSERT(obj.t_->salt != 0);
50 1730 : result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
51 1730 : auto i = obj.t_->bucket(
52 : result.second);
53 2145 : while(i != object::null_index_)
54 : {
55 737 : auto& v = (*obj.t_)[i];
56 737 : if( key == v.key() )
57 : {
58 322 : result.first = &v;
59 322 : return result;
60 : }
61 415 : i = access::next(v);
62 : }
63 1408 : result.first = nullptr;
64 1408 : return result;
65 : }
66 :
67 :
68 : template
69 : std::pair<key_value_pair*, std::size_t>
70 : find_in_object<string_view>(
71 : object const& obj,
72 : string_view key) noexcept;
73 :
74 : } // namespace detail
75 :
76 : //----------------------------------------------------------
77 :
78 : constexpr object::table::table() = default;
79 :
80 : // empty objects point here
81 : BOOST_JSON_REQUIRE_CONST_INIT
82 : object::table object::empty_;
83 :
84 : std::size_t
85 6854 : object::table::
86 : digest(string_view key) const noexcept
87 : {
88 6854 : BOOST_ASSERT(salt != 0);
89 6854 : return detail::digest(
90 13708 : key.begin(), key.end(), salt);
91 : }
92 :
93 : auto
94 10037 : object::table::
95 : bucket(std::size_t hash) noexcept ->
96 : index_t&
97 : {
98 : return reinterpret_cast<
99 10037 : index_t*>(&(*this)[capacity])[
100 10037 : hash % capacity];
101 : }
102 :
103 : auto
104 6823 : object::table::
105 : bucket(string_view key) noexcept ->
106 : index_t&
107 : {
108 6823 : return bucket(digest(key));
109 : }
110 :
111 : void
112 386 : object::table::
113 : clear() noexcept
114 : {
115 386 : BOOST_ASSERT(! is_small());
116 : // initialize buckets
117 772 : std::memset(
118 : reinterpret_cast<index_t*>(
119 386 : &(*this)[capacity]),
120 : 0xff, // null_index_
121 386 : capacity * sizeof(index_t));
122 386 : }
123 :
124 : object::table*
125 35490 : object::table::
126 : allocate(
127 : std::size_t capacity,
128 : std::uintptr_t salt,
129 : storage_ptr const& sp)
130 : {
131 : BOOST_STATIC_ASSERT(
132 : alignof(key_value_pair) >=
133 : alignof(index_t));
134 35490 : BOOST_ASSERT(capacity > 0);
135 35490 : BOOST_ASSERT(capacity <= max_size());
136 : table* p;
137 35490 : if(capacity <= detail::small_object_size_)
138 : {
139 : p = reinterpret_cast<
140 35093 : table*>(sp->allocate(
141 35093 : sizeof(table) + capacity *
142 : sizeof(key_value_pair)));
143 35002 : p->capacity = static_cast<
144 : std::uint32_t>(capacity);
145 : }
146 : else
147 : {
148 : p = reinterpret_cast<
149 397 : table*>(sp->allocate(
150 397 : sizeof(table) + capacity * (
151 : sizeof(key_value_pair) +
152 : sizeof(index_t))));
153 382 : p->capacity = static_cast<
154 : std::uint32_t>(capacity);
155 382 : p->clear();
156 : }
157 35384 : if(salt)
158 : {
159 477 : p->salt = salt;
160 : }
161 : else
162 : {
163 : // VFALCO This would be better if it
164 : // was random, but maybe this
165 : // is good enough.
166 34907 : p->salt = reinterpret_cast<
167 : std::uintptr_t>(p);
168 : }
169 35384 : return p;
170 : }
171 :
172 : //----------------------------------------------------------
173 :
174 : void
175 374 : object::
176 : revert_construct::
177 : destroy() noexcept
178 : {
179 374 : obj_->destroy();
180 374 : }
181 :
182 : //----------------------------------------------------------
183 :
184 : void
185 230 : object::
186 : revert_insert::
187 : destroy() noexcept
188 : {
189 230 : obj_->destroy(
190 230 : &(*obj_->t_)[size_],
191 230 : obj_->end());
192 230 : }
193 :
194 : //----------------------------------------------------------
195 : //
196 : // Construction
197 : //
198 : //----------------------------------------------------------
199 :
200 34879 : object::
201 34879 : object(detail::unchecked_object&& uo)
202 34879 : : sp_(uo.storage())
203 : {
204 34879 : if(uo.size() == 0)
205 : {
206 1049 : t_ = &empty_;
207 1049 : return;
208 : }
209 : // should already be checked
210 33830 : BOOST_ASSERT(
211 : uo.size() <= max_size());
212 33830 : t_ = table::allocate(
213 33830 : uo.size(), 0, sp_);
214 :
215 : // insert all elements, keeping
216 : // the last of any duplicate keys.
217 33791 : auto dest = begin();
218 33791 : auto src = uo.release();
219 33791 : auto const end = src + 2 * uo.size();
220 33791 : if(t_->is_small())
221 : {
222 33758 : t_->size = 0;
223 70267 : while(src != end)
224 : {
225 36509 : access::construct_key_value_pair(
226 36509 : dest, pilfer(src[0]), pilfer(src[1]));
227 36509 : src += 2;
228 36509 : auto result = detail::find_in_object(*this, dest->key());
229 36509 : if(! result.first)
230 : {
231 36499 : ++dest;
232 36499 : ++t_->size;
233 36499 : continue;
234 : }
235 : // handle duplicate
236 10 : auto& v = *result.first;
237 : // don't bother to check if
238 : // storage deallocate is trivial
239 10 : v.~key_value_pair();
240 : // trivial relocate
241 10 : std::memcpy(
242 : static_cast<void*>(&v),
243 : dest, sizeof(v));
244 : }
245 33758 : return;
246 : }
247 1674 : while(src != end)
248 : {
249 1641 : access::construct_key_value_pair(
250 1641 : dest, pilfer(src[0]), pilfer(src[1]));
251 1641 : src += 2;
252 1641 : auto& head = t_->bucket(dest->key());
253 1641 : auto i = head;
254 : for(;;)
255 : {
256 2330 : if(i == null_index_)
257 : {
258 : // end of bucket
259 1640 : access::next(
260 1640 : *dest) = head;
261 1640 : head = static_cast<index_t>(
262 1640 : dest - begin());
263 1640 : ++dest;
264 1640 : break;
265 : }
266 690 : auto& v = (*t_)[i];
267 690 : if(v.key() != dest->key())
268 : {
269 689 : i = access::next(v);
270 689 : continue;
271 : }
272 :
273 : // handle duplicate
274 1 : access::next(*dest) =
275 1 : access::next(v);
276 : // don't bother to check if
277 : // storage deallocate is trivial
278 1 : v.~key_value_pair();
279 : // trivial relocate
280 1 : std::memcpy(
281 : static_cast<void*>(&v),
282 : dest, sizeof(v));
283 1 : break;
284 689 : }
285 : }
286 33 : t_->size = static_cast<
287 33 : index_t>(dest - begin());
288 39 : }
289 :
290 35979 : object::
291 34419 : ~object() noexcept
292 : {
293 35979 : if(sp_.is_not_shared_and_deallocate_is_trivial())
294 5 : return;
295 35974 : if(t_->capacity == 0)
296 1555 : return;
297 34419 : destroy();
298 35979 : }
299 :
300 7 : object::
301 : object(
302 : std::size_t min_capacity,
303 7 : storage_ptr sp)
304 7 : : sp_(std::move(sp))
305 7 : , t_(&empty_)
306 : {
307 7 : reserve(min_capacity);
308 7 : }
309 :
310 79 : object::
311 79 : object(object&& other) noexcept
312 79 : : sp_(other.sp_)
313 158 : , t_(detail::exchange(
314 79 : other.t_, &empty_))
315 : {
316 79 : }
317 :
318 186 : object::
319 : object(
320 : object&& other,
321 186 : storage_ptr sp)
322 186 : : sp_(std::move(sp))
323 : {
324 186 : if(*sp_ == *other.sp_)
325 : {
326 196 : t_ = detail::exchange(
327 98 : other.t_, &empty_);
328 98 : return;
329 : }
330 :
331 88 : t_ = &empty_;
332 151 : object(other, sp_).swap(*this);
333 63 : }
334 :
335 197 : object::
336 : object(
337 : object const& other,
338 197 : storage_ptr sp)
339 197 : : sp_(std::move(sp))
340 197 : , t_(&empty_)
341 : {
342 197 : reserve(other.size());
343 185 : revert_construct r(*this);
344 185 : if(t_->is_small())
345 : {
346 712 : for(auto const& v : other)
347 : {
348 724 : ::new(end())
349 800 : key_value_pair(v, sp_);
350 572 : ++t_->size;
351 : }
352 64 : r.commit();
353 64 : return;
354 : }
355 2485 : for(auto const& v : other)
356 : {
357 : // skip duplicate checking
358 : auto& head =
359 2480 : t_->bucket(v.key());
360 2480 : auto pv = ::new(end())
361 2520 : key_value_pair(v, sp_);
362 2440 : access::next(*pv) = head;
363 2440 : head = t_->size;
364 2440 : ++t_->size;
365 : }
366 5 : r.commit();
367 313 : }
368 :
369 389 : object::
370 : object(
371 : std::initializer_list<std::pair<
372 : string_view, value_ref>> init,
373 : std::size_t min_capacity,
374 389 : storage_ptr sp)
375 389 : : sp_(std::move(sp))
376 389 : , t_(&empty_)
377 : {
378 389 : if( min_capacity < init.size())
379 343 : min_capacity = init.size();
380 389 : reserve(min_capacity);
381 373 : revert_construct r(*this);
382 373 : insert(init);
383 260 : r.commit();
384 502 : }
385 :
386 : //----------------------------------------------------------
387 : //
388 : // Assignment
389 : //
390 : //----------------------------------------------------------
391 :
392 : object&
393 22 : object::
394 : operator=(object const& other)
395 : {
396 39 : object tmp(other, sp_);
397 5 : this->~object();
398 5 : ::new(this) object(pilfer(tmp));
399 5 : return *this;
400 5 : }
401 :
402 : object&
403 7 : object::
404 : operator=(object&& other)
405 : {
406 11 : object tmp(std::move(other), sp_);
407 3 : this->~object();
408 3 : ::new(this) object(pilfer(tmp));
409 3 : return *this;
410 3 : }
411 :
412 : object&
413 7 : object::
414 : operator=(
415 : std::initializer_list<std::pair<
416 : string_view, value_ref>> init)
417 : {
418 11 : object tmp(init, sp_);
419 3 : this->~object();
420 3 : ::new(this) object(pilfer(tmp));
421 3 : return *this;
422 3 : }
423 :
424 : //----------------------------------------------------------
425 : //
426 : // Lookup
427 : //
428 : //----------------------------------------------------------
429 :
430 : system::result<value&>
431 4 : object::
432 : try_at(string_view key) noexcept
433 : {
434 4 : auto it = find(key);
435 4 : if( it != end() )
436 2 : return it->value();
437 :
438 2 : system::error_code ec;
439 2 : BOOST_JSON_FAIL(ec, error::out_of_range);
440 2 : return ec;
441 : }
442 :
443 : system::result<value const&>
444 107 : object::
445 : try_at(string_view key) const noexcept
446 : {
447 107 : auto it = find(key);
448 107 : if( it != end() )
449 101 : return it->value();
450 :
451 6 : system::error_code ec;
452 6 : BOOST_JSON_FAIL(ec, error::out_of_range);
453 6 : return ec;
454 : }
455 :
456 : value const&
457 103 : object::
458 : at(string_view key, source_location const& loc) const&
459 : {
460 103 : return try_at(key).value(loc);
461 : }
462 :
463 : //----------------------------------------------------------
464 : //
465 : // Modifiers
466 : //
467 : //----------------------------------------------------------
468 :
469 : void
470 7 : object::
471 : clear() noexcept
472 : {
473 7 : if(empty())
474 2 : return;
475 5 : if(! sp_.is_not_shared_and_deallocate_is_trivial())
476 5 : destroy(begin(), end());
477 5 : if(! t_->is_small())
478 4 : t_->clear();
479 5 : t_->size = 0;
480 : }
481 :
482 : void
483 433 : object::
484 : insert(
485 : std::initializer_list<std::pair<
486 : string_view, value_ref>> init)
487 : {
488 433 : auto const n0 = size();
489 433 : if(init.size() > max_size() - n0)
490 : {
491 : BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
492 1 : detail::throw_system_error( error::object_too_large, &loc );
493 : }
494 432 : revert_insert r( *this, n0 + init.size() );
495 425 : if(t_->is_small())
496 : {
497 2058 : for(auto& iv : init)
498 : {
499 : auto result =
500 1850 : detail::find_in_object(*this, iv.first);
501 1850 : if(result.first)
502 : {
503 : // ignore duplicate
504 4 : continue;
505 : }
506 1923 : ::new(end()) key_value_pair(
507 : iv.first,
508 2074 : iv.second.make_value(sp_));
509 1769 : ++t_->size;
510 : }
511 208 : r.commit();
512 208 : return;
513 : }
514 1999 : for(auto& iv : init)
515 : {
516 1939 : auto& head = t_->bucket(iv.first);
517 1939 : auto i = head;
518 : for(;;)
519 : {
520 2351 : if(i == null_index_)
521 : {
522 : // VFALCO value_ref should construct
523 : // a key_value_pair using placement
524 1937 : auto& v = *::new(end())
525 : key_value_pair(
526 : iv.first,
527 2097 : iv.second.make_value(sp_));
528 1857 : access::next(v) = head;
529 1857 : head = static_cast<index_t>(
530 1857 : t_->size);
531 1857 : ++t_->size;
532 1857 : break;
533 : }
534 414 : auto& v = (*t_)[i];
535 414 : if(v.key() == iv.first)
536 : {
537 : // ignore duplicate
538 2 : break;
539 : }
540 412 : i = access::next(v);
541 412 : }
542 : }
543 60 : r.commit();
544 425 : }
545 :
546 : auto
547 6 : object::
548 : erase(const_iterator pos) noexcept ->
549 : iterator
550 : {
551 6 : return do_erase(pos,
552 2 : [this](iterator p) {
553 : // the casts silence warnings
554 2 : std::memcpy(
555 : static_cast<void*>(p),
556 2 : static_cast<void const*>(end()),
557 : sizeof(*p));
558 2 : },
559 3 : [this](iterator p) {
560 3 : reindex_relocate(end(), p);
561 6 : });
562 : }
563 :
564 : auto
565 5 : object::
566 : erase(string_view key) noexcept ->
567 : std::size_t
568 : {
569 5 : auto it = find(key);
570 5 : if(it == end())
571 1 : return 0;
572 4 : erase(it);
573 4 : return 1;
574 : }
575 :
576 : auto
577 3 : object::
578 : stable_erase(const_iterator pos) noexcept ->
579 : iterator
580 : {
581 3 : return do_erase(pos,
582 2 : [this](iterator p) {
583 : // the casts silence warnings
584 2 : std::memmove(
585 : static_cast<void*>(p),
586 2 : static_cast<void const*>(p + 1),
587 2 : sizeof(*p) * (end() - p));
588 2 : },
589 1 : [this](iterator p) {
590 10 : for (; p != end(); ++p)
591 : {
592 9 : reindex_relocate(p + 1, p);
593 : }
594 3 : });
595 : }
596 :
597 : auto
598 2 : object::
599 : stable_erase(string_view key) noexcept ->
600 : std::size_t
601 : {
602 2 : auto it = find(key);
603 2 : if(it == end())
604 1 : return 0;
605 1 : stable_erase(it);
606 1 : return 1;
607 : }
608 :
609 : void
610 36 : object::
611 : swap(object& other)
612 : {
613 36 : if(*sp_ == *other.sp_)
614 : {
615 52 : t_ = detail::exchange(
616 26 : other.t_, t_);
617 26 : return;
618 : }
619 : object temp1(
620 10 : std::move(*this),
621 24 : other.storage());
622 : object temp2(
623 6 : std::move(other),
624 16 : this->storage());
625 2 : other.~object();
626 2 : ::new(&other) object(pilfer(temp1));
627 2 : this->~object();
628 2 : ::new(this) object(pilfer(temp2));
629 6 : }
630 :
631 : //----------------------------------------------------------
632 : //
633 : // Lookup
634 : //
635 : //----------------------------------------------------------
636 :
637 : auto
638 28 : object::
639 : operator[](string_view key) ->
640 : value&
641 : {
642 : auto const result =
643 28 : emplace(key, nullptr);
644 56 : return result.first->value();
645 : }
646 :
647 : auto
648 8 : object::
649 : count(string_view key) const noexcept ->
650 : std::size_t
651 : {
652 8 : if(find(key) == end())
653 3 : return 0;
654 5 : return 1;
655 : }
656 :
657 : auto
658 27 : object::
659 : find(string_view key) noexcept ->
660 : iterator
661 : {
662 27 : if(empty())
663 1 : return end();
664 : auto const p =
665 26 : detail::find_in_object(*this, key).first;
666 26 : if(p)
667 20 : return p;
668 6 : return end();
669 : }
670 :
671 : auto
672 896 : object::
673 : find(string_view key) const noexcept ->
674 : const_iterator
675 : {
676 896 : if(empty())
677 1 : return end();
678 : auto const p =
679 895 : detail::find_in_object(*this, key).first;
680 895 : if(p)
681 883 : return p;
682 12 : return end();
683 : }
684 :
685 : bool
686 3 : object::
687 : contains(
688 : string_view key) const noexcept
689 : {
690 3 : if(empty())
691 1 : return false;
692 2 : return detail::find_in_object(*this, key).first
693 2 : != nullptr;
694 : }
695 :
696 : value const*
697 3 : object::
698 : if_contains(
699 : string_view key) const noexcept
700 : {
701 3 : auto const it = find(key);
702 3 : if(it != end())
703 2 : return &it->value();
704 1 : return nullptr;
705 : }
706 :
707 : value*
708 5 : object::
709 : if_contains(
710 : string_view key) noexcept
711 : {
712 5 : auto const it = find(key);
713 5 : if(it != end())
714 4 : return &it->value();
715 1 : return nullptr;
716 : }
717 :
718 : //----------------------------------------------------------
719 : //
720 : // (private)
721 : //
722 : //----------------------------------------------------------
723 :
724 : key_value_pair*
725 3704 : object::
726 : insert_impl(
727 : pilfered<key_value_pair> p,
728 : std::size_t hash)
729 : {
730 3704 : BOOST_ASSERT(
731 : capacity() > size());
732 3704 : if(t_->is_small())
733 : {
734 2220 : auto const pv = ::new(end())
735 2220 : key_value_pair(p);
736 2220 : ++t_->size;
737 2220 : return pv;
738 : }
739 : auto& head =
740 1484 : t_->bucket(hash);
741 1484 : auto const pv = ::new(end())
742 1484 : key_value_pair(p);
743 1484 : access::next(*pv) = head;
744 1484 : head = t_->size;
745 1484 : ++t_->size;
746 1484 : return pv;
747 : }
748 :
749 : // allocate new table, copy elements there, and rehash them
750 : object::table*
751 1667 : object::
752 : reserve_impl(std::size_t new_capacity)
753 : {
754 1667 : BOOST_ASSERT(
755 : new_capacity > t_->capacity);
756 1660 : auto t = table::allocate(
757 : growth(new_capacity),
758 1667 : t_->salt, sp_);
759 1593 : if(! empty())
760 476 : std::memcpy(
761 : static_cast<
762 476 : void*>(&(*t)[0]),
763 476 : begin(),
764 476 : size() * sizeof(
765 : key_value_pair));
766 1593 : t->size = t_->size;
767 1593 : std::swap(t_, t);
768 :
769 1593 : if(! t_->is_small())
770 : {
771 : // rebuild hash table,
772 : // without dup checks
773 349 : auto p = end();
774 349 : index_t i = t_->size;
775 1095 : while(i-- > 0)
776 : {
777 746 : --p;
778 : auto& head =
779 746 : t_->bucket(p->key());
780 746 : access::next(*p) = head;
781 746 : head = i;
782 : }
783 : }
784 :
785 1593 : return t;
786 : }
787 :
788 : bool
789 83 : object::
790 : equal(object const& other) const noexcept
791 : {
792 83 : if(size() != other.size())
793 5 : return false;
794 78 : auto const end_ = other.end();
795 851 : for(auto e : *this)
796 : {
797 775 : auto it = other.find(e.key());
798 775 : if(it == end_)
799 1 : return false;
800 774 : if(it->value() != e.value())
801 1 : return false;
802 775 : }
803 76 : return true;
804 : }
805 :
806 : std::size_t
807 1667 : object::
808 : growth(
809 : std::size_t new_size) const
810 : {
811 1667 : if(new_size > max_size())
812 : {
813 : BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
814 7 : detail::throw_system_error( error::object_too_large, &loc );
815 : }
816 1660 : std::size_t const old = capacity();
817 1660 : if(old > max_size() - old / 2)
818 2 : return new_size;
819 1658 : std::size_t const g =
820 1658 : old + old / 2; // 1.5x
821 1658 : if(g < new_size)
822 1267 : return new_size;
823 391 : return g;
824 : }
825 :
826 : void
827 17 : object::
828 : remove(
829 : index_t& head,
830 : key_value_pair& v) noexcept
831 : {
832 17 : BOOST_ASSERT(! t_->is_small());
833 : auto const i = static_cast<
834 17 : index_t>(&v - begin());
835 17 : if(head == i)
836 : {
837 15 : head = access::next(v);
838 15 : return;
839 : }
840 : auto* pn =
841 2 : &access::next((*t_)[head]);
842 3 : while(*pn != i)
843 1 : pn = &access::next((*t_)[*pn]);
844 2 : *pn = access::next(v);
845 : }
846 :
847 : void
848 34793 : object::
849 : destroy() noexcept
850 : {
851 34793 : BOOST_ASSERT(t_->capacity > 0);
852 34793 : BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
853 34793 : destroy(begin(), end());
854 34793 : table::deallocate(t_, sp_);
855 34793 : }
856 :
857 : void
858 35028 : object::
859 : destroy(
860 : key_value_pair* first,
861 : key_value_pair* last) noexcept
862 : {
863 35028 : BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
864 83478 : while(last != first)
865 48450 : (--last)->~key_value_pair();
866 35028 : }
867 :
868 : template<class FS, class FB>
869 : auto
870 9 : object::
871 : do_erase(
872 : const_iterator pos,
873 : FS small_reloc,
874 : FB big_reloc) noexcept
875 : -> iterator
876 : {
877 9 : auto p = begin() + (pos - begin());
878 9 : if(t_->is_small())
879 : {
880 4 : p->~value_type();
881 4 : --t_->size;
882 4 : if(p != end())
883 : {
884 4 : small_reloc(p);
885 : }
886 4 : return p;
887 : }
888 5 : remove(t_->bucket(p->key()), *p);
889 5 : p->~value_type();
890 5 : --t_->size;
891 5 : if(p != end())
892 : {
893 4 : big_reloc(p);
894 : }
895 5 : return p;
896 : }
897 :
898 : void
899 12 : object::
900 : reindex_relocate(
901 : key_value_pair* src,
902 : key_value_pair* dst) noexcept
903 : {
904 12 : BOOST_ASSERT(! t_->is_small());
905 12 : auto& head = t_->bucket(src->key());
906 12 : remove(head, *src);
907 : // the casts silence warnings
908 12 : std::memcpy(
909 : static_cast<void*>(dst),
910 : static_cast<void const*>(src),
911 : sizeof(*dst));
912 12 : access::next(*dst) = head;
913 12 : head = static_cast<
914 12 : index_t>(dst - begin());
915 12 : }
916 :
917 : } // namespace json
918 : } // namespace boost
919 :
920 : //----------------------------------------------------------
921 : //
922 : // std::hash specialization
923 : //
924 : //----------------------------------------------------------
925 :
926 : std::size_t
927 8 : std::hash<::boost::json::object>::operator()(
928 : ::boost::json::object const& jo) const noexcept
929 : {
930 8 : return ::boost::hash< ::boost::json::object >()( jo );
931 : }
932 :
933 : //----------------------------------------------------------
934 :
935 :
936 : #endif
|