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