Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:44:42

0001 //
0002 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/json
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 } // namespace detail
0075 
0076 //----------------------------------------------------------
0077 
0078 constexpr object::table::table() = default;
0079 
0080 // empty objects point here
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     // initialize buckets
0117     std::memset(
0118         reinterpret_cast<index_t*>(
0119             &(*this)[capacity]),
0120         0xff, // null_index_
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         // VFALCO This would be better if it
0164         //        was random, but maybe this
0165         //        is good enough.
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 // Construction
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     // should already be checked
0210     BOOST_ASSERT(
0211         uo.size() <= max_size());
0212     t_ = table::allocate(
0213         uo.size(), 0, sp_);
0214 
0215     // insert all elements, keeping
0216     // the last of any duplicate keys.
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             // handle duplicate
0236             auto& v = *result.first;
0237             // don't bother to check if
0238             // storage deallocate is trivial
0239             v.~key_value_pair();
0240             // trivial relocate
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                 // end of bucket
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             // handle duplicate
0274             access::next(*dest) =
0275                 access::next(v);
0276             // don't bother to check if
0277             // storage deallocate is trivial
0278             v.~key_value_pair();
0279             // trivial relocate
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         // skip duplicate checking
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 // Assignment
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 // Modifiers
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                 // ignore duplicate
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                 // VFALCO value_ref should construct
0484                 // a key_value_pair using placement
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                 // ignore duplicate
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             // the casts silence warnings
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             // the casts silence warnings
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 // Lookup
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 // (private)
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 // allocate new table, copy elements there, and rehash them
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         // rebuild hash table,
0733         // without dup checks
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; // 1.5x
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     // the casts silence warnings
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 } // namespace json
0879 } // namespace boost
0880 
0881 //----------------------------------------------------------
0882 //
0883 // std::hash specialization
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