impl/object.ipp

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