GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/object.hpp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 168 168 100.0%
Functions: 69 71 97.2%
Branches: 70 72 97.2%

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_HPP
11 #define BOOST_JSON_IMPL_OBJECT_HPP
12
13 #include <boost/json/value.hpp>
14 #include <iterator>
15 #include <cmath>
16 #include <type_traits>
17 #include <utility>
18
19 namespace boost {
20 namespace json {
21
22 namespace detail {
23
24 // Objects with size less than or equal
25 // to this number will use a linear search
26 // instead of the more expensive hash function.
27 static
28 constexpr
29 std::size_t
30 small_object_size_ = 18;
31
32 BOOST_STATIC_ASSERT(
33 small_object_size_ <
34 BOOST_JSON_MAX_STRUCTURED_SIZE);
35
36 } // detail
37
38 //----------------------------------------------------------
39
40 struct alignas(key_value_pair)
41 object::table
42 {
43 std::uint32_t size = 0;
44 std::uint32_t capacity = 0;
45 std::uintptr_t salt = 0;
46
47 #if defined(_MSC_VER) && BOOST_JSON_ARCH == 32
48 // VFALCO If we make key_value_pair smaller,
49 // then we might want to revisit this
50 // padding.
51 BOOST_STATIC_ASSERT(
52 sizeof(key_value_pair) == 32);
53 char pad[4] = {}; // silence warnings
54 #endif
55
56 constexpr table();
57
58 // returns true if we use a linear
59 // search instead of the hash table.
60 121981 bool is_small() const noexcept
61 {
62 121981 return capacity <=
63 121981 detail::small_object_size_;
64 }
65
66 key_value_pair&
67 292947 operator[](
68 std::size_t pos) noexcept
69 {
70 return reinterpret_cast<
71 key_value_pair*>(
72 292947 this + 1)[pos];
73 }
74
75 // VFALCO This is exported for tests
76 BOOST_JSON_DECL
77 std::size_t
78 digest(string_view key) const noexcept;
79
80 inline
81 index_t&
82 bucket(std::size_t hash) noexcept;
83
84 inline
85 index_t&
86 bucket(string_view key) noexcept;
87
88 inline
89 void
90 clear() noexcept;
91
92 static
93 inline
94 table*
95 allocate(
96 std::size_t capacity,
97 std::uintptr_t salt,
98 storage_ptr const& sp);
99
100 static
101 void
102 36386 deallocate(
103 table* p,
104 storage_ptr const& sp) noexcept
105 {
106
2/2
✓ Branch 0 taken 1006 times.
✓ Branch 1 taken 35380 times.
36386 if(p->capacity == 0)
107 1006 return;
108
2/2
✓ Branch 1 taken 382 times.
✓ Branch 2 taken 34998 times.
35380 if(! p->is_small())
109 382 sp->deallocate(p,
110 382 sizeof(table) + p->capacity * (
111 sizeof(key_value_pair) +
112 sizeof(index_t)));
113 else
114 34998 sp->deallocate(p,
115 34998 sizeof(table) + p->capacity *
116 sizeof(key_value_pair));
117 }
118 };
119
120 //----------------------------------------------------------
121
122 class object::revert_construct
123 {
124 object* obj_;
125
126 BOOST_JSON_DECL
127 void
128 destroy() noexcept;
129
130 public:
131 explicit
132 713 revert_construct(
133 object& obj) noexcept
134 713 : obj_(&obj)
135 {
136 713 }
137
138 713 ~revert_construct()
139 374 {
140
2/2
✓ Branch 0 taken 339 times.
✓ Branch 1 taken 374 times.
713 if(! obj_)
141 339 return;
142 374 destroy();
143 713 }
144
145 void
146 339 commit() noexcept
147 {
148 339 obj_ = nullptr;
149 339 }
150 };
151
152 //----------------------------------------------------------
153
154 class object::revert_insert
155 {
156 object* obj_;
157 table* t_ = nullptr;
158 std::size_t size_;
159
160 BOOST_JSON_DECL
161 void
162 destroy() noexcept;
163
164 public:
165 explicit
166 511 revert_insert(
167 object& obj,
168 std::size_t capacity)
169 511 : obj_(&obj)
170 511 , size_(obj_->size())
171 {
172
2/2
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 373 times.
511 if( capacity > obj_->capacity() )
173 138 t_ = obj_->reserve_impl(capacity);
174 502 }
175
176 502 ~revert_insert()
177 230 {
178
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 230 times.
502 if(! obj_)
179 272 return;
180
181 230 destroy();
182
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 113 times.
230 if( t_ )
183 {
184 117 table::deallocate( obj_->t_, obj_->sp_ );
185 117 obj_->t_ = t_;
186 }
187 else
188 {
189 113 obj_->t_->size = static_cast<index_t>(size_);
190 }
191 502 }
192
193 void
194 272 commit() noexcept
195 {
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 272 times.
272 BOOST_ASSERT(obj_);
197
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 260 times.
272 if( t_ )
198 12 table::deallocate( t_, obj_->sp_ );
199 272 obj_ = nullptr;
200 272 }
201 };
202
203 //----------------------------------------------------------
204 //
205 // Iterators
206 //
207 //----------------------------------------------------------
208
209 auto
210 70799 object::
211 begin() noexcept ->
212 iterator
213 {
214 70799 return &(*t_)[0];
215 }
216
217 auto
218 53225 object::
219 begin() const noexcept ->
220 const_iterator
221 {
222 53225 return &(*t_)[0];
223 }
224
225 auto
226 3 object::
227 cbegin() const noexcept ->
228 const_iterator
229 {
230 3 return &(*t_)[0];
231 }
232
233 auto
234 46051 object::
235 end() noexcept ->
236 iterator
237 {
238 46051 return &(*t_)[t_->size];
239 }
240
241 auto
242 27813 object::
243 end() const noexcept ->
244 const_iterator
245 {
246 27813 return &(*t_)[t_->size];
247 }
248
249 auto
250 3 object::
251 cend() const noexcept ->
252 const_iterator
253 {
254 3 return &(*t_)[t_->size];
255 }
256
257 auto
258 2 object::
259 rbegin() noexcept ->
260 reverse_iterator
261 {
262 2 return reverse_iterator(end());
263 }
264
265 auto
266 2 object::
267 rbegin() const noexcept ->
268 const_reverse_iterator
269 {
270 2 return const_reverse_iterator(end());
271 }
272
273 auto
274 2 object::
275 crbegin() const noexcept ->
276 const_reverse_iterator
277 {
278 2 return const_reverse_iterator(end());
279 }
280
281 auto
282 2 object::
283 rend() noexcept ->
284 reverse_iterator
285 {
286 2 return reverse_iterator(begin());
287 }
288
289 auto
290 2 object::
291 rend() const noexcept ->
292 const_reverse_iterator
293 {
294 2 return const_reverse_iterator(begin());
295 }
296
297 auto
298 2 object::
299 crend() const noexcept ->
300 const_reverse_iterator
301 {
302 2 return const_reverse_iterator(begin());
303 }
304
305 //----------------------------------------------------------
306 //
307 // Capacity
308 //
309 //----------------------------------------------------------
310
311 bool
312 10329 object::
313 empty() const noexcept
314 {
315 10329 return t_->size == 0;
316 }
317
318 auto
319 44124 object::
320 size() const noexcept ->
321 std::size_t
322 {
323 44124 return t_->size;
324 }
325
326 constexpr
327 std::size_t
328 73166 object::
329 max_size() noexcept
330 {
331 // max_size depends on the address model
332 using min = std::integral_constant<std::size_t,
333 (std::size_t(-1) - sizeof(table)) /
334 (sizeof(key_value_pair) + sizeof(index_t))>;
335 return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
336 73166 min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
337 }
338
339 auto
340 18598 object::
341 capacity() const noexcept ->
342 std::size_t
343 {
344 18598 return t_->capacity;
345 }
346
347 void
348 4689 object::
349 reserve(std::size_t new_capacity)
350 {
351
2/2
✓ Branch 1 taken 3160 times.
✓ Branch 2 taken 1529 times.
4689 if( new_capacity <= capacity() )
352 3160 return;
353 1529 table* const old_table = reserve_impl(new_capacity);
354 1464 table::deallocate( old_table, sp_ );
355 }
356
357 //----------------------------------------------------------
358 //
359 // Lookup
360 //
361 //----------------------------------------------------------
362
363 value&
364 41 object::
365 at(string_view key, source_location const& loc) &
366 {
367 41 auto const& self = *this;
368 41 return const_cast< value& >( self.at(key, loc) );
369 }
370
371 value&&
372 5 object::
373 at(string_view key, source_location const& loc) &&
374 {
375 5 return std::move( at(key, loc) );
376 }
377
378 //----------------------------------------------------------
379
380 template<class P, class>
381 auto
382 6210 object::
383 insert(P&& p) ->
384 std::pair<iterator, bool>
385 {
386
3/3
✓ Branch 2 taken 44 times.
✓ Branch 5 taken 44 times.
✓ Branch 1 taken 1 times.
6800 key_value_pair v(
387 6210 std::forward<P>(p), sp_);
388
1/1
✓ Branch 3 taken 2812 times.
11496 return emplace_impl( v.key(), pilfer(v) );
389 5914 }
390
391 template<class M>
392 auto
393 34 object::
394 insert_or_assign(
395 string_view key, M&& m) ->
396 std::pair<iterator, bool>
397 {
398
1/1
✓ Branch 1 taken 10 times.
34 std::pair<iterator, bool> result = emplace_impl(
399 key, key, static_cast<M&&>(m) );
400
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
20 if( !result.second )
401 {
402
2/2
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
16 value(static_cast<M>(m), sp_).swap(
403 6 result.first->value());
404 }
405 18 return result;
406 }
407
408 template<class Arg>
409 auto
410 1607 object::
411 emplace(
412 string_view key,
413 Arg&& arg) ->
414 std::pair<iterator, bool>
415 {
416 1607 return emplace_impl( key, key, static_cast<Arg&&>(arg) );
417 }
418
419 //----------------------------------------------------------
420 //
421 // (private)
422 //
423 //----------------------------------------------------------
424
425 template<class InputIt>
426 void
427 155 object::
428 construct(
429 InputIt first,
430 InputIt last,
431 std::size_t min_capacity,
432 std::input_iterator_tag)
433 {
434
1/1
✓ Branch 1 taken 76 times.
155 reserve(min_capacity);
435 151 revert_construct r(*this);
436
2/2
✓ Branch 1 taken 750 times.
✓ Branch 2 taken 3 times.
1484 while(first != last)
437 {
438
1/1
✓ Branch 2 taken 677 times.
1478 insert(*first);
439 1333 ++first;
440 }
441 6 r.commit();
442 151 }
443
444 template<class InputIt>
445 void
446 163 object::
447 construct(
448 InputIt first,
449 InputIt last,
450 std::size_t min_capacity,
451 std::forward_iterator_tag)
452 {
453 163 auto n = static_cast<
454 163 std::size_t>(std::distance(
455 first, last));
456
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 6 times.
163 if( n < min_capacity)
457 152 n = min_capacity;
458
1/1
✓ Branch 1 taken 79 times.
163 reserve(n);
459 158 revert_construct r(*this);
460
3/3
✓ Branch 0 taken 764 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
1542 while(first != last)
461 {
462
1/1
✓ Branch 1 taken 692 times.
1528 insert(*first);
463 1384 ++first;
464 }
465 14 r.commit();
466 158 }
467
468 template<class InputIt>
469 void
470 94 object::
471 insert(
472 InputIt first,
473 InputIt last,
474 std::input_iterator_tag)
475 {
476 // Since input iterators cannot be rewound,
477 // we keep inserted elements on an exception.
478 //
479
2/2
✓ Branch 1 taken 867 times.
✓ Branch 2 taken 4 times.
871 while(first != last)
480 {
481 867 insert(*first);
482 777 ++first;
483 }
484 4 }
485
486 template<class InputIt>
487 void
488 159 object::
489 insert(
490 InputIt first,
491 InputIt last,
492 std::forward_iterator_tag)
493 {
494 159 auto const n =
495 static_cast<std::size_t>(
496 159 std::distance(first, last));
497 159 auto const n0 = size();
498
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 79 times.
159 if(n > max_size() - n0)
499 {
500 BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
501 1 detail::throw_system_error( error::object_too_large, &loc );
502 }
503
1/1
✓ Branch 1 taken 77 times.
158 revert_insert r( *this, n0 + n );
504
2/2
✓ Branch 0 taken 734 times.
✓ Branch 1 taken 4 times.
1476 while(first != last)
505 {
506
1/1
✓ Branch 1 taken 661 times.
1468 insert(*first);
507 1322 ++first;
508 }
509 8 r.commit();
510 154 }
511
512 template< class... Args >
513 std::pair<object::iterator, bool>
514 7555 object::
515 emplace_impl( string_view key, Args&& ... args )
516 {
517 7555 std::pair<iterator, std::size_t> search_result(nullptr, 0);
518
2/2
✓ Branch 1 taken 3368 times.
✓ Branch 2 taken 544 times.
7555 if( !empty() )
519 {
520 6518 search_result = detail::find_in_object(*this, key);
521
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 3340 times.
6518 if( search_result.first )
522 54 return { search_result.first, false };
523 }
524
525 // we create the new value before reserving, in case it is a reference to
526 // a subobject of the current object
527
2/2
✓ Branch 1 taken 906 times.
✓ Branch 3 taken 2825 times.
8085 key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
528 // the key might get deallocated too
529 7195 key = kv.key();
530
531 7195 std::size_t const old_capacity = capacity();
532
1/1
✓ Branch 2 taken 3704 times.
7195 reserve(size() + 1);
533
2/2
✓ Branch 2 taken 405 times.
✓ Branch 3 taken 123 times.
8148 if( (empty() && capacity() > detail::small_object_size_)
534
6/6
✓ Branch 0 taken 528 times.
✓ Branch 1 taken 3176 times.
✓ Branch 3 taken 588 times.
✓ Branch 4 taken 2993 times.
✓ Branch 5 taken 711 times.
✓ Branch 6 taken 2993 times.
8148 || (capacity() != old_capacity) )
535 1374 search_result.second = detail::digest(
536 1374 key.begin(), key.end(), t_->salt);
537
538
3/4
✓ Branch 1 taken 1484 times.
✓ Branch 2 taken 2220 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1484 times.
7143 BOOST_ASSERT(
539 t_->is_small() ||
540 (search_result.second ==
541 detail::digest(key.begin(), key.end(), t_->salt)) );
542
543
1/1
✓ Branch 2 taken 3704 times.
7143 return { insert_impl(pilfer(kv), search_result.second), true };
544 7195 }
545
546 //----------------------------------------------------------
547
548 namespace detail {
549
550 34879 unchecked_object::
551 1086 ~unchecked_object()
552 {
553
2/2
✓ Branch 0 taken 33791 times.
✓ Branch 1 taken 1088 times.
34879 if(! data_)
554 33791 return;
555
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1086 times.
1088 if(sp_.is_not_shared_and_deallocate_is_trivial())
556 2 return;
557 1086 value* p = data_;
558
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1086 times.
1146 while(size_--)
559 {
560 60 p[0].~value();
561 60 p[1].~value();
562 60 p += 2;
563 }
564 34879 }
565
566 } // detail
567
568 } // namespace json
569 } // namespace boost
570
571 #endif
572