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