GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/object.ipp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 435 435 100.0%
Functions: 51 51 100.0%
Branches: 163 183 89.1%

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