LCOV - code coverage report
Current view: top level - json/impl - object.ipp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 441 441
Test Date: 2026-08-18 13:48:28 Functions: 100.0 % 51 51

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

Generated by: LCOV version 2.3