LCOV - code coverage report
Current view: top level - json/impl - object.ipp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 435 435
Test Date: 2026-02-25 20:43:10 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       42845 : find_in_object(
      33                 :     object const& obj,
      34                 :     CharRange key) noexcept
      35                 : {
      36           42845 :     BOOST_ASSERT(obj.t_->capacity > 0);
      37           42845 :     if(obj.t_->is_small())
      38                 :     {
      39           40997 :         auto it = &(*obj.t_)[0];
      40                 :         auto const last =
      41           40997 :             &(*obj.t_)[obj.t_->size];
      42           75329 :         for(;it != last; ++it)
      43           35055 :             if( key == it->key() )
      44             723 :                 return { it, 0 };
      45           40274 :         return { nullptr, 0 };
      46                 :     }
      47                 :     std::pair<
      48                 :         key_value_pair*,
      49            1848 :         std::size_t> result;
      50            1848 :     BOOST_ASSERT(obj.t_->salt != 0);
      51            1848 :     result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
      52            1848 :     auto i = obj.t_->bucket(
      53                 :         result.second);
      54            3121 :     while(i != object::null_index_)
      55                 :     {
      56            1611 :         auto& v = (*obj.t_)[i];
      57            1611 :         if( key == v.key() )
      58                 :         {
      59             338 :             result.first = &v;
      60             338 :             return result;
      61                 :         }
      62            1273 :         i = access::next(v);
      63                 :     }
      64            1510 :     result.first = nullptr;
      65            1510 :     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            7135 : object::table::
      87                 : digest(string_view key) const noexcept
      88                 : {
      89            7135 :     BOOST_ASSERT(salt != 0);
      90            7135 :     return detail::digest(
      91           14270 :         key.begin(), key.end(), salt);
      92                 : }
      93                 : 
      94                 : auto
      95           10517 : object::table::
      96                 : bucket(std::size_t hash) noexcept ->
      97                 :     index_t&
      98                 : {
      99                 :     return reinterpret_cast<
     100           10517 :         index_t*>(&(*this)[capacity])[
     101           10517 :             hash % capacity];
     102                 : }
     103                 : 
     104                 : auto
     105            7082 : object::table::
     106                 : bucket(string_view key) noexcept ->
     107                 :     index_t&
     108                 : {
     109            7082 :     return bucket(digest(key));
     110                 : }
     111                 : 
     112                 : void
     113             392 : object::table::
     114                 : clear() noexcept
     115                 : {
     116             392 :     BOOST_ASSERT(! is_small());
     117                 :     // initialize buckets
     118             784 :     std::memset(
     119                 :         reinterpret_cast<index_t*>(
     120             392 :             &(*this)[capacity]),
     121                 :         0xff, // null_index_
     122             392 :         capacity * sizeof(index_t));
     123             392 : }
     124                 : 
     125                 : object::table*
     126           35479 : 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           35479 :     BOOST_ASSERT(capacity > 0);
     135           35479 :     BOOST_ASSERT(capacity <= max_size());
     136                 :     table* p;
     137           35479 :     if(capacity <= detail::small_object_size_)
     138                 :     {
     139                 :         p = reinterpret_cast<
     140           35076 :             table*>(sp->allocate(
     141           35076 :                 sizeof(table) + capacity *
     142                 :                     sizeof(key_value_pair)));
     143           34985 :         p->capacity = static_cast<
     144                 :             std::uint32_t>(capacity);
     145                 :     }
     146                 :     else
     147                 :     {
     148                 :         p = reinterpret_cast<
     149             403 :             table*>(sp->allocate(
     150             403 :                 sizeof(table) + capacity * (
     151                 :                     sizeof(key_value_pair) +
     152                 :                     sizeof(index_t))));
     153             388 :         p->capacity = static_cast<
     154                 :             std::uint32_t>(capacity);
     155             388 :         p->clear();
     156                 :     }
     157           35373 :     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           34884 :         p->salt = reinterpret_cast<
     167                 :             std::uintptr_t>(p);
     168                 :     }
     169           35373 :     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            2569 :             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             929 :             auto& v = (*t_)[i];
     267             929 :             if(v.key() != dest->key())
     268                 :             {
     269             928 :                 i = access::next(v);
     270             928 :                 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             928 :         }
     285                 :     }
     286              33 :     t_->size = static_cast<
     287              33 :         index_t>(dest - begin());
     288              39 : }
     289                 : 
     290           35946 : object::
     291           34396 : ~object() noexcept
     292                 : {
     293           35946 :     if(sp_.is_not_shared_and_deallocate_is_trivial())
     294               5 :         return;
     295           35941 :     if(t_->capacity == 0)
     296            1545 :         return;
     297           34396 :     destroy();
     298           35946 : }
     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              71 : object::
     311              71 : object(object&& other) noexcept
     312              71 :     : sp_(other.sp_)
     313             142 :     , t_(detail::exchange(
     314              71 :         other.t_, &empty_))
     315                 : {
     316              71 : }
     317                 : 
     318             184 : object::
     319                 : object(
     320                 :     object&& other,
     321             184 :     storage_ptr sp)
     322             184 :     : sp_(std::move(sp))
     323                 : {
     324             184 :     if(*sp_ == *other.sp_)
     325                 :     {
     326             192 :         t_ = detail::exchange(
     327              96 :             other.t_, &empty_);
     328              96 :         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             381 : object::
     370                 : object(
     371                 :     std::initializer_list<std::pair<
     372                 :         string_view, value_ref>> init,
     373                 :     std::size_t min_capacity,
     374             381 :     storage_ptr sp)
     375             381 :     : sp_(std::move(sp))
     376             381 :     , t_(&empty_)
     377                 : {
     378             381 :     if( min_capacity < init.size())
     379             335 :         min_capacity = init.size();
     380             381 :     reserve(min_capacity);
     381             365 :     revert_construct r(*this);
     382             365 :     insert(init);
     383             252 :     r.commit();
     384             494 : }
     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             108 : object::
     445                 : try_at(string_view key) const noexcept
     446                 : {
     447             108 :     auto it = find(key);
     448             108 :     if( it != end() )
     449             102 :         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             104 : object::
     458                 : at(string_view key, source_location const& loc) const&
     459                 : {
     460             104 :     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             425 : object::
     484                 : insert(
     485                 :     std::initializer_list<std::pair<
     486                 :         string_view, value_ref>> init)
     487                 : {
     488             425 :     auto const n0 = size();
     489             425 :     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             424 :     revert_insert r( *this, n0 + init.size() );
     495             417 :     if(t_->is_small())
     496                 :     {
     497            2032 :         for(auto& iv : init)
     498                 :         {
     499                 :             auto result =
     500            1832 :                 detail::find_in_object(*this, iv.first);
     501            1832 :             if(result.first)
     502                 :             {
     503                 :                 // ignore duplicate
     504               4 :                 continue;
     505                 :             }
     506            1905 :             ::new(end()) key_value_pair(
     507                 :                 iv.first,
     508            2056 :                 iv.second.make_value(sp_));
     509            1751 :             ++t_->size;
     510                 :         }
     511             200 :         r.commit();
     512             200 :         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            3552 :             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            1615 :             auto& v = (*t_)[i];
     535            1615 :             if(v.key() == iv.first)
     536                 :             {
     537                 :                 // ignore duplicate
     538               2 :                 break;
     539                 :             }
     540            1613 :             i = access::next(v);
     541            1613 :         }
     542                 :     }
     543              60 :     r.commit();
     544             417 : }
     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             146 : object::
     639                 : operator[](string_view key) ->
     640                 :     value&
     641                 : {
     642                 :     auto const result =
     643             146 :         emplace(key, nullptr);
     644             292 :     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             879 : object::
     673                 : find(string_view key) const noexcept ->
     674                 :     const_iterator
     675                 : {
     676             879 :     if(empty())
     677               1 :         return end();
     678                 :     auto const p =
     679             878 :         detail::find_in_object(*this, key).first;
     680             878 :     if(p)
     681             866 :         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            3781 : object::
     726                 : insert_impl(
     727                 :     pilfered<key_value_pair> p,
     728                 :     std::size_t hash)
     729                 : {
     730            3781 :     BOOST_ASSERT(
     731                 :         capacity() > size());
     732            3781 :     if(t_->is_small())
     733                 :     {
     734            2194 :         auto const pv = ::new(end())
     735            2194 :             key_value_pair(p);
     736            2194 :         ++t_->size;
     737            2194 :         return pv;
     738                 :     }
     739                 :     auto& head =
     740            1587 :         t_->bucket(hash);
     741            1587 :     auto const pv = ::new(end())
     742            1587 :         key_value_pair(p);
     743            1587 :     access::next(*pv) = head;
     744            1587 :     head = t_->size;
     745            1587 :     ++t_->size;
     746            1587 :     return pv;
     747                 : }
     748                 : 
     749                 : // allocate new table, copy elements there, and rehash them
     750                 : object::table*
     751            1656 : object::
     752                 : reserve_impl(std::size_t new_capacity)
     753                 : {
     754            1656 :     BOOST_ASSERT(
     755                 :         new_capacity > t_->capacity);
     756            1649 :     auto t = table::allocate(
     757                 :         growth(new_capacity),
     758            1656 :             t_->salt, sp_);
     759            1582 :     if(! empty())
     760             488 :         std::memcpy(
     761                 :             static_cast<
     762             488 :                 void*>(&(*t)[0]),
     763             488 :             begin(),
     764             488 :             size() * sizeof(
     765                 :                 key_value_pair));
     766            1582 :     t->size = t_->size;
     767            1582 :     std::swap(t_, t);
     768                 : 
     769            1582 :     if(! t_->is_small())
     770                 :     {
     771                 :         // rebuild hash table,
     772                 :         // without dup checks
     773             355 :         auto p = end();
     774             355 :         index_t i = t_->size;
     775            1360 :         while(i-- > 0)
     776                 :         {
     777            1005 :             --p;
     778                 :             auto& head =
     779            1005 :                 t_->bucket(p->key());
     780            1005 :             access::next(*p) = head;
     781            1005 :             head = i;
     782                 :         }
     783                 :     }
     784                 : 
     785            1582 :     return t;
     786                 : }
     787                 : 
     788                 : bool
     789              75 : object::
     790                 : equal(object const& other) const noexcept
     791                 : {
     792              75 :     if(size() != other.size())
     793               5 :         return false;
     794              70 :     auto const end_ = other.end();
     795             825 :     for(auto e : *this)
     796                 :     {
     797             757 :         auto it = other.find(e.key());
     798             757 :         if(it == end_)
     799               1 :             return false;
     800             756 :         if(it->value() != e.value())
     801               1 :             return false;
     802             757 :     }
     803              68 :     return true;
     804                 : }
     805                 : 
     806                 : std::size_t
     807            1656 : object::
     808                 : growth(
     809                 :     std::size_t new_size) const
     810                 : {
     811            1656 :     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            1649 :     std::size_t const old = capacity();
     817            1649 :     if(old > max_size() - old / 2)
     818               2 :         return new_size;
     819            1647 :     std::size_t const g =
     820            1647 :         old + old / 2; // 1.5x
     821            1647 :     if(g < new_size)
     822            1245 :         return new_size;
     823             402 :     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              11 :         head = access::next(v);
     838              11 :         return;
     839                 :     }
     840                 :     auto* pn =
     841               6 :         &access::next((*t_)[head]);
     842               7 :     while(*pn != i)
     843               1 :         pn = &access::next((*t_)[*pn]);
     844               6 :     *pn = access::next(v);
     845                 : }
     846                 : 
     847                 : void
     848           34770 : object::
     849                 : destroy() noexcept
     850                 : {
     851           34770 :     BOOST_ASSERT(t_->capacity > 0);
     852           34770 :     BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
     853           34770 :     destroy(begin(), end());
     854           34770 :     table::deallocate(t_, sp_);
     855           34770 : }
     856                 : 
     857                 : void
     858           35005 : object::
     859                 : destroy(
     860                 :     key_value_pair* first,
     861                 :     key_value_pair* last) noexcept
     862                 : {
     863           35005 :     BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
     864           83514 :     while(last != first)
     865           48509 :         (--last)->~key_value_pair();
     866           35005 : }
     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.3