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