File indexing completed on 2025-01-30 09:44:42
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_JSON_IMPL_OBJECT_IPP
0011 #define BOOST_JSON_IMPL_OBJECT_IPP
0012
0013 #include <boost/container_hash/hash.hpp>
0014 #include <boost/json/object.hpp>
0015 #include <boost/json/detail/digest.hpp>
0016 #include <boost/json/detail/except.hpp>
0017 #include <algorithm>
0018 #include <cmath>
0019 #include <cstdlib>
0020 #include <cstring>
0021 #include <new>
0022 #include <stdexcept>
0023 #include <type_traits>
0024
0025 namespace boost {
0026 namespace json {
0027 namespace detail {
0028
0029 template<class CharRange>
0030 std::pair<key_value_pair*, std::size_t>
0031 find_in_object(
0032 object const& obj,
0033 CharRange key) noexcept
0034 {
0035 BOOST_ASSERT(obj.t_->capacity > 0);
0036 if(obj.t_->is_small())
0037 {
0038 auto it = &(*obj.t_)[0];
0039 auto const last =
0040 &(*obj.t_)[obj.t_->size];
0041 for(;it != last; ++it)
0042 if( key == it->key() )
0043 return { it, 0 };
0044 return { nullptr, 0 };
0045 }
0046 std::pair<
0047 key_value_pair*,
0048 std::size_t> result;
0049 BOOST_ASSERT(obj.t_->salt != 0);
0050 result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
0051 auto i = obj.t_->bucket(
0052 result.second);
0053 while(i != object::null_index_)
0054 {
0055 auto& v = (*obj.t_)[i];
0056 if( key == v.key() )
0057 {
0058 result.first = &v;
0059 return result;
0060 }
0061 i = access::next(v);
0062 }
0063 result.first = nullptr;
0064 return result;
0065 }
0066
0067
0068 template
0069 std::pair<key_value_pair*, std::size_t>
0070 find_in_object<string_view>(
0071 object const& obj,
0072 string_view key) noexcept;
0073
0074 }
0075
0076
0077
0078 constexpr object::table::table() = default;
0079
0080
0081 BOOST_JSON_REQUIRE_CONST_INIT
0082 object::table object::empty_;
0083
0084 std::size_t
0085 object::table::
0086 digest(string_view key) const noexcept
0087 {
0088 BOOST_ASSERT(salt != 0);
0089 return detail::digest(
0090 key.begin(), key.end(), salt);
0091 }
0092
0093 auto
0094 object::table::
0095 bucket(std::size_t hash) noexcept ->
0096 index_t&
0097 {
0098 return reinterpret_cast<
0099 index_t*>(&(*this)[capacity])[
0100 hash % capacity];
0101 }
0102
0103 auto
0104 object::table::
0105 bucket(string_view key) noexcept ->
0106 index_t&
0107 {
0108 return bucket(digest(key));
0109 }
0110
0111 void
0112 object::table::
0113 clear() noexcept
0114 {
0115 BOOST_ASSERT(! is_small());
0116
0117 std::memset(
0118 reinterpret_cast<index_t*>(
0119 &(*this)[capacity]),
0120 0xff,
0121 capacity * sizeof(index_t));
0122 }
0123
0124 object::table*
0125 object::table::
0126 allocate(
0127 std::size_t capacity,
0128 std::uintptr_t salt,
0129 storage_ptr const& sp)
0130 {
0131 BOOST_STATIC_ASSERT(
0132 alignof(key_value_pair) >=
0133 alignof(index_t));
0134 BOOST_ASSERT(capacity > 0);
0135 BOOST_ASSERT(capacity <= max_size());
0136 table* p;
0137 if(capacity <= detail::small_object_size_)
0138 {
0139 p = reinterpret_cast<
0140 table*>(sp->allocate(
0141 sizeof(table) + capacity *
0142 sizeof(key_value_pair)));
0143 p->capacity = static_cast<
0144 std::uint32_t>(capacity);
0145 }
0146 else
0147 {
0148 p = reinterpret_cast<
0149 table*>(sp->allocate(
0150 sizeof(table) + capacity * (
0151 sizeof(key_value_pair) +
0152 sizeof(index_t))));
0153 p->capacity = static_cast<
0154 std::uint32_t>(capacity);
0155 p->clear();
0156 }
0157 if(salt)
0158 {
0159 p->salt = salt;
0160 }
0161 else
0162 {
0163
0164
0165
0166 p->salt = reinterpret_cast<
0167 std::uintptr_t>(p);
0168 }
0169 return p;
0170 }
0171
0172
0173
0174 void
0175 object::
0176 revert_construct::
0177 destroy() noexcept
0178 {
0179 obj_->destroy();
0180 }
0181
0182
0183
0184 void
0185 object::
0186 revert_insert::
0187 destroy() noexcept
0188 {
0189 obj_->destroy(
0190 &(*obj_->t_)[size_],
0191 obj_->end());
0192 }
0193
0194
0195
0196
0197
0198
0199
0200 object::
0201 object(detail::unchecked_object&& uo)
0202 : sp_(uo.storage())
0203 {
0204 if(uo.size() == 0)
0205 {
0206 t_ = &empty_;
0207 return;
0208 }
0209
0210 BOOST_ASSERT(
0211 uo.size() <= max_size());
0212 t_ = table::allocate(
0213 uo.size(), 0, sp_);
0214
0215
0216
0217 auto dest = begin();
0218 auto src = uo.release();
0219 auto const end = src + 2 * uo.size();
0220 if(t_->is_small())
0221 {
0222 t_->size = 0;
0223 while(src != end)
0224 {
0225 access::construct_key_value_pair(
0226 dest, pilfer(src[0]), pilfer(src[1]));
0227 src += 2;
0228 auto result = detail::find_in_object(*this, dest->key());
0229 if(! result.first)
0230 {
0231 ++dest;
0232 ++t_->size;
0233 continue;
0234 }
0235
0236 auto& v = *result.first;
0237
0238
0239 v.~key_value_pair();
0240
0241 std::memcpy(
0242 static_cast<void*>(&v),
0243 dest, sizeof(v));
0244 }
0245 return;
0246 }
0247 while(src != end)
0248 {
0249 access::construct_key_value_pair(
0250 dest, pilfer(src[0]), pilfer(src[1]));
0251 src += 2;
0252 auto& head = t_->bucket(dest->key());
0253 auto i = head;
0254 for(;;)
0255 {
0256 if(i == null_index_)
0257 {
0258
0259 access::next(
0260 *dest) = head;
0261 head = static_cast<index_t>(
0262 dest - begin());
0263 ++dest;
0264 break;
0265 }
0266 auto& v = (*t_)[i];
0267 if(v.key() != dest->key())
0268 {
0269 i = access::next(v);
0270 continue;
0271 }
0272
0273
0274 access::next(*dest) =
0275 access::next(v);
0276
0277
0278 v.~key_value_pair();
0279
0280 std::memcpy(
0281 static_cast<void*>(&v),
0282 dest, sizeof(v));
0283 break;
0284 }
0285 }
0286 t_->size = static_cast<
0287 index_t>(dest - begin());
0288 }
0289
0290 object::
0291 ~object() noexcept
0292 {
0293 if(sp_.is_not_shared_and_deallocate_is_trivial())
0294 return;
0295 if(t_->capacity == 0)
0296 return;
0297 destroy();
0298 }
0299
0300 object::
0301 object(
0302 std::size_t min_capacity,
0303 storage_ptr sp)
0304 : sp_(std::move(sp))
0305 , t_(&empty_)
0306 {
0307 reserve(min_capacity);
0308 }
0309
0310 object::
0311 object(object&& other) noexcept
0312 : sp_(other.sp_)
0313 , t_(detail::exchange(
0314 other.t_, &empty_))
0315 {
0316 }
0317
0318 object::
0319 object(
0320 object&& other,
0321 storage_ptr sp)
0322 : sp_(std::move(sp))
0323 {
0324 if(*sp_ == *other.sp_)
0325 {
0326 t_ = detail::exchange(
0327 other.t_, &empty_);
0328 return;
0329 }
0330
0331 t_ = &empty_;
0332 object(other, sp_).swap(*this);
0333 }
0334
0335 object::
0336 object(
0337 object const& other,
0338 storage_ptr sp)
0339 : sp_(std::move(sp))
0340 , t_(&empty_)
0341 {
0342 reserve(other.size());
0343 revert_construct r(*this);
0344 if(t_->is_small())
0345 {
0346 for(auto const& v : other)
0347 {
0348 ::new(end())
0349 key_value_pair(v, sp_);
0350 ++t_->size;
0351 }
0352 r.commit();
0353 return;
0354 }
0355 for(auto const& v : other)
0356 {
0357
0358 auto& head =
0359 t_->bucket(v.key());
0360 auto pv = ::new(end())
0361 key_value_pair(v, sp_);
0362 access::next(*pv) = head;
0363 head = t_->size;
0364 ++t_->size;
0365 }
0366 r.commit();
0367 }
0368
0369 object::
0370 object(
0371 std::initializer_list<std::pair<
0372 string_view, value_ref>> init,
0373 std::size_t min_capacity,
0374 storage_ptr sp)
0375 : sp_(std::move(sp))
0376 , t_(&empty_)
0377 {
0378 if( min_capacity < init.size())
0379 min_capacity = init.size();
0380 reserve(min_capacity);
0381 revert_construct r(*this);
0382 insert(init);
0383 r.commit();
0384 }
0385
0386
0387
0388
0389
0390
0391
0392 object&
0393 object::
0394 operator=(object const& other)
0395 {
0396 object tmp(other, sp_);
0397 this->~object();
0398 ::new(this) object(pilfer(tmp));
0399 return *this;
0400 }
0401
0402 object&
0403 object::
0404 operator=(object&& other)
0405 {
0406 object tmp(std::move(other), sp_);
0407 this->~object();
0408 ::new(this) object(pilfer(tmp));
0409 return *this;
0410 }
0411
0412 object&
0413 object::
0414 operator=(
0415 std::initializer_list<std::pair<
0416 string_view, value_ref>> init)
0417 {
0418 object tmp(init, sp_);
0419 this->~object();
0420 ::new(this) object(pilfer(tmp));
0421 return *this;
0422 }
0423
0424
0425
0426
0427
0428
0429
0430 void
0431 object::
0432 clear() noexcept
0433 {
0434 if(empty())
0435 return;
0436 if(! sp_.is_not_shared_and_deallocate_is_trivial())
0437 destroy(begin(), end());
0438 if(! t_->is_small())
0439 t_->clear();
0440 t_->size = 0;
0441 }
0442
0443 void
0444 object::
0445 insert(
0446 std::initializer_list<std::pair<
0447 string_view, value_ref>> init)
0448 {
0449 auto const n0 = size();
0450 if(init.size() > max_size() - n0)
0451 {
0452 BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
0453 detail::throw_system_error( error::object_too_large, &loc );
0454 }
0455 revert_insert r( *this, n0 + init.size() );
0456 if(t_->is_small())
0457 {
0458 for(auto& iv : init)
0459 {
0460 auto result =
0461 detail::find_in_object(*this, iv.first);
0462 if(result.first)
0463 {
0464
0465 continue;
0466 }
0467 ::new(end()) key_value_pair(
0468 iv.first,
0469 iv.second.make_value(sp_));
0470 ++t_->size;
0471 }
0472 r.commit();
0473 return;
0474 }
0475 for(auto& iv : init)
0476 {
0477 auto& head = t_->bucket(iv.first);
0478 auto i = head;
0479 for(;;)
0480 {
0481 if(i == null_index_)
0482 {
0483
0484
0485 auto& v = *::new(end())
0486 key_value_pair(
0487 iv.first,
0488 iv.second.make_value(sp_));
0489 access::next(v) = head;
0490 head = static_cast<index_t>(
0491 t_->size);
0492 ++t_->size;
0493 break;
0494 }
0495 auto& v = (*t_)[i];
0496 if(v.key() == iv.first)
0497 {
0498
0499 break;
0500 }
0501 i = access::next(v);
0502 }
0503 }
0504 r.commit();
0505 }
0506
0507 auto
0508 object::
0509 erase(const_iterator pos) noexcept ->
0510 iterator
0511 {
0512 return do_erase(pos,
0513 [this](iterator p) {
0514
0515 std::memcpy(
0516 static_cast<void*>(p),
0517 static_cast<void const*>(end()),
0518 sizeof(*p));
0519 },
0520 [this](iterator p) {
0521 reindex_relocate(end(), p);
0522 });
0523 }
0524
0525 auto
0526 object::
0527 erase(string_view key) noexcept ->
0528 std::size_t
0529 {
0530 auto it = find(key);
0531 if(it == end())
0532 return 0;
0533 erase(it);
0534 return 1;
0535 }
0536
0537 auto
0538 object::
0539 stable_erase(const_iterator pos) noexcept ->
0540 iterator
0541 {
0542 return do_erase(pos,
0543 [this](iterator p) {
0544
0545 std::memmove(
0546 static_cast<void*>(p),
0547 static_cast<void const*>(p + 1),
0548 sizeof(*p) * (end() - p));
0549 },
0550 [this](iterator p) {
0551 for (; p != end(); ++p)
0552 {
0553 reindex_relocate(p + 1, p);
0554 }
0555 });
0556 }
0557
0558 auto
0559 object::
0560 stable_erase(string_view key) noexcept ->
0561 std::size_t
0562 {
0563 auto it = find(key);
0564 if(it == end())
0565 return 0;
0566 stable_erase(it);
0567 return 1;
0568 }
0569
0570 void
0571 object::
0572 swap(object& other)
0573 {
0574 if(*sp_ == *other.sp_)
0575 {
0576 t_ = detail::exchange(
0577 other.t_, t_);
0578 return;
0579 }
0580 object temp1(
0581 std::move(*this),
0582 other.storage());
0583 object temp2(
0584 std::move(other),
0585 this->storage());
0586 other.~object();
0587 ::new(&other) object(pilfer(temp1));
0588 this->~object();
0589 ::new(this) object(pilfer(temp2));
0590 }
0591
0592
0593
0594
0595
0596
0597
0598 auto
0599 object::
0600 operator[](string_view key) ->
0601 value&
0602 {
0603 auto const result =
0604 emplace(key, nullptr);
0605 return result.first->value();
0606 }
0607
0608 auto
0609 object::
0610 count(string_view key) const noexcept ->
0611 std::size_t
0612 {
0613 if(find(key) == end())
0614 return 0;
0615 return 1;
0616 }
0617
0618 auto
0619 object::
0620 find(string_view key) noexcept ->
0621 iterator
0622 {
0623 if(empty())
0624 return end();
0625 auto const p =
0626 detail::find_in_object(*this, key).first;
0627 if(p)
0628 return p;
0629 return end();
0630 }
0631
0632 auto
0633 object::
0634 find(string_view key) const noexcept ->
0635 const_iterator
0636 {
0637 if(empty())
0638 return end();
0639 auto const p =
0640 detail::find_in_object(*this, key).first;
0641 if(p)
0642 return p;
0643 return end();
0644 }
0645
0646 bool
0647 object::
0648 contains(
0649 string_view key) const noexcept
0650 {
0651 if(empty())
0652 return false;
0653 return detail::find_in_object(*this, key).first
0654 != nullptr;
0655 }
0656
0657 value const*
0658 object::
0659 if_contains(
0660 string_view key) const noexcept
0661 {
0662 auto const it = find(key);
0663 if(it != end())
0664 return &it->value();
0665 return nullptr;
0666 }
0667
0668 value*
0669 object::
0670 if_contains(
0671 string_view key) noexcept
0672 {
0673 auto const it = find(key);
0674 if(it != end())
0675 return &it->value();
0676 return nullptr;
0677 }
0678
0679
0680
0681
0682
0683
0684
0685 key_value_pair*
0686 object::
0687 insert_impl(
0688 pilfered<key_value_pair> p,
0689 std::size_t hash)
0690 {
0691 BOOST_ASSERT(
0692 capacity() > size());
0693 if(t_->is_small())
0694 {
0695 auto const pv = ::new(end())
0696 key_value_pair(p);
0697 ++t_->size;
0698 return pv;
0699 }
0700 auto& head =
0701 t_->bucket(hash);
0702 auto const pv = ::new(end())
0703 key_value_pair(p);
0704 access::next(*pv) = head;
0705 head = t_->size;
0706 ++t_->size;
0707 return pv;
0708 }
0709
0710
0711 object::table*
0712 object::
0713 reserve_impl(std::size_t new_capacity)
0714 {
0715 BOOST_ASSERT(
0716 new_capacity > t_->capacity);
0717 auto t = table::allocate(
0718 growth(new_capacity),
0719 t_->salt, sp_);
0720 if(! empty())
0721 std::memcpy(
0722 static_cast<
0723 void*>(&(*t)[0]),
0724 begin(),
0725 size() * sizeof(
0726 key_value_pair));
0727 t->size = t_->size;
0728 std::swap(t_, t);
0729
0730 if(! t_->is_small())
0731 {
0732
0733
0734 auto p = end();
0735 index_t i = t_->size;
0736 while(i-- > 0)
0737 {
0738 --p;
0739 auto& head =
0740 t_->bucket(p->key());
0741 access::next(*p) = head;
0742 head = i;
0743 }
0744 }
0745
0746 return t;
0747 }
0748
0749 bool
0750 object::
0751 equal(object const& other) const noexcept
0752 {
0753 if(size() != other.size())
0754 return false;
0755 auto const end_ = other.end();
0756 for(auto e : *this)
0757 {
0758 auto it = other.find(e.key());
0759 if(it == end_)
0760 return false;
0761 if(it->value() != e.value())
0762 return false;
0763 }
0764 return true;
0765 }
0766
0767 std::size_t
0768 object::
0769 growth(
0770 std::size_t new_size) const
0771 {
0772 if(new_size > max_size())
0773 {
0774 BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
0775 detail::throw_system_error( error::object_too_large, &loc );
0776 }
0777 std::size_t const old = capacity();
0778 if(old > max_size() - old / 2)
0779 return new_size;
0780 std::size_t const g =
0781 old + old / 2;
0782 if(g < new_size)
0783 return new_size;
0784 return g;
0785 }
0786
0787 void
0788 object::
0789 remove(
0790 index_t& head,
0791 key_value_pair& v) noexcept
0792 {
0793 BOOST_ASSERT(! t_->is_small());
0794 auto const i = static_cast<
0795 index_t>(&v - begin());
0796 if(head == i)
0797 {
0798 head = access::next(v);
0799 return;
0800 }
0801 auto* pn =
0802 &access::next((*t_)[head]);
0803 while(*pn != i)
0804 pn = &access::next((*t_)[*pn]);
0805 *pn = access::next(v);
0806 }
0807
0808 void
0809 object::
0810 destroy() noexcept
0811 {
0812 BOOST_ASSERT(t_->capacity > 0);
0813 BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
0814 destroy(begin(), end());
0815 table::deallocate(t_, sp_);
0816 }
0817
0818 void
0819 object::
0820 destroy(
0821 key_value_pair* first,
0822 key_value_pair* last) noexcept
0823 {
0824 BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
0825 while(last != first)
0826 (--last)->~key_value_pair();
0827 }
0828
0829 template<class FS, class FB>
0830 auto
0831 object::
0832 do_erase(
0833 const_iterator pos,
0834 FS small_reloc,
0835 FB big_reloc) noexcept
0836 -> iterator
0837 {
0838 auto p = begin() + (pos - begin());
0839 if(t_->is_small())
0840 {
0841 p->~value_type();
0842 --t_->size;
0843 if(p != end())
0844 {
0845 small_reloc(p);
0846 }
0847 return p;
0848 }
0849 remove(t_->bucket(p->key()), *p);
0850 p->~value_type();
0851 --t_->size;
0852 if(p != end())
0853 {
0854 big_reloc(p);
0855 }
0856 return p;
0857 }
0858
0859 void
0860 object::
0861 reindex_relocate(
0862 key_value_pair* src,
0863 key_value_pair* dst) noexcept
0864 {
0865 BOOST_ASSERT(! t_->is_small());
0866 auto& head = t_->bucket(src->key());
0867 remove(head, *src);
0868
0869 std::memcpy(
0870 static_cast<void*>(dst),
0871 static_cast<void const*>(src),
0872 sizeof(*dst));
0873 access::next(*dst) = head;
0874 head = static_cast<
0875 index_t>(dst - begin());
0876 }
0877
0878 }
0879 }
0880
0881
0882
0883
0884
0885
0886
0887 std::size_t
0888 std::hash<::boost::json::object>::operator()(
0889 ::boost::json::object const& jo) const noexcept
0890 {
0891 return ::boost::hash< ::boost::json::object >()( jo );
0892 }
0893
0894
0895
0896
0897 #endif