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

Generated by: LCOV version 2.3