/src/boost/boost/json/impl/object.hpp
Line | Count | Source (jump to first uncovered line) |
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 | | bool is_small() const noexcept |
61 | 20.5k | { |
62 | 20.5k | return capacity <= |
63 | 20.5k | detail::small_object_size_; |
64 | 20.5k | } |
65 | | |
66 | | key_value_pair& |
67 | | operator[]( |
68 | | std::size_t pos) noexcept |
69 | 801k | { |
70 | 801k | return reinterpret_cast< |
71 | 801k | key_value_pair*>( |
72 | 801k | this + 1)[pos]; |
73 | 801k | } |
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 | | deallocate( |
103 | | table* p, |
104 | | storage_ptr const& sp) noexcept |
105 | 3.75k | { |
106 | 3.75k | if(p->capacity == 0) |
107 | 0 | return; |
108 | 3.75k | if(! p->is_small()) |
109 | 337 | sp->deallocate(p, |
110 | 337 | sizeof(table) + p->capacity * ( |
111 | 337 | sizeof(key_value_pair) + |
112 | 337 | sizeof(index_t))); |
113 | 3.41k | else |
114 | 3.41k | sp->deallocate(p, |
115 | 3.41k | sizeof(table) + p->capacity * |
116 | 3.41k | sizeof(key_value_pair)); |
117 | 3.75k | } |
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 | | revert_construct( |
133 | | object& obj) noexcept |
134 | 0 | : obj_(&obj) |
135 | 0 | { |
136 | 0 | } |
137 | | |
138 | | ~revert_construct() |
139 | 0 | { |
140 | 0 | if(! obj_) |
141 | 0 | return; |
142 | 0 | destroy(); |
143 | 0 | } |
144 | | |
145 | | void |
146 | | commit() noexcept |
147 | 0 | { |
148 | 0 | obj_ = nullptr; |
149 | 0 | } |
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 | | revert_insert( |
167 | | object& obj, |
168 | | std::size_t capacity) |
169 | 0 | : obj_(&obj) |
170 | 0 | , size_(obj_->size()) |
171 | 0 | { |
172 | 0 | if( capacity > obj_->capacity() ) |
173 | 0 | t_ = obj_->reserve_impl(capacity); |
174 | 0 | } |
175 | | |
176 | | ~revert_insert() |
177 | 0 | { |
178 | 0 | if(! obj_) |
179 | 0 | return; |
180 | | |
181 | 0 | destroy(); |
182 | 0 | if( t_ ) |
183 | 0 | { |
184 | 0 | table::deallocate( obj_->t_, obj_->sp_ ); |
185 | 0 | obj_->t_ = t_; |
186 | 0 | } |
187 | 0 | else |
188 | 0 | { |
189 | 0 | obj_->t_->size = static_cast<index_t>(size_); |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | void |
194 | | commit() noexcept |
195 | 0 | { |
196 | 0 | BOOST_ASSERT(obj_); |
197 | 0 | if( t_ ) |
198 | 0 | table::deallocate( t_, obj_->sp_ ); |
199 | 0 | obj_ = nullptr; |
200 | 0 | } |
201 | | }; |
202 | | |
203 | | //---------------------------------------------------------- |
204 | | // |
205 | | // Iterators |
206 | | // |
207 | | //---------------------------------------------------------- |
208 | | |
209 | | auto |
210 | | object:: |
211 | | begin() noexcept -> |
212 | | iterator |
213 | 22.8k | { |
214 | 22.8k | return &(*t_)[0]; |
215 | 22.8k | } |
216 | | |
217 | | auto |
218 | | object:: |
219 | | begin() const noexcept -> |
220 | | const_iterator |
221 | 10.2k | { |
222 | 10.2k | return &(*t_)[0]; |
223 | 10.2k | } |
224 | | |
225 | | auto |
226 | | object:: |
227 | | cbegin() const noexcept -> |
228 | | const_iterator |
229 | | { |
230 | | return &(*t_)[0]; |
231 | | } |
232 | | |
233 | | auto |
234 | | object:: |
235 | | end() noexcept -> |
236 | | iterator |
237 | 3.75k | { |
238 | 3.75k | return &(*t_)[t_->size]; |
239 | 3.75k | } |
240 | | |
241 | | auto |
242 | | object:: |
243 | | end() const noexcept -> |
244 | | const_iterator |
245 | 12.1k | { |
246 | 12.1k | return &(*t_)[t_->size]; |
247 | 12.1k | } |
248 | | |
249 | | auto |
250 | | object:: |
251 | | cend() const noexcept -> |
252 | | const_iterator |
253 | | { |
254 | | return &(*t_)[t_->size]; |
255 | | } |
256 | | |
257 | | auto |
258 | | object:: |
259 | | rbegin() noexcept -> |
260 | | reverse_iterator |
261 | | { |
262 | | return reverse_iterator(end()); |
263 | | } |
264 | | |
265 | | auto |
266 | | object:: |
267 | | rbegin() const noexcept -> |
268 | | const_reverse_iterator |
269 | | { |
270 | | return const_reverse_iterator(end()); |
271 | | } |
272 | | |
273 | | auto |
274 | | object:: |
275 | | crbegin() const noexcept -> |
276 | | const_reverse_iterator |
277 | | { |
278 | | return const_reverse_iterator(end()); |
279 | | } |
280 | | |
281 | | auto |
282 | | object:: |
283 | | rend() noexcept -> |
284 | | reverse_iterator |
285 | | { |
286 | | return reverse_iterator(begin()); |
287 | | } |
288 | | |
289 | | auto |
290 | | object:: |
291 | | rend() const noexcept -> |
292 | | const_reverse_iterator |
293 | | { |
294 | | return const_reverse_iterator(begin()); |
295 | | } |
296 | | |
297 | | auto |
298 | | object:: |
299 | | crend() const noexcept -> |
300 | | const_reverse_iterator |
301 | | { |
302 | | return const_reverse_iterator(begin()); |
303 | | } |
304 | | |
305 | | //---------------------------------------------------------- |
306 | | // |
307 | | // Capacity |
308 | | // |
309 | | //---------------------------------------------------------- |
310 | | |
311 | | bool |
312 | | object:: |
313 | | empty() const noexcept |
314 | 0 | { |
315 | 0 | return t_->size == 0; |
316 | 0 | } |
317 | | |
318 | | auto |
319 | | object:: |
320 | | size() const noexcept -> |
321 | | std::size_t |
322 | 0 | { |
323 | 0 | return t_->size; |
324 | 0 | } |
325 | | |
326 | | constexpr |
327 | | std::size_t |
328 | | object:: |
329 | | max_size() noexcept |
330 | 0 | { |
331 | | // max_size depends on the address model |
332 | 0 | using min = std::integral_constant<std::size_t, |
333 | 0 | (std::size_t(-1) - sizeof(table)) / |
334 | 0 | (sizeof(key_value_pair) + sizeof(index_t))>; |
335 | 0 | return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ? |
336 | 0 | min::value : BOOST_JSON_MAX_STRUCTURED_SIZE; |
337 | 0 | } |
338 | | |
339 | | auto |
340 | | object:: |
341 | | capacity() const noexcept -> |
342 | | std::size_t |
343 | 0 | { |
344 | 0 | return t_->capacity; |
345 | 0 | } |
346 | | |
347 | | void |
348 | | object:: |
349 | | reserve(std::size_t new_capacity) |
350 | 0 | { |
351 | 0 | if( new_capacity <= capacity() ) |
352 | 0 | return; |
353 | 0 | table* const old_table = reserve_impl(new_capacity); |
354 | 0 | table::deallocate( old_table, sp_ ); |
355 | 0 | } |
356 | | |
357 | | //---------------------------------------------------------- |
358 | | // |
359 | | // Lookup |
360 | | // |
361 | | //---------------------------------------------------------- |
362 | | |
363 | | value& |
364 | | object:: |
365 | | at(string_view key, source_location const& loc) & |
366 | | { |
367 | | auto const& self = *this; |
368 | | return const_cast< value& >( self.at(key, loc) ); |
369 | | } |
370 | | |
371 | | value&& |
372 | | object:: |
373 | | at(string_view key, source_location const& loc) && |
374 | | { |
375 | | return std::move( at(key, loc) ); |
376 | | } |
377 | | |
378 | | //---------------------------------------------------------- |
379 | | |
380 | | template<class P, class> |
381 | | auto |
382 | | object:: |
383 | | insert(P&& p) -> |
384 | | std::pair<iterator, bool> |
385 | | { |
386 | | key_value_pair v( |
387 | | std::forward<P>(p), sp_); |
388 | | return emplace_impl( v.key(), pilfer(v) ); |
389 | | } |
390 | | |
391 | | template<class M> |
392 | | auto |
393 | | object:: |
394 | | insert_or_assign( |
395 | | string_view key, M&& m) -> |
396 | | std::pair<iterator, bool> |
397 | | { |
398 | | std::pair<iterator, bool> result = emplace_impl( |
399 | | key, key, static_cast<M&&>(m) ); |
400 | | if( !result.second ) |
401 | | { |
402 | | value(static_cast<M>(m), sp_).swap( |
403 | | result.first->value()); |
404 | | } |
405 | | return result; |
406 | | } |
407 | | |
408 | | template<class Arg> |
409 | | auto |
410 | | object:: |
411 | | emplace( |
412 | | string_view key, |
413 | | Arg&& arg) -> |
414 | | std::pair<iterator, bool> |
415 | 0 | { |
416 | 0 | return emplace_impl( key, key, static_cast<Arg&&>(arg) ); |
417 | 0 | } Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace<decltype(nullptr)>(boost::core::basic_string_view<char>, decltype(nullptr)&&) Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace<boost::json::value>(boost::core::basic_string_view<char>, boost::json::value&&) |
418 | | |
419 | | //---------------------------------------------------------- |
420 | | // |
421 | | // (private) |
422 | | // |
423 | | //---------------------------------------------------------- |
424 | | |
425 | | template<class InputIt> |
426 | | void |
427 | | object:: |
428 | | construct( |
429 | | InputIt first, |
430 | | InputIt last, |
431 | | std::size_t min_capacity, |
432 | | std::input_iterator_tag) |
433 | | { |
434 | | reserve(min_capacity); |
435 | | revert_construct r(*this); |
436 | | while(first != last) |
437 | | { |
438 | | insert(*first); |
439 | | ++first; |
440 | | } |
441 | | r.commit(); |
442 | | } |
443 | | |
444 | | template<class InputIt> |
445 | | void |
446 | | object:: |
447 | | construct( |
448 | | InputIt first, |
449 | | InputIt last, |
450 | | std::size_t min_capacity, |
451 | | std::forward_iterator_tag) |
452 | | { |
453 | | auto n = static_cast< |
454 | | std::size_t>(std::distance( |
455 | | first, last)); |
456 | | if( n < min_capacity) |
457 | | n = min_capacity; |
458 | | reserve(n); |
459 | | revert_construct r(*this); |
460 | | while(first != last) |
461 | | { |
462 | | insert(*first); |
463 | | ++first; |
464 | | } |
465 | | r.commit(); |
466 | | } |
467 | | |
468 | | template<class InputIt> |
469 | | void |
470 | | 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 | | while(first != last) |
480 | | { |
481 | | insert(*first); |
482 | | ++first; |
483 | | } |
484 | | } |
485 | | |
486 | | template<class InputIt> |
487 | | void |
488 | | object:: |
489 | | insert( |
490 | | InputIt first, |
491 | | InputIt last, |
492 | | std::forward_iterator_tag) |
493 | | { |
494 | | auto const n = |
495 | | static_cast<std::size_t>( |
496 | | std::distance(first, last)); |
497 | | auto const n0 = size(); |
498 | | if(n > max_size() - n0) |
499 | | { |
500 | | BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION; |
501 | | detail::throw_system_error( error::object_too_large, &loc ); |
502 | | } |
503 | | revert_insert r( *this, n0 + n ); |
504 | | while(first != last) |
505 | | { |
506 | | insert(*first); |
507 | | ++first; |
508 | | } |
509 | | r.commit(); |
510 | | } |
511 | | |
512 | | template< class... Args > |
513 | | std::pair<object::iterator, bool> |
514 | | object:: |
515 | | emplace_impl( string_view key, Args&& ... args ) |
516 | 0 | { |
517 | 0 | std::pair<iterator, std::size_t> search_result(nullptr, 0); |
518 | 0 | if( !empty() ) |
519 | 0 | { |
520 | 0 | search_result = detail::find_in_object(*this, key); |
521 | 0 | if( search_result.first ) |
522 | 0 | return { search_result.first, false }; |
523 | 0 | } |
524 | | |
525 | | // we create the new value before reserving, in case it is a reference to |
526 | | // a subobject of the current object |
527 | 0 | key_value_pair kv( static_cast<Args&&>(args)..., sp_ ); |
528 | | // the key might get deallocated too |
529 | 0 | key = kv.key(); |
530 | |
|
531 | 0 | std::size_t const old_capacity = capacity(); |
532 | 0 | reserve(size() + 1); |
533 | 0 | if( (empty() && capacity() > detail::small_object_size_) |
534 | 0 | || (capacity() != old_capacity) ) |
535 | 0 | search_result.second = detail::digest( |
536 | 0 | key.begin(), key.end(), t_->salt); |
537 | |
|
538 | 0 | BOOST_ASSERT( |
539 | 0 | t_->is_small() || |
540 | 0 | (search_result.second == |
541 | 0 | detail::digest(key.begin(), key.end(), t_->salt)) ); |
542 | |
|
543 | 0 | return { insert_impl(pilfer(kv), search_result.second), true }; |
544 | 0 | } Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace_impl<boost::core::basic_string_view<char>&, decltype(nullptr)>(boost::core::basic_string_view<char>, boost::core::basic_string_view<char>&, decltype(nullptr)&&) Unexecuted instantiation: std::__1::pair<boost::json::key_value_pair*, bool> boost::json::object::emplace_impl<boost::core::basic_string_view<char>&, boost::json::value>(boost::core::basic_string_view<char>, boost::core::basic_string_view<char>&, boost::json::value&&) |
545 | | |
546 | | //---------------------------------------------------------- |
547 | | |
548 | | namespace detail { |
549 | | |
550 | | unchecked_object:: |
551 | | ~unchecked_object() |
552 | 14.9k | { |
553 | 14.9k | if(! data_) |
554 | 7.29k | return; |
555 | 7.63k | if(sp_.is_not_shared_and_deallocate_is_trivial()) |
556 | 1.94k | return; |
557 | 5.69k | value* p = data_; |
558 | 5.69k | while(size_--) |
559 | 0 | { |
560 | 0 | p[0].~value(); |
561 | 0 | p[1].~value(); |
562 | 0 | p += 2; |
563 | 0 | } |
564 | 5.69k | } |
565 | | |
566 | | } // detail |
567 | | |
568 | | } // namespace json |
569 | | } // namespace boost |
570 | | |
571 | | #endif |