impl/object.ipp

100.0% Lines (441/0/441) 100.0% List of functions (51/0/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 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