impl/object.ipp

100.0% Lines (442 / 442) 100.0% Functions (51 / 51)
object.ipp
f(x) 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