LCOV - code coverage report
Current view: top level - json/impl - object.ipp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 435 435
Test Date: 2025-12-23 17:20:51 Functions: 100.0 % 51 51

            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
        

Generated by: LCOV version 2.1