2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
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)
7 // Official repository: https://github.com/boostorg/json
10 #ifndef BOOST_JSON_IMPL_OBJECT_IPP
11 #define BOOST_JSON_IMPL_OBJECT_IPP
13 #include <boost/json/object.hpp>
14 #include <boost/json/detail/digest.hpp>
15 #include <boost/json/detail/except.hpp>
22 #include <type_traits>
26 //----------------------------------------------------------
28 constexpr object::table::table() = default;
30 // empty objects point here
31 BOOST_JSON_REQUIRE_CONST_INIT
32 object::table object::empty_;
36 digest(string_view key) const noexcept
38 BOOST_ASSERT(salt != 0);
39 return detail::digest(
40 key.data(), key.size(), salt);
45 bucket(std::size_t hash) noexcept ->
48 return reinterpret_cast<
49 index_t*>(&(*this)[capacity])[
55 bucket(string_view key) noexcept ->
58 return bucket(digest(key));
65 BOOST_ASSERT(! is_small());
68 reinterpret_cast<index_t*>(
71 capacity * sizeof(index_t));
79 storage_ptr const& sp)
82 alignof(key_value_pair) >=
84 BOOST_ASSERT(capacity > 0);
85 BOOST_ASSERT(capacity <= max_size());
87 if(capacity <= detail::small_object_size_)
91 sizeof(table) + capacity *
92 sizeof(key_value_pair)));
93 p->capacity = static_cast<
94 std::uint32_t>(capacity);
100 sizeof(table) + capacity * (
101 sizeof(key_value_pair) +
103 p->capacity = static_cast<
104 std::uint32_t>(capacity);
113 // VFALCO This would be better if it
114 // was random, but maybe this
116 p->salt = reinterpret_cast<
122 //----------------------------------------------------------
132 //----------------------------------------------------------
144 //----------------------------------------------------------
148 //----------------------------------------------------------
151 object(detail::unchecked_object&& uo)
159 // should already be checked
161 uo.size() <= max_size());
162 t_ = table::allocate(
165 // insert all elements, keeping
166 // the last of any duplicate keys.
168 auto src = uo.release();
169 auto const end = src + 2 * uo.size();
175 access::construct_key_value_pair(
176 dest, pilfer(src[0]), pilfer(src[1]));
178 auto result = find_impl(dest->key());
186 auto& v = *result.first;
187 // don't bother to check if
188 // storage deallocate is trivial
192 static_cast<void*>(&v),
199 access::construct_key_value_pair(
200 dest, pilfer(src[0]), pilfer(src[1]));
202 auto& head = t_->bucket(dest->key());
211 head = static_cast<index_t>(
217 if(v.key() != dest->key())
224 access::next(*dest) =
226 // don't bother to check if
227 // storage deallocate is trivial
231 static_cast<void*>(&v),
236 t_->size = static_cast<
237 index_t>(dest - begin());
243 if(sp_.is_not_shared_and_deallocate_is_trivial())
245 if(t_->capacity == 0)
252 std::size_t min_capacity,
257 reserve(min_capacity);
261 object(object&& other) noexcept
263 , t_(detail::exchange(
274 if(*sp_ == *other.sp_)
276 t_ = detail::exchange(
282 object(other, sp_).swap(*this);
292 reserve(other.size());
293 revert_construct r(*this);
296 for(auto const& v : other)
299 key_value_pair(v, sp_);
305 for(auto const& v : other)
307 // skip duplicate checking
310 auto pv = ::new(end())
311 key_value_pair(v, sp_);
312 access::next(*pv) = head;
321 std::initializer_list<std::pair<
322 string_view, value_ref>> init,
323 std::size_t min_capacity,
328 if( min_capacity < init.size())
329 min_capacity = init.size();
330 reserve(min_capacity);
331 revert_construct r(*this);
336 //----------------------------------------------------------
340 //----------------------------------------------------------
344 operator=(object const& other)
346 object tmp(other, sp_);
348 ::new(this) object(pilfer(tmp));
354 operator=(object&& other)
356 object tmp(std::move(other), sp_);
358 ::new(this) object(pilfer(tmp));
365 std::initializer_list<std::pair<
366 string_view, value_ref>> init)
368 object tmp(init, sp_);
370 ::new(this) object(pilfer(tmp));
374 //----------------------------------------------------------
378 //----------------------------------------------------------
386 if(! sp_.is_not_shared_and_deallocate_is_trivial())
387 destroy(begin(), end());
396 std::initializer_list<std::pair<
397 string_view, value_ref>> init)
399 auto const n0 = size();
400 if(init.size() > max_size() - n0)
401 detail::throw_length_error(
403 BOOST_CURRENT_LOCATION);
404 reserve(n0 + init.size());
405 revert_insert r(*this);
417 ::new(end()) key_value_pair(
419 iv.second.make_value(sp_));
427 auto& head = t_->bucket(iv.first);
433 // VFALCO value_ref should construct
434 // a key_value_pair using placement
435 auto& v = *::new(end())
438 iv.second.make_value(sp_));
439 access::next(v) = head;
440 head = static_cast<index_t>(
446 if(v.key() == iv.first)
459 erase(const_iterator pos) noexcept ->
462 auto p = begin() + (pos - begin());
467 auto const pb = end();
470 // the casts silence warnings
472 static_cast<void*>(p),
473 static_cast<void const*>(pb),
478 remove(t_->bucket(p->key()), *p);
481 auto const pb = end();
484 auto& head = t_->bucket(pb->key());
486 // the casts silence warnings
488 static_cast<void*>(p),
489 static_cast<void const*>(pb),
491 access::next(*p) = head;
493 index_t>(p - begin());
500 erase(string_view key) noexcept ->
514 if(*sp_ == *other.sp_)
516 t_ = detail::exchange(
527 ::new(&other) object(pilfer(temp1));
529 ::new(this) object(pilfer(temp2));
532 //----------------------------------------------------------
536 //----------------------------------------------------------
540 operator[](string_view key) ->
544 emplace(key, nullptr);
545 return result.first->value();
550 count(string_view key) const noexcept ->
553 if(find(key) == end())
560 find(string_view key) noexcept ->
566 find_impl(key).first;
574 find(string_view key) const noexcept ->
580 find_impl(key).first;
589 string_view key) const noexcept
594 key).first != nullptr;
600 string_view key) const noexcept
602 auto const it = find(key);
611 string_view key) noexcept
613 auto const it = find(key);
619 //----------------------------------------------------------
623 //----------------------------------------------------------
628 string_view key) const noexcept ->
633 BOOST_ASSERT(t_->capacity > 0);
639 for(;it != last; ++it)
642 return { nullptr, 0 };
647 result.second = t_->digest(key);
650 while(i != null_index_)
660 result.first = nullptr;
667 pilfered<key_value_pair> p) ->
668 std::pair<iterator, bool>
670 // caller is responsible
671 // for preventing aliasing.
674 find_impl(p.get().key());
676 return { result.first, false };
677 return { insert_impl(
678 p, result.second), true };
684 pilfered<key_value_pair> p,
688 capacity() > size());
691 auto const pv = ::new(end())
698 auto const pv = ::new(end())
700 access::next(*pv) = head;
706 // rehash to at least `n` buckets
709 rehash(std::size_t new_capacity)
712 new_capacity > t_->capacity);
713 auto t = table::allocate(
714 growth(new_capacity),
724 table::deallocate(t_, sp_);
729 // rebuild hash table,
730 // without dup checks
732 index_t i = t_->size;
737 t_->bucket(p->key());
738 access::next(*p) = head;
746 equal(object const& other) const noexcept
748 if(size() != other.size())
750 auto const end_ = other.end();
753 auto it = other.find(e.key());
756 if(it->value() != e.value())
765 std::size_t new_size) const
767 if(new_size > max_size())
768 detail::throw_length_error(
770 BOOST_CURRENT_LOCATION);
771 std::size_t const old = capacity();
772 if(old > max_size() - old / 2)
774 std::size_t const g =
775 old + old / 2; // 1.5x
785 key_value_pair& v) noexcept
787 BOOST_ASSERT(! t_->is_small());
788 auto const i = static_cast<
789 index_t>(&v - begin());
792 head = access::next(v);
796 &access::next((*t_)[head]);
798 pn = &access::next((*t_)[*pn]);
799 *pn = access::next(v);
806 BOOST_ASSERT(t_->capacity > 0);
807 BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
808 destroy(begin(), end());
809 table::deallocate(t_, sp_);
815 key_value_pair* first,
816 key_value_pair* last) noexcept
818 BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
820 (--last)->~key_value_pair();