From 3c5c8cec3f0356a028a4b56ba6cac2256340dab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Wed, 4 May 2022 23:36:34 +0200 Subject: [PATCH] Rewritten hash semi-intrusive containers to improve compilation times and runtime performance. Added experimental "linear_buckets" option. --- doc/intrusive.qbk | 6 + .../intrusive/detail/exception_disposer.hpp | 31 - .../boost/intrusive/detail/hashtable_node.hpp | 276 ++- include/boost/intrusive/hashtable.hpp | 1537 ++++++++++------- include/boost/intrusive/intrusive_fwd.hpp | 2 + include/boost/intrusive/options.hpp | 5 + include/boost/intrusive/priority_compare.hpp | 11 +- include/boost/intrusive/unordered_set.hpp | 41 +- .../boost/intrusive/unordered_set_hook.hpp | 4 +- test/bptr_value.hpp | 4 +- test/int_holder.hpp | 6 - test/itestvalue.hpp | 97 +- test/stateful_value_traits_test.cpp | 4 +- test/test_container.hpp | 11 +- test/unordered_multiset_test.cpp | 53 +- test/unordered_set_test.cpp | 50 +- test/unordered_test.hpp | 171 +- 17 files changed, 1379 insertions(+), 930 deletions(-) diff --git a/doc/intrusive.qbk b/doc/intrusive.qbk index 35712419..25cc5240 100644 --- a/doc/intrusive.qbk +++ b/doc/intrusive.qbk @@ -3886,6 +3886,12 @@ to be inserted in intrusive containers are allocated using `std::vector` or `std [section:release_notes Release Notes] +[section:release_notes_boost_1_80_00 Boost 1.80 Release] + +* Semi-intrusive hash containers rewritten to reduce compilation times and improve runtime performance. + +[endsect] + [section:release_notes_boost_1_79_00 Boost 1.79 Release] * The library now compiles without warnings with GCC's -Wcast-align=strict diff --git a/include/boost/intrusive/detail/exception_disposer.hpp b/include/boost/intrusive/detail/exception_disposer.hpp index 91c5bf3b..0e21faeb 100644 --- a/include/boost/intrusive/detail/exception_disposer.hpp +++ b/include/boost/intrusive/detail/exception_disposer.hpp @@ -52,37 +52,6 @@ class exception_disposer } }; -template -class exception_array_disposer -{ - Container *cont_; - Disposer &disp_; - SizeType &constructed_; - - exception_array_disposer(const exception_array_disposer&); - exception_array_disposer &operator=(const exception_array_disposer&); - - public: - - exception_array_disposer - (Container &cont, Disposer &disp, SizeType &constructed) - : cont_(&cont), disp_(disp), constructed_(constructed) - {} - - BOOST_INTRUSIVE_FORCEINLINE void release() - { cont_ = 0; } - - ~exception_array_disposer() - { - SizeType n = constructed_; - if(cont_){ - while(n--){ - cont_[n].clear_and_dispose(disp_); - } - } - } -}; - } //namespace detail{ } //namespace intrusive{ } //namespace boost{ diff --git a/include/boost/intrusive/detail/hashtable_node.hpp b/include/boost/intrusive/detail/hashtable_node.hpp index 0b86372d..0c753809 100644 --- a/include/boost/intrusive/detail/hashtable_node.hpp +++ b/include/boost/intrusive/detail/hashtable_node.hpp @@ -26,7 +26,10 @@ #include #include #include -#include //make_slist +#include +#include +#include +#include #include #include #include @@ -35,79 +38,43 @@ namespace boost { namespace intrusive { -template -struct bucket_impl : public Slist +template +struct bucket_impl + : public NodeTraits::node { - typedef Slist slist_type; - BOOST_INTRUSIVE_FORCEINLINE bucket_impl() - {} - - BOOST_INTRUSIVE_FORCEINLINE bucket_impl(const bucket_impl &) - {} - - BOOST_INTRUSIVE_FORCEINLINE ~bucket_impl() - { - //This bucket is still being used! - BOOST_INTRUSIVE_INVARIANT_ASSERT(Slist::empty()); - } - - BOOST_INTRUSIVE_FORCEINLINE bucket_impl &operator=(const bucket_impl&) - { - //This bucket is still in use! - BOOST_INTRUSIVE_INVARIANT_ASSERT(Slist::empty()); - return *this; - } -}; + public: + typedef NodeTraits node_traits; -template -struct bucket_traits_impl -{ private: - BOOST_COPYABLE_AND_MOVABLE(bucket_traits_impl) + typedef typename node_traits::node_ptr node_ptr; + typedef typename node_traits::const_node_ptr const_node_ptr; + + typedef detail::common_slist_algorithms algo_t; public: - /// @cond + BOOST_INTRUSIVE_FORCEINLINE bucket_impl() + {} - typedef typename pointer_traits - ::template rebind_pointer - < bucket_impl >::type bucket_ptr; - typedef Slist slist; - typedef typename Slist::size_type size_type; - /// @endcond - - BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl(bucket_ptr buckets, size_type len) - : buckets_(buckets), buckets_len_(len) + BOOST_INTRUSIVE_FORCEINLINE bucket_impl(const bucket_impl &) {} - BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl(const bucket_traits_impl &x) - : buckets_(x.buckets_), buckets_len_(x.buckets_len_) + BOOST_INTRUSIVE_FORCEINLINE ~bucket_impl() {} - BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl(BOOST_RV_REF(bucket_traits_impl) x) - : buckets_(x.buckets_), buckets_len_(x.buckets_len_) - { x.buckets_ = bucket_ptr(); x.buckets_len_ = 0; } + BOOST_INTRUSIVE_FORCEINLINE bucket_impl &operator=(const bucket_impl&) + { return *this; } - BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl& operator=(BOOST_RV_REF(bucket_traits_impl) x) - { - buckets_ = x.buckets_; buckets_len_ = x.buckets_len_; - x.buckets_ = bucket_ptr(); x.buckets_len_ = 0; return *this; - } + BOOST_INTRUSIVE_FORCEINLINE node_ptr get_node_ptr() + { return pointer_traits::pointer_to(*this); } - BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl& operator=(BOOST_COPY_ASSIGN_REF(bucket_traits_impl) x) - { - buckets_ = x.buckets_; buckets_len_ = x.buckets_len_; return *this; - } + BOOST_INTRUSIVE_FORCEINLINE const_node_ptr get_node_ptr() const + { return pointer_traits::pointer_to(*this); } - BOOST_INTRUSIVE_FORCEINLINE bucket_ptr bucket_begin() const - { return buckets_; } + BOOST_INTRUSIVE_FORCEINLINE node_ptr begin_ptr() + { return node_traits::get_next(get_node_ptr()); } +}; - BOOST_INTRUSIVE_FORCEINLINE size_type bucket_count() const BOOST_NOEXCEPT - { return buckets_len_; } - private: - bucket_ptr buckets_; - size_type buckets_len_; -}; template struct hash_reduced_slist_node_traits @@ -133,22 +100,7 @@ struct reduced_slist_node_traits >::type type; }; -template -struct get_slist_impl -{ - typedef trivial_value_traits trivial_traits; - - //Reducing symbol length - struct type : make_slist - < typename NodeTraits::node - , boost::intrusive::value_traits - , boost::intrusive::constant_time_size - , boost::intrusive::size_type - >::type - {}; -}; - -template +template class hashtable_iterator { typedef typename BucketValueTraits::value_traits value_traits; @@ -166,24 +118,25 @@ class hashtable_iterator private: typedef typename value_traits::node_traits node_traits; typedef typename node_traits::node_ptr node_ptr; - typedef typename get_slist_impl - < typename reduced_slist_node_traits - ::type >::type slist_impl; - typedef typename slist_impl::iterator siterator; - typedef typename slist_impl::const_iterator const_siterator; - typedef bucket_impl bucket_type; + typedef typename BucketValueTraits::bucket_type bucket_type; + typedef typename bucket_type::node_traits slist_node_traits; + typedef typename slist_node_traits::node_ptr slist_node_ptr; + typedef trivial_value_traits + slist_value_traits; + typedef slist_iterator siterator; + typedef slist_iterator const_siterator; + typedef circular_slist_algorithms slist_node_algorithms; typedef typename pointer_traits ::template rebind_pointer < const BucketValueTraits >::type const_bucketvaltraits_ptr; - typedef typename slist_impl::size_type size_type; class nat; typedef typename detail::if_c< IsConst - , hashtable_iterator + , hashtable_iterator , nat>::type nonconst_iterator; - BOOST_INTRUSIVE_FORCEINLINE static node_ptr downcast_bucket(typename bucket_type::node_ptr p) + BOOST_INTRUSIVE_FORCEINLINE static node_ptr downcast_bucket(typename bucket_type::node_traits::node_ptr p) { return pointer_traits:: pointer_to(static_cast(*p)); @@ -211,8 +164,8 @@ class hashtable_iterator BOOST_INTRUSIVE_FORCEINLINE const siterator &slist_it() const { return slist_it_; } - BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator unconst() const - { return hashtable_iterator(this->slist_it(), this->get_bucket_value_traits()); } + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator unconst() const + { return hashtable_iterator(this->slist_it(), this->get_bucket_value_traits()); } BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator& operator++() { this->increment(); return *this; } @@ -252,33 +205,36 @@ class hashtable_iterator { return traitsptr_->priv_bucket_traits(); } private: + void increment() { const bucket_traits &rbuck_traits = this->priv_bucket_traits(); bucket_type* const buckets = boost::movelib::to_raw_pointer(rbuck_traits.bucket_begin()); - const size_type buckets_len = rbuck_traits.bucket_count(); + const std::size_t buckets_len = rbuck_traits.bucket_count(); ++slist_it_; - const typename slist_impl::node_ptr n = slist_it_.pointed_node(); - const siterator first_bucket_bbegin = buckets->end(); - if(first_bucket_bbegin.pointed_node() <= n && n <= buckets[buckets_len-1].cend().pointed_node()){ + const slist_node_ptr n = slist_it_.pointed_node(); + const siterator first_bucket_bbegin(buckets->get_node_ptr()); + if(first_bucket_bbegin.pointed_node() <= n && n <= buckets[buckets_len-1].get_node_ptr()){ //If one-past the node is inside the bucket then look for the next non-empty bucket //1. get the bucket_impl from the iterator - const bucket_type &b = static_cast - (bucket_type::slist_type::container_from_end_iterator(slist_it_)); + const bucket_type &b = static_cast(*n); //2. Now just calculate the index b has in the bucket array - size_type n_bucket = static_cast(&b - buckets); + std::size_t n_bucket = static_cast(&b - buckets); //3. Iterate until a non-empty bucket is found + slist_node_ptr bucket_nodeptr = buckets->get_node_ptr(); do{ if (++n_bucket >= buckets_len){ //bucket overflow, return end() iterator - slist_it_ = buckets->before_begin(); + slist_it_ = first_bucket_bbegin; return; } + bucket_nodeptr = buckets[n_bucket].get_node_ptr(); } - while (buckets[n_bucket].empty()); - slist_it_ = buckets[n_bucket].begin(); + while (slist_node_algorithms::is_empty(bucket_nodeptr)); + slist_it_ = siterator(bucket_nodeptr); + ++slist_it_; } else{ //++slist_it_ yield to a valid object @@ -289,6 +245,134 @@ class hashtable_iterator const_bucketvaltraits_ptr traitsptr_; }; +template +class hashtable_iterator +{ + typedef typename BucketValueTraits::value_traits value_traits; + typedef typename BucketValueTraits::bucket_traits bucket_traits; + + typedef iiterator< value_traits, IsConst + , std::forward_iterator_tag> types_t; + public: + typedef typename types_t::iterator_type::difference_type difference_type; + typedef typename types_t::iterator_type::value_type value_type; + typedef typename types_t::iterator_type::pointer pointer; + typedef typename types_t::iterator_type::reference reference; + typedef typename types_t::iterator_type::iterator_category iterator_category; + + private: + typedef typename value_traits::node_traits node_traits; + typedef typename node_traits::node_ptr node_ptr; + typedef typename BucketValueTraits::bucket_type bucket_type; + typedef typename BucketValueTraits::bucket_ptr bucket_ptr; + typedef typename bucket_type::node_traits slist_node_traits; + typedef linear_slist_algorithms slist_node_algorithms; + typedef typename slist_node_traits::node_ptr slist_node_ptr; + typedef trivial_value_traits + slist_value_traits; + typedef slist_iterator siterator; + typedef slist_iterator const_siterator; + + static const bool stateful_value_traits = + detail::is_stateful_value_traits::value; + + typedef typename pointer_traits + ::template rebind_pointer + < const value_traits >::type const_value_traits_ptr; + class nat; + typedef typename + detail::if_c< IsConst + , hashtable_iterator + , nat>::type nonconst_iterator; + + BOOST_INTRUSIVE_FORCEINLINE static node_ptr downcast_bucket(slist_node_ptr p) + { + return pointer_traits:: + pointer_to(static_cast(*p)); + } + + public: + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator () + : slist_it_() //Value initialization to achieve "null iterators" (N3644) + , members_() + {} + + BOOST_INTRUSIVE_FORCEINLINE explicit hashtable_iterator(siterator ptr, bucket_ptr bp, const_value_traits_ptr traits_ptr) + : slist_it_ (ptr) + , members_ (bp, traits_ptr) + {} + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator(const hashtable_iterator &other) + : slist_it_(other.slist_it()), members_(other.get_bucket_ptr(), other.get_value_traits()) + {} + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator(const nonconst_iterator &other) + : slist_it_(other.slist_it()), members_(other.get_bucket_ptr(), other.get_value_traits()) + {} + + BOOST_INTRUSIVE_FORCEINLINE const siterator &slist_it() const + { return slist_it_; } + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator unconst() const + { return hashtable_iterator(this->slist_it(), members_.nodeptr_, members_.get_ptr()); } + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator& operator++() + { this->increment(); return *this; } + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator &operator=(const hashtable_iterator &other) + { slist_it_ = other.slist_it(); members_ = other.members_; return *this; } + + BOOST_INTRUSIVE_FORCEINLINE hashtable_iterator operator++(int) + { + hashtable_iterator result (*this); + this->increment(); + return result; + } + + BOOST_INTRUSIVE_FORCEINLINE friend bool operator== (const hashtable_iterator& i, const hashtable_iterator& i2) + { return i.slist_it_ == i2.slist_it_; } + + BOOST_INTRUSIVE_FORCEINLINE friend bool operator!= (const hashtable_iterator& i, const hashtable_iterator& i2) + { return !(i == i2); } + + BOOST_INTRUSIVE_FORCEINLINE reference operator*() const + { return *this->operator ->(); } + + BOOST_INTRUSIVE_FORCEINLINE pointer operator->() const + { return this->operator_arrow(detail::bool_()); } + + BOOST_INTRUSIVE_FORCEINLINE const_value_traits_ptr get_value_traits() const + { return members_.get_ptr(); } + + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr get_bucket_ptr() const + { return members_.nodeptr_; } + + private: + + BOOST_INTRUSIVE_FORCEINLINE pointer operator_arrow(detail::false_) const + { return value_traits::to_value_ptr(downcast_bucket(slist_it_.pointed_node())); } + + BOOST_INTRUSIVE_FORCEINLINE pointer operator_arrow(detail::true_) const + { return this->get_value_traits()->to_value_ptr(downcast_bucket(slist_it_.pointed_node())); } + + void increment() + { + ++slist_it_; + if (slist_it_ == siterator()){ + slist_node_ptr bucket_nodeptr; + do { + ++members_.nodeptr_; + bucket_nodeptr = members_.nodeptr_->get_node_ptr(); + }while(slist_node_algorithms::is_empty(bucket_nodeptr)); + slist_it_ = siterator(slist_node_traits::get_next(bucket_nodeptr)); + } + } + + siterator slist_it_; + iiterator_members members_; +}; + } //namespace intrusive { } //namespace boost { diff --git a/include/boost/intrusive/hashtable.hpp b/include/boost/intrusive/hashtable.hpp index e2d0c9eb..3066c5f6 100644 --- a/include/boost/intrusive/hashtable.hpp +++ b/include/boost/intrusive/hashtable.hpp @@ -27,12 +27,17 @@ #include #include #include +#include +#include +#include //Implementation utilities #include -#include +#include #include #include +#include +#include //boost #include @@ -40,11 +45,10 @@ #include #include #include +#include //std C++ -#include //std::equal_to #include //std::pair -#include //std::lower_bound, std::upper_bound #include //std::size_t #if defined(BOOST_HAS_PRAGMA_ONCE) @@ -129,39 +133,6 @@ struct prime_list_holder return static_cast(n); } - template //sizeof(SizeType) > sizeof(std::size_t) - static SizeType suggested_upper_bucket_count_dispatch(SizeType n, detail::true_) - { - std::size_t const c = n > std::size_t(-1) - ? std::size_t(-1) - : suggested_upper_bucket_count_impl(static_cast(n)); - return static_cast(c); - } - - template //sizeof(SizeType) > sizeof(std::size_t) - static SizeType suggested_lower_bucket_count_dispatch(SizeType n, detail::true_) - { - std::size_t const c = n > std::size_t(-1) - ? std::size_t(-1) - : suggested_lower_bucket_count_impl(static_cast(n)); - return static_cast(c); - } - - template - static SizeType suggested_upper_bucket_count_dispatch(SizeType n, detail::false_) - { - std::size_t const c = suggested_upper_bucket_count_impl(static_cast(n)); - return truncate_size_type(c, detail::bool_<(sizeof(SizeType) < sizeof(std::size_t))>()); - - } - - template - static SizeType suggested_lower_bucket_count_dispatch(SizeType n, detail::false_) - { - std::size_t const c = suggested_lower_bucket_count_impl(static_cast(n)); - return truncate_size_type(c, detail::bool_<(sizeof(SizeType) < sizeof(std::size_t))>()); - } - static const std::size_t prime_list[]; static const std::size_t prime_list_size; @@ -169,10 +140,9 @@ struct prime_list_holder { const std::size_t *primes = &prime_list_holder<0>::prime_list[0]; const std::size_t *primes_end = primes + prime_list_holder<0>::prime_list_size; - std::size_t const* bound = std::lower_bound(primes, primes_end, n); - //Tables have upper SIZE_MAX, so we must always found an entry - BOOST_INTRUSIVE_INVARIANT_ASSERT(bound != primes_end); - bound -= std::size_t(bound != primes); + std::size_t const* bound = + boost::movelib::lower_bound(primes, primes_end, n, value_less()); + bound -= std::size_t(bound != primes_end); return *bound; } @@ -180,7 +150,8 @@ struct prime_list_holder { const std::size_t *primes = &prime_list_holder<0>::prime_list[0]; const std::size_t *primes_end = primes + prime_list_holder<0>::prime_list_size; - std::size_t const* bound = std::upper_bound(primes, primes_end, n); + std::size_t const* bound = + boost::movelib::upper_bound(primes, primes_end, n, value_less()); bound -= std::size_t(bound == primes_end); return *bound; } @@ -190,13 +161,15 @@ struct prime_list_holder template static BOOST_INTRUSIVE_FORCEINLINE SizeType suggested_upper_bucket_count(SizeType n) { - return (suggested_upper_bucket_count_dispatch)(n, detail::bool_<(sizeof(SizeType) > sizeof(std::size_t))>()); + std::size_t const c = suggested_upper_bucket_count_impl(static_cast(n)); + return truncate_size_type(c, detail::bool_<(sizeof(SizeType) < sizeof(std::size_t))>()); } template static BOOST_INTRUSIVE_FORCEINLINE SizeType suggested_lower_bucket_count(SizeType n) { - return (suggested_lower_bucket_count_dispatch)(n, detail::bool_<(sizeof(SizeType) > sizeof(std::size_t))>()); + std::size_t const c = suggested_lower_bucket_count_impl(static_cast(n)); + return truncate_size_type(c, detail::bool_<(sizeof(SizeType) < sizeof(std::size_t))>()); } }; @@ -228,9 +201,10 @@ const std::size_t prime_list_holder::prime_list[] = { BOOST_INTRUSIVE_PRIME_C(25165843), BOOST_INTRUSIVE_PRIME_C(50331653), BOOST_INTRUSIVE_PRIME_C(100663319), BOOST_INTRUSIVE_PRIME_C(201326611), BOOST_INTRUSIVE_PRIME_C(402653189), BOOST_INTRUSIVE_PRIME_C(805306457), - BOOST_INTRUSIVE_PRIME_C(1610612741), BOOST_INTRUSIVE_PRIME_C(3221225473), + BOOST_INTRUSIVE_PRIME_C(1610612741), #if BOOST_INTRUSIVE_64_BIT_SIZE_T - //Taken from Boost.MultiIndex code, thanks to Joaquin M Lopez Munoz. + //Taken from Boost.MultiIndex code, thanks to Joaquin M. Lopez Munoz. + BOOST_INTRUSIVE_PRIME_C(3221225473), BOOST_INTRUSIVE_PRIME_C(6442450939), BOOST_INTRUSIVE_PRIME_C(12884901893), BOOST_INTRUSIVE_PRIME_C(25769803751), BOOST_INTRUSIVE_PRIME_C(51539607551), BOOST_INTRUSIVE_PRIME_C(103079215111), BOOST_INTRUSIVE_PRIME_C(206158430209), @@ -246,10 +220,9 @@ const std::size_t prime_list_holder::prime_list[] = { BOOST_INTRUSIVE_PRIME_C(108086391056891903), BOOST_INTRUSIVE_PRIME_C(216172782113783843), BOOST_INTRUSIVE_PRIME_C(432345564227567621), BOOST_INTRUSIVE_PRIME_C(864691128455135207), BOOST_INTRUSIVE_PRIME_C(1729382256910270481), BOOST_INTRUSIVE_PRIME_C(3458764513820540933), - BOOST_INTRUSIVE_PRIME_C(6917529027641081903), BOOST_INTRUSIVE_PRIME_C(13835058055282163729), - BOOST_INTRUSIVE_PRIME_C(18446744073709551557), BOOST_INTRUSIVE_PRIME_C(18446744073709551615) //Upper limit, just in case + BOOST_INTRUSIVE_PRIME_C(6917529027641081903), BOOST_INTRUSIVE_PRIME_C(9223372036854775783) #else - BOOST_INTRUSIVE_PRIME_C(4294967291), BOOST_INTRUSIVE_PRIME_C(4294967295) //Upper limit, just in case + BOOST_INTRUSIVE_PRIME_C(2147483647) #endif }; @@ -270,69 +243,157 @@ struct hash_bool_flags static const std::size_t cache_begin_pos = 8u; static const std::size_t compare_hash_pos = 16u; static const std::size_t incremental_pos = 32u; + static const std::size_t linear_buckets_pos = 64u; }; -namespace detail { - -template -struct get_slist_impl_from_supposed_value_traits +template +class exception_bucket_disposer { - typedef SupposedValueTraits value_traits; - typedef typename detail::get_node_traits - ::type node_traits; - typedef typename get_slist_impl - ::type - >::type type; + Bucket *cont_; + Disposer &disp_; + const SizeType &constructed_; + + exception_bucket_disposer(const exception_bucket_disposer&); + exception_bucket_disposer &operator=(const exception_bucket_disposer&); + + public: + + exception_bucket_disposer + (Bucket &cont, Disposer &disp, const SizeType &constructed) + : cont_(&cont), disp_(disp), constructed_(constructed) + {} + + BOOST_INTRUSIVE_FORCEINLINE void release() + { cont_ = 0; } + + ~exception_bucket_disposer() + { + SizeType n = constructed_; + if(cont_){ + while(n--){ + Algo::detach_and_dispose(cont_[n].get_node_ptr(), disp_); + } + } + } }; template struct unordered_bucket_impl { - typedef typename - get_slist_impl_from_supposed_value_traits - ::type slist_impl; - typedef bucket_impl implementation_defined; - typedef implementation_defined type; + typedef typename detail::get_node_traits + ::type node_traits; + typedef typename reduced_slist_node_traits + ::type reduced_node_traits; + typedef bucket_impl type; + + typedef typename pointer_traits + + ::template rebind_pointer::type pointer; }; template struct unordered_bucket_ptr_impl { - typedef typename detail::get_node_traits - ::type::node_ptr node_ptr; - typedef typename unordered_bucket_impl - ::type bucket_type; + typedef typename unordered_bucket_impl::pointer type; +}; - typedef typename pointer_traits - ::template rebind_pointer - < bucket_type >::type implementation_defined; - typedef implementation_defined type; + +template +struct bucket_traits_impl +{ +private: + BOOST_COPYABLE_AND_MOVABLE(bucket_traits_impl) + +public: + /// @cond + + typedef BucketPtr bucket_ptr; + typedef SizeType size_type; + + /// @endcond + + BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl(bucket_ptr buckets, size_type len) + : buckets_(buckets), buckets_len_(len) + {} + + BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl(const bucket_traits_impl& x) + : buckets_(x.buckets_), buckets_len_(x.buckets_len_) + {} + + BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl(BOOST_RV_REF(bucket_traits_impl) x) + : buckets_(x.buckets_), buckets_len_(x.buckets_len_) + { + x.buckets_ = bucket_ptr(); x.buckets_len_ = 0u; + } + + BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl& operator=(BOOST_RV_REF(bucket_traits_impl) x) + { + buckets_ = x.buckets_; buckets_len_ = x.buckets_len_; + x.buckets_ = bucket_ptr(); x.buckets_len_ = 0u; return *this; + } + + BOOST_INTRUSIVE_FORCEINLINE bucket_traits_impl& operator=(BOOST_COPY_ASSIGN_REF(bucket_traits_impl) x) + { + buckets_ = x.buckets_; buckets_len_ = x.buckets_len_; return *this; + } + + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr bucket_begin() const + { + return buckets_; + } + + BOOST_INTRUSIVE_FORCEINLINE size_type bucket_count() const BOOST_NOEXCEPT + { + return buckets_len_; + } + +private: + bucket_ptr buckets_; + size_type buckets_len_; }; + template struct store_hash_is_true { template - struct two_or_three {yes_type _[2u + (unsigned)Add];}; - template static yes_type test(...); + struct two_or_three {detail::yes_type _[2u + (unsigned)Add];}; + template static detail::yes_type test(...); template static two_or_three test (int); - static const bool value = sizeof(test(0)) > sizeof(yes_type)*2u; + static const bool value = sizeof(test(0)) > sizeof(detail::yes_type)*2u; }; template struct optimize_multikey_is_true { template - struct two_or_three {yes_type _[2u + (unsigned)Add];}; - template static yes_type test(...); + struct two_or_three { detail::yes_type _[2u + (unsigned)Add];}; + template static detail::yes_type test(...); template static two_or_three test (int); - static const bool value = sizeof(test(0)) > sizeof(yes_type)*2u; + static const bool value = sizeof(test(0)) > sizeof(detail::yes_type)*2u; }; +template struct insert_commit_data_impl { std::size_t hash; + std::size_t bucket_idx; + BOOST_INTRUSIVE_FORCEINLINE std::size_t get_hash() const + { return hash; } + + BOOST_INTRUSIVE_FORCEINLINE void set_hash(std::size_t h) + { hash = h; } +}; + +template<> +struct insert_commit_data_impl +{ + std::size_t bucket_idx; + BOOST_INTRUSIVE_FORCEINLINE std::size_t get_hash() const + { return 0U; } + + BOOST_INTRUSIVE_FORCEINLINE void set_hash(std::size_t) + {} }; template @@ -375,21 +436,22 @@ struct group_functions typedef circular_slist_algorithms node_algorithms; static slist_node_ptr get_bucket_before_begin - (slist_node_ptr bucket_beg, slist_node_ptr bucket_end, node_ptr p) + (slist_node_ptr bucket_beg, slist_node_ptr bucket_last, slist_node_ptr sp, detail::true_) { //First find the last node of p's group. //This requires checking the first node of the next group or //the bucket node. + node_ptr p = dcast_bucket_ptr(sp); node_ptr prev_node = p; node_ptr nxt(node_traits::get_next(p)); - while(!(bucket_beg <= nxt && nxt <= bucket_end) && + while(!(bucket_beg <= nxt && nxt <= bucket_last) && (group_traits::get_next(nxt) == prev_node)){ prev_node = nxt; nxt = node_traits::get_next(nxt); } //If we've reached the bucket node just return it. - if(bucket_beg <= nxt && nxt <= bucket_end){ + if(bucket_beg <= nxt && nxt <= bucket_last){ return nxt; } @@ -398,17 +460,28 @@ struct group_functions node_ptr last_node_group = group_traits::get_next(first_node_of_group); slist_node_ptr possible_end = node_traits::get_next(last_node_group); - while(!(bucket_beg <= possible_end && possible_end <= bucket_end)){ - first_node_of_group = detail::dcast_bucket_ptr(possible_end); + while(!(bucket_beg <= possible_end && possible_end <= bucket_last)){ + first_node_of_group = dcast_bucket_ptr(possible_end); last_node_group = group_traits::get_next(first_node_of_group); possible_end = node_traits::get_next(last_node_group); } return possible_end; } + static slist_node_ptr get_bucket_before_begin + (slist_node_ptr bucket_beg, slist_node_ptr bucket_last, slist_node_ptr sp, detail::false_) + { + //The end node is embedded in the singly linked list: + //iterate until we reach it. + while (!(bucket_beg <= sp && sp <= bucket_last)){ + sp = reduced_node_traits::get_next(sp); + } + return sp; + } + static node_ptr get_prev_to_first_in_group(slist_node_ptr bucket_node, node_ptr first_in_group) { - node_ptr nb = detail::dcast_bucket_ptr(bucket_node); + node_ptr nb = dcast_bucket_ptr(bucket_node); node_ptr n; while((n = node_traits::get_next(nb)) != first_in_group){ nb = group_traits::get_next(n); //go to last in group @@ -444,31 +517,35 @@ struct group_functions return n; } - BOOST_INTRUSIVE_FORCEINLINE static node_ptr next_group_if_first_in_group(node_ptr ptr) - { return node_traits::get_next(group_traits::get_next(ptr)); } - BOOST_INTRUSIVE_FORCEINLINE static node_ptr get_first_in_group(node_ptr n, detail::false_) { return n; } - BOOST_INTRUSIVE_FORCEINLINE static void insert_in_group(node_ptr first_in_group, node_ptr n, true_) + BOOST_INTRUSIVE_FORCEINLINE static bool is_first_in_group(node_ptr ptr) + { return node_traits::get_next(group_traits::get_next(ptr)) != ptr; } + + + BOOST_INTRUSIVE_FORCEINLINE static void insert_in_group(node_ptr first_in_group, node_ptr n, detail::true_) { group_algorithms::link_after(first_in_group, n); } - static void insert_in_group(node_ptr, node_ptr, false_) + BOOST_INTRUSIVE_FORCEINLINE static void insert_in_group(node_ptr, node_ptr, detail::false_) {} - static node_ptr split_group(node_ptr const new_first_in_group) + //Splits a group in two groups, and makes "new_first" the first node in the second group. + //Returns the first element of the first group + static node_ptr split_group(node_ptr const new_first) { - node_ptr const first((get_first_in_group)(new_first_in_group, detail::true_())); - if(first != new_first_in_group){ - node_ptr const last = group_traits::get_next(first); - group_traits::set_next(first, group_traits::get_next(new_first_in_group)); - group_traits::set_next(new_first_in_group, last); + node_ptr const old_first((get_first_in_group)(new_first, detail::true_())); + //Check new_first was not the first in group + if(old_first != new_first){ + node_ptr const last = group_traits::get_next(old_first); + group_traits::set_next(old_first, group_traits::get_next(new_first)); + group_traits::set_next(new_first, last); } - return first; + return old_first; } }; -template +template class incremental_rehash_rollback { private: @@ -494,7 +571,7 @@ class incremental_rehash_rollback if(!released_){ //If an exception is thrown, just put all moved nodes back in the old bucket //and move back the split mark. - destiny_bucket_.splice_after(destiny_bucket_.before_begin(), source_bucket_); + SlistNodeAlgorithms::transfer_after(destiny_bucket_.get_node_ptr(), source_bucket_.get_node_ptr()); split_traits_.decrement(); } } @@ -509,10 +586,10 @@ class incremental_rehash_rollback template struct node_functions { - BOOST_INTRUSIVE_FORCEINLINE static void store_hash(typename NodeTraits::node_ptr p, std::size_t h, true_) + BOOST_INTRUSIVE_FORCEINLINE static void store_hash(typename NodeTraits::node_ptr p, std::size_t h, detail::true_) { return NodeTraits::set_hash(p, h); } - BOOST_INTRUSIVE_FORCEINLINE static void store_hash(typename NodeTraits::node_ptr, std::size_t, false_) + BOOST_INTRUSIVE_FORCEINLINE static void store_hash(typename NodeTraits::node_ptr, std::size_t, detail::false_) {} }; @@ -523,25 +600,22 @@ BOOST_INTRUSIVE_FORCEINLINE std::size_t hash_to_bucket(std::size_t hash_value, s { return hash_value & (bucket_cnt - 1); } template -std::size_t hash_to_bucket_split(std::size_t hash_value, std::size_t bucket_cnt, std::size_t split) +BOOST_INTRUSIVE_FORCEINLINE std::size_t hash_to_bucket_split(std::size_t hash_value, std::size_t bucket_cnt, std::size_t split) { - std::size_t bucket_number = detail::hash_to_bucket(hash_value, bucket_cnt, detail::bool_()); + std::size_t bucket_number = hash_to_bucket(hash_value, bucket_cnt, detail::bool_()); BOOST_IF_CONSTEXPR(Incremental) bucket_number -= static_cast(bucket_number >= split)*(bucket_cnt/2); return bucket_number; } -} //namespace detail { - //!This metafunction will obtain the type of a bucket //!from the value_traits or hook option to be used with //!a hash container. template struct unordered_bucket - : public detail::unordered_bucket_impl - ::proto_value_traits - > + : public unordered_bucket_impl + < typename ValueTraitsOrHookOption:: + template pack::proto_value_traits> {}; //!This metafunction will obtain the type of a bucket pointer @@ -549,10 +623,9 @@ struct unordered_bucket //!a hash container. template struct unordered_bucket_ptr - : public detail::unordered_bucket_ptr_impl - ::proto_value_traits - > + : public unordered_bucket_ptr_impl + < typename ValueTraitsOrHookOption:: + template pack::proto_value_traits> {}; //!This metafunction will obtain the type of the default bucket traits @@ -563,13 +636,12 @@ template struct unordered_default_bucket_traits { typedef typename ValueTraitsOrHookOption:: - template pack::proto_value_traits supposed_value_traits; - typedef typename detail:: - get_slist_impl_from_supposed_value_traits - ::type slist_impl; + template pack::proto_value_traits supposed_value_traits; + typedef bucket_traits_impl - implementation_defined; - typedef implementation_defined type; + < typename unordered_bucket_ptr_impl + ::type + , std::size_t> type; }; struct default_bucket_traits; @@ -595,24 +667,23 @@ struct hashtable_defaults static const bool cache_begin = false; static const bool compare_hash = false; static const bool incremental = false; + static const bool linear_buckets = false; }; template struct downcast_node_to_value_t : public detail::node_to_value { - typedef detail::node_to_value base_t; - typedef typename base_t::result_type result_type; - typedef ValueTraits value_traits; - typedef typename get_slist_impl - ::type - >::type slist_impl; + typedef detail::node_to_value base_t; + typedef typename base_t::result_type result_type; + typedef ValueTraits value_traits; + typedef typename unordered_bucket_impl + ::type::node_traits::node node; typedef typename detail::add_const_if_c - ::type & first_argument_type; + ::type &first_argument_type; typedef typename detail::add_const_if_c < typename ValueTraits::node_traits::node - , IsConst>::type & intermediate_argument_type; + , IsConst>::type &intermediate_argument_type; typedef typename pointer_traits :: template rebind_pointer @@ -653,7 +724,7 @@ struct node_cast_adaptor //bucket_plus_vtraits stores ValueTraits + BucketTraits //this data is needed by iterators to obtain the //value from the iterator and detect the bucket -template +template struct bucket_plus_vtraits { typedef BucketTraits bucket_traits; @@ -661,17 +732,28 @@ struct bucket_plus_vtraits static const bool safemode_or_autounlink = is_safe_autounlink::value; - typedef typename - detail::get_slist_impl_from_supposed_value_traits - ::type slist_impl; + typedef typename unordered_bucket_impl + ::type bucket_type; + typedef typename unordered_bucket_ptr_impl + ::type bucket_ptr; typedef typename value_traits::node_traits node_traits; + typedef typename bucket_type::node_traits slist_node_traits; typedef unordered_group_adapter group_traits; - typedef typename slist_impl::iterator siterator; - typedef bucket_impl bucket_type; - typedef detail::group_functions group_functions_t; - typedef typename slist_impl::node_algorithms node_algorithms; - typedef typename slist_impl::node_ptr slist_node_ptr; - typedef typename node_traits::node_ptr node_ptr; + typedef group_functions group_functions_t; + typedef typename detail::if_c + < LinearBuckets + , linear_slist_algorithms + , circular_slist_algorithms + >::type slist_node_algorithms; + + typedef typename slist_node_traits::node_ptr slist_node_ptr; + typedef trivial_value_traits + slist_value_traits; + typedef slist_iterator siterator; + typedef slist_iterator const_siterator; + + typedef typename node_traits::node_ptr node_ptr; + typedef typename node_traits::const_node_ptr const_node_ptr; typedef typename node_traits::node node; typedef typename value_traits::value_type value_type; typedef typename value_traits::pointer pointer; @@ -688,8 +770,9 @@ struct bucket_plus_vtraits :: template rebind_pointer ::type const_bucket_value_traits_ptr; - typedef typename detail::unordered_bucket_ptr_impl - ::type bucket_ptr; + typedef detail::bool_ linear_buckets_t; + + static const std::size_t bucket_overhead = LinearBuckets ? 1u : 0u; template BOOST_INTRUSIVE_FORCEINLINE bucket_plus_vtraits(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits) @@ -731,41 +814,103 @@ struct bucket_plus_vtraits //bucket operations BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_bucket_pointer() const BOOST_NOEXCEPT - { return this->priv_bucket_traits().bucket_begin(); } + { return this->data.bucket_traits_.bucket_begin(); } - std::size_t priv_bucket_count() const BOOST_NOEXCEPT + BOOST_INTRUSIVE_FORCEINLINE std::size_t priv_bucket_count() const BOOST_NOEXCEPT { - const std::size_t bc = this->priv_bucket_traits().bucket_count(); - return bc; + BOOST_IF_CONSTEXPR(bucket_overhead){ + const std::size_t n = this->data.bucket_traits_.bucket_count(); + return n - std::size_t(n != 0)*bucket_overhead; + } + else{ + return this->data.bucket_traits_.bucket_count(); + } } BOOST_INTRUSIVE_FORCEINLINE bucket_type &priv_bucket(std::size_t n) const BOOST_NOEXCEPT { - BOOST_INTRUSIVE_INVARIANT_ASSERT(n < this->priv_bucket_traits().bucket_count()); - return priv_bucket_pointer()[std::ptrdiff_t(n)]; + BOOST_INTRUSIVE_INVARIANT_ASSERT(n < this->priv_bucket_count()); + return this->priv_bucket_pointer()[std::ptrdiff_t(n)]; } + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_bucket_ptr(std::size_t n) const BOOST_NOEXCEPT + { return pointer_traits::pointer_to(this->priv_bucket(n)); } + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_invalid_bucket() const + { return this->priv_bucket_pointer() + std::ptrdiff_t(priv_bucket_count()); } + + BOOST_INTRUSIVE_FORCEINLINE void priv_set_sentinel_bucket() const { - const bucket_traits &rbt = this->priv_bucket_traits(); - return rbt.bucket_begin() + std::ptrdiff_t(rbt.bucket_count()); + BOOST_IF_CONSTEXPR(LinearBuckets) { + BOOST_INTRUSIVE_INVARIANT_ASSERT(priv_bucket_traits().bucket_count() > 1); + bucket_type &b = this->priv_bucket_pointer()[std::ptrdiff_t(this->priv_bucket_count())]; + slist_node_algorithms::set_sentinel(b.get_node_ptr()); + } + } + + BOOST_INTRUSIVE_FORCEINLINE void priv_unset_sentinel_bucket() const + { + BOOST_IF_CONSTEXPR(LinearBuckets) { + BOOST_INTRUSIVE_INVARIANT_ASSERT(priv_bucket_traits().bucket_count() > 1); + bucket_type& b = this->priv_bucket_pointer()[std::ptrdiff_t(this->priv_bucket_count())]; + slist_node_algorithms::init_header(b.get_node_ptr()); + } } BOOST_INTRUSIVE_FORCEINLINE siterator priv_invalid_local_it() const - { return this->priv_bucket_traits().bucket_begin()->before_begin(); } + { + BOOST_IF_CONSTEXPR(LinearBuckets) + return siterator(this->priv_invalid_bucket()->get_node_ptr()); + else + return siterator(this->priv_bucket_pointer()->get_node_ptr()); + } + + BOOST_INTRUSIVE_FORCEINLINE siterator priv_bucket_lbegin(std::size_t n) const + { siterator s(this->priv_bucket_lbbegin(n)); return ++s; } + + BOOST_INTRUSIVE_FORCEINLINE siterator priv_bucket_lbbegin(std::size_t n) const + { return this->sit_bbegin(this->priv_bucket(n)); } + + BOOST_INTRUSIVE_FORCEINLINE siterator priv_bucket_lend(std::size_t n) const + { return this->sit_end(this->priv_bucket(n)); } + + BOOST_INTRUSIVE_FORCEINLINE std::size_t priv_bucket_size(std::size_t n) const + { return slist_node_algorithms::count(this->priv_bucket(n).get_node_ptr())-1u; } + + BOOST_INTRUSIVE_FORCEINLINE bool priv_bucket_empty(std::size_t n) const + { return slist_node_algorithms::is_empty(this->priv_bucket(n).get_node_ptr()); } + + BOOST_INTRUSIVE_FORCEINLINE bool priv_bucket_empty(bucket_ptr p) const + { return slist_node_algorithms::is_empty(p->get_node_ptr()); } + + static BOOST_INTRUSIVE_FORCEINLINE siterator priv_bucket_lbegin(bucket_type &b) + { return siterator(slist_node_traits::get_next(b.get_node_ptr())); } + + static BOOST_INTRUSIVE_FORCEINLINE siterator priv_bucket_lbbegin(bucket_type& b) + { return siterator(b.get_node_ptr()); } + + static BOOST_INTRUSIVE_FORCEINLINE siterator priv_bucket_lend(bucket_type& b) + { return siterator(slist_node_algorithms::end_node(b.get_node_ptr())); } + + static BOOST_INTRUSIVE_FORCEINLINE std::size_t priv_bucket_size(const bucket_type& b) + { return slist_node_algorithms::count(b.get_node_ptr())-1u; } + + static BOOST_INTRUSIVE_FORCEINLINE bool priv_bucket_empty(const bucket_type& b) + { return slist_node_algorithms::is_empty(b.get_node_ptr()); } template - static std::size_t priv_erase_from_single_bucket(bucket_type &b, siterator sbefore_first, siterator slast, NodeDisposer node_disposer, detail::true_) //optimize multikey + static std::size_t priv_erase_from_single_bucket + (bucket_type &b, siterator sbefore_first, siterator slast, NodeDisposer node_disposer, detail::true_) //optimize multikey { std::size_t n = 0; siterator const sfirst(++siterator(sbefore_first)); if(sfirst != slast){ - node_ptr const nf = detail::dcast_bucket_ptr(sfirst.pointed_node()); - node_ptr const nl = detail::dcast_bucket_ptr(slast.pointed_node()); - node_ptr const ne = detail::dcast_bucket_ptr(b.end().pointed_node()); + node_ptr const nf = dcast_bucket_ptr(sfirst.pointed_node()); + node_ptr const nl = dcast_bucket_ptr(slast.pointed_node()); + slist_node_ptr const ne = (priv_bucket_lend(b)).pointed_node(); - if(group_functions_t::next_group_if_first_in_group(nf) != nf) { - // The node is at the beginning of a group. + if(group_functions_t::is_first_in_group(nf)) { + // The first node is at the beginning of a group. if(nl != ne){ group_functions_t::split_group(nl); } @@ -773,52 +918,42 @@ struct bucket_plus_vtraits else { node_ptr const group1 = group_functions_t::split_group(nf); if(nl != ne) { - node_ptr const group2 = group_functions_t::split_group(ne); + node_ptr const group2 = group_functions_t::split_group(nl); if(nf == group2) { //Both first and last in the same group //so join group1 and group2 node_ptr const end1 = group_traits::get_next(group1); node_ptr const end2 = group_traits::get_next(group2); group_traits::set_next(group1, end2); - group_traits::set_next(group2, end1); + group_traits::set_next(nl, end1); } } } - siterator it(++siterator(sbefore_first)); - while(it != slast){ - node_disposer((it++).pointed_node()); - ++n; - } - b.erase_after(sbefore_first, slast); + n = slist_node_algorithms::unlink_after_and_dispose(sbefore_first.pointed_node(), slast.pointed_node(), node_disposer); } return n; } template - static std::size_t priv_erase_from_single_bucket(bucket_type &b, siterator sbefore_first, siterator slast, NodeDisposer node_disposer, detail::false_) //optimize multikey + static std::size_t priv_erase_from_single_bucket + (bucket_type &, siterator sbefore_first, siterator slast, NodeDisposer node_disposer, detail::false_) //optimize multikey { - std::size_t n = 0; - siterator it(++siterator(sbefore_first)); - while(it != slast){ - node_disposer((it++).pointed_node()); - ++n; - } - b.erase_after(sbefore_first, slast); - return n; + return slist_node_algorithms::unlink_after_and_dispose(sbefore_first.pointed_node(), slast.pointed_node(), node_disposer); } template static void priv_erase_node(bucket_type &b, siterator i, NodeDisposer node_disposer, detail::true_) //optimize multikey { - node_ptr const ne(detail::dcast_bucket_ptr(b.end().pointed_node())); - node_ptr n(detail::dcast_bucket_ptr(i.pointed_node())); + slist_node_ptr const ne(priv_bucket_lend(b).pointed_node()); + slist_node_ptr const nbb(priv_bucket_lbbegin(b).pointed_node()); + node_ptr n(dcast_bucket_ptr(i.pointed_node())); node_ptr pos = node_traits::get_next(group_traits::get_next(n)); node_ptr bn; node_ptr nn(node_traits::get_next(n)); if(pos != n) { //Node is the first of the group - bn = group_functions_t::get_prev_to_first_in_group(ne, n); + bn = group_functions_t::get_prev_to_first_in_group(nbb, n); //Unlink the rest of the group if it's not the last node of its group if(nn != ne && group_traits::get_next(nn) == n){ @@ -836,12 +971,15 @@ struct bucket_plus_vtraits node_ptr const x(group_algorithms::get_previous_node(n)); group_algorithms::unlink_after(x); } - b.erase_after_and_dispose(bucket_type::s_iterator_to(*bn), node_disposer); + slist_node_algorithms::unlink_after_and_dispose(bn, node_disposer); } template - BOOST_INTRUSIVE_FORCEINLINE static void priv_erase_node(bucket_type &b, siterator i, NodeDisposer node_disposer, detail::false_) //optimize multikey - { b.erase_after_and_dispose(b.previous(i), node_disposer); } + BOOST_INTRUSIVE_FORCEINLINE static void priv_erase_node(bucket_type &b, siterator i, NodeDisposer node_disposer, detail::false_) //!optimize multikey + { + slist_node_ptr bi = slist_node_algorithms::get_previous_node(b.get_node_ptr(), i.pointed_node()); + slist_node_algorithms::unlink_after_and_dispose(bi, node_disposer); + } template std::size_t priv_erase_node_range( siterator const &before_first_it, std::size_t const first_bucket @@ -853,11 +991,11 @@ struct bucket_plus_vtraits if(first_bucket != last_bucket){ bucket_type *b = &this->priv_bucket(0); num_erased += this->priv_erase_from_single_bucket - (b[first_bucket], before_first_it, b[first_bucket].end(), node_disposer, optimize_multikey_tag); + (b[first_bucket], before_first_it, this->priv_bucket_lend(first_bucket), node_disposer, optimize_multikey_tag); for(std::size_t i = 0, n = (last_bucket - first_bucket - 1); i != n; ++i){ num_erased += this->priv_erase_whole_bucket(b[first_bucket+i+1], node_disposer); } - last_step_before_it = b[last_bucket].before_begin(); + last_step_before_it = this->priv_bucket_lbbegin(last_bucket); } else{ last_step_before_it = before_first_it; @@ -872,91 +1010,71 @@ struct bucket_plus_vtraits //First find the last node of p's group. //This requires checking the first node of the next group or //the bucket node. - slist_node_ptr end_ptr(b.end().pointed_node()); - node_ptr possible_end(node_traits::get_next( detail::dcast_bucket_ptr(end_ptr))); - node_ptr last_node_group(possible_end); + slist_node_ptr end_ptr(sit_end(b).pointed_node()); + slist_node_ptr last_node_group(b.get_node_ptr()); + slist_node_ptr possible_end(slist_node_traits::get_next(last_node_group)); while(end_ptr != possible_end){ - last_node_group = group_traits::get_next(detail::dcast_bucket_ptr(possible_end)); - possible_end = node_traits::get_next(last_node_group); + last_node_group = group_traits::get_next(dcast_bucket_ptr(possible_end)); + possible_end = slist_node_traits::get_next(last_node_group); } - return bucket_type::s_iterator_to(*last_node_group); + return siterator(last_node_group); } - template - std::size_t priv_erase_whole_bucket(bucket_type &b, NodeDisposer node_disposer) - { - std::size_t num_erased = 0; - siterator b_begin(b.before_begin()); - siterator nxt(b_begin); - ++nxt; - siterator const end_sit(b.end()); - while(nxt != end_sit){ - //No need to init group links as we'll delete all bucket nodes - nxt = bucket_type::s_erase_after_and_dispose(b_begin, node_disposer); - ++num_erased; - } - return num_erased; + BOOST_INTRUSIVE_FORCEINLINE static siterator priv_get_last(bucket_type &b, detail::false_) //NOT optimize multikey + { + slist_node_ptr p = b.get_node_ptr(); + return siterator(slist_node_algorithms::get_previous_node(p, slist_node_algorithms::end_node(p))); } - BOOST_INTRUSIVE_FORCEINLINE static siterator priv_get_last(bucket_type &b, detail::false_) //NOT optimize multikey - { return b.previous(b.end()); } + template + static BOOST_INTRUSIVE_FORCEINLINE std::size_t priv_erase_whole_bucket(bucket_type &b, NodeDisposer node_disposer) + { return slist_node_algorithms::detach_and_dispose(b.get_node_ptr(), node_disposer); } static siterator priv_get_previous(bucket_type &b, siterator i, detail::true_) //optimize multikey { - node_ptr const elem(detail::dcast_bucket_ptr(i.pointed_node())); + node_ptr const elem(dcast_bucket_ptr(i.pointed_node())); node_ptr const prev_in_group(group_traits::get_next(elem)); bool const first_in_group = node_traits::get_next(prev_in_group) != elem; - typename bucket_type::node &n = first_in_group - ? *group_functions_t::get_prev_to_first_in_group(b.end().pointed_node(), elem) - : *group_traits::get_next(elem) + slist_node_ptr n = first_in_group + ? group_functions_t::get_prev_to_first_in_group(b.get_node_ptr(), elem) + : group_traits::get_next(elem) ; - return bucket_type::s_iterator_to(n); + return siterator(n); } BOOST_INTRUSIVE_FORCEINLINE static siterator priv_get_previous(bucket_type &b, siterator i, detail::false_) //NOT optimize multikey - { return b.previous(i); } + { return siterator(slist_node_algorithms::get_previous_node(b.get_node_ptr(), i.pointed_node())); } - std::size_t priv_get_bucket_num_no_hash_store(siterator it, detail::true_) //optimize multikey + template + struct typeof_node_disposer { - const bucket_type &f = this->priv_bucket(0u); - const bucket_type &l = this->priv_bucket(this->priv_bucket_count() - 1u); - slist_node_ptr bb = group_functions_t::get_bucket_before_begin - ( f.end().pointed_node() - , l.end().pointed_node() - , detail::dcast_bucket_ptr(it.pointed_node())); - //Now get the bucket_impl from the iterator - const bucket_type &b = static_cast - (bucket_type::slist_type::container_from_end_iterator(bucket_type::s_iterator_to(*bb))); - //Now just calculate the index b has in the bucket array - return static_cast(&b - &f); - } + typedef node_cast_adaptor + < detail::node_disposer< Disposer, value_traits, CommonSListAlgorithms> + , slist_node_ptr, node_ptr > type; + }; - std::size_t priv_get_bucket_num_no_hash_store(siterator it, detail::false_) //NO optimize multikey + template + BOOST_INTRUSIVE_FORCEINLINE typename typeof_node_disposer::type + make_node_disposer(const Disposer &disposer) const { - const bucket_type &f = this->priv_bucket(0u); - const bucket_type &l = this->priv_bucket(this->priv_bucket_count() - 1u); - slist_node_ptr first_ptr(f.cend().pointed_node()) - , last_ptr (l.cend().pointed_node()); + typedef typename typeof_node_disposer::type return_t; + return return_t(disposer, &this->priv_value_traits()); + } - //The end node is embedded in the singly linked list: - //iterate until we reach it. - while(!(std::less_equal()(first_ptr, it.pointed_node()) && - std::less_equal()(it.pointed_node(), last_ptr))){ - ++it; - } - //Now get the bucket_impl from the iterator - const bucket_type &b = static_cast - (bucket_type::container_from_end_iterator(it)); + static BOOST_INTRUSIVE_FORCEINLINE bucket_ptr to_ptr(bucket_type &b) + { return pointer_traits::pointer_to(b); } - //Now just calculate the index b has in the bucket array - return static_cast(&b - &f); - } + static BOOST_INTRUSIVE_FORCEINLINE siterator sit_bbegin(bucket_type& b) + { return siterator(b.get_node_ptr()); } + + static BOOST_INTRUSIVE_FORCEINLINE siterator sit_end(bucket_type& b) + { return siterator(slist_node_algorithms::end_node(b.get_node_ptr())); } - BOOST_INTRUSIVE_FORCEINLINE static std::size_t priv_stored_hash(slist_node_ptr n, detail::true_) //store_hash - { return node_traits::get_hash(detail::dcast_bucket_ptr(n)); } + BOOST_INTRUSIVE_FORCEINLINE static std::size_t priv_stored_hash(siterator s, detail::true_) //store_hash + { return node_traits::get_hash(dcast_bucket_ptr(s.pointed_node())); } - BOOST_INTRUSIVE_FORCEINLINE static std::size_t priv_stored_hash(slist_node_ptr, detail::false_) //NO store_hash + BOOST_INTRUSIVE_FORCEINLINE static std::size_t priv_stored_hash(siterator, detail::false_) //NO store_hash { return std::size_t(-1); } BOOST_INTRUSIVE_FORCEINLINE node &priv_value_to_node(reference v) @@ -965,21 +1083,36 @@ struct bucket_plus_vtraits BOOST_INTRUSIVE_FORCEINLINE const node &priv_value_to_node(const_reference v) const { return *this->priv_value_traits().to_node_ptr(v); } - BOOST_INTRUSIVE_FORCEINLINE reference priv_value_from_slist_node(slist_node_ptr n) - { return *this->priv_value_traits().to_value_ptr(detail::dcast_bucket_ptr(n)); } + BOOST_INTRUSIVE_FORCEINLINE node_ptr priv_value_to_node_ptr(reference v) + { return this->priv_value_traits().to_node_ptr(v); } + + BOOST_INTRUSIVE_FORCEINLINE const_node_ptr priv_value_to_node_ptr(const_reference v) const + { return this->priv_value_traits().to_node_ptr(v); } + + BOOST_INTRUSIVE_FORCEINLINE reference priv_value_from_siterator(siterator s) + { return *this->priv_value_traits().to_value_ptr(dcast_bucket_ptr(s.pointed_node())); } + + BOOST_INTRUSIVE_FORCEINLINE const_reference priv_value_from_siterator(siterator s) const + { return *this->priv_value_traits().to_value_ptr(dcast_bucket_ptr(s.pointed_node())); } - BOOST_INTRUSIVE_FORCEINLINE const_reference priv_value_from_slist_node(slist_node_ptr n) const - { return *this->priv_value_traits().to_value_ptr(detail::dcast_bucket_ptr(n)); } + static void priv_init_buckets(const bucket_ptr buckets_ptr, const std::size_t bucket_cnt) + { + bucket_ptr buckets_it = buckets_ptr; + for (std::size_t bucket_i = 0; bucket_i != bucket_cnt; ++buckets_it, ++bucket_i) { + slist_node_algorithms::init_header(buckets_it->get_node_ptr()); + } + } void priv_clear_buckets(const bucket_ptr buckets_ptr, const std::size_t bucket_cnt) { bucket_ptr buckets_it = buckets_ptr; for(std::size_t bucket_i = 0; bucket_i != bucket_cnt; ++buckets_it, ++bucket_i){ + bucket_type &b = *buckets_it; BOOST_IF_CONSTEXPR(safemode_or_autounlink){ - buckets_it->clear_and_dispose(detail::init_disposer()); + slist_node_algorithms::detach_and_dispose(b.get_node_ptr(), this->make_node_disposer(detail::null_disposer())); } else{ - buckets_it->clear(); + slist_node_algorithms::init_header(b.get_node_ptr()); } } } @@ -987,17 +1120,17 @@ struct bucket_plus_vtraits BOOST_INTRUSIVE_FORCEINLINE std::size_t priv_stored_or_compute_hash(const value_type &v, detail::true_) const //For store_hash == true { return node_traits::get_hash(this->priv_value_traits().to_node_ptr(v)); } - typedef hashtable_iterator iterator; - typedef hashtable_iterator const_iterator; + typedef hashtable_iterator iterator; + typedef hashtable_iterator const_iterator; BOOST_INTRUSIVE_FORCEINLINE iterator end() BOOST_NOEXCEPT - { return iterator(this->priv_invalid_local_it(), 0); } + { return this->build_iterator(this->priv_invalid_local_it(), this->priv_invalid_bucket()); } BOOST_INTRUSIVE_FORCEINLINE const_iterator end() const BOOST_NOEXCEPT - { return this->cend(); } + { return this->build_const_iterator(this->priv_invalid_local_it(), this->priv_invalid_bucket()); } BOOST_INTRUSIVE_FORCEINLINE const_iterator cend() const BOOST_NOEXCEPT - { return const_iterator(this->priv_invalid_local_it(), 0); } + { return this->build_const_iterator(this->priv_invalid_local_it(), this->priv_invalid_bucket()); } //Public functions: struct data_type : public ValueTraits @@ -1009,6 +1142,24 @@ struct bucket_plus_vtraits bucket_traits bucket_traits_; } data; + + BOOST_INTRUSIVE_FORCEINLINE iterator build_iterator(siterator s, bucket_ptr p) + { return this->build_iterator(s, p, linear_buckets_t()); } + + BOOST_INTRUSIVE_FORCEINLINE iterator build_iterator(siterator s, bucket_ptr p, detail::true_) //linear buckets + { return iterator(s, p, this->priv_value_traits_ptr()); } + + BOOST_INTRUSIVE_FORCEINLINE iterator build_iterator(siterator s, bucket_ptr, detail::false_) //!linear buckets + { return iterator(s, &this->get_bucket_value_traits()); } + + BOOST_INTRUSIVE_FORCEINLINE const_iterator build_const_iterator(siterator s, bucket_ptr p) const + { return this->build_const_iterator(s, p, linear_buckets_t()); } + + BOOST_INTRUSIVE_FORCEINLINE const_iterator build_const_iterator(siterator s, bucket_ptr p, detail::true_) const //linear buckets + { return const_iterator(s, p, this->priv_value_traits_ptr()); } + + BOOST_INTRUSIVE_FORCEINLINE const_iterator build_const_iterator(siterator s, bucket_ptr, detail::false_) const //!linear buckets + { return const_iterator(s, &this->get_bucket_value_traits()); } }; template @@ -1032,7 +1183,7 @@ struct get_equal_to template struct get_equal_to { - typedef std::equal_to type; + typedef value_equal type; }; template @@ -1074,18 +1225,19 @@ struct hash_key_equal //bucket_hash_t //Stores bucket_plus_vtraits plust the hash function -template +template struct bucket_hash_t //Use public inheritance to avoid MSVC bugs with closures : public detail::ebo_functor_holder - ::value_traits::value_type + ::value_traits::value_type , VoidOrKeyOfValue , VoidOrKeyHash >::type > - , bucket_plus_vtraits //4 + , bucket_plus_vtraits //4 { - typedef typename bucket_plus_vtraits::value_traits value_traits; + typedef typename bucket_plus_vtraits + ::value_traits value_traits; typedef typename value_traits::value_type value_type; typedef typename value_traits::node_traits node_traits; typedef hash_key_hash @@ -1094,7 +1246,7 @@ struct bucket_hash_t typedef typename hash_key_types_base::key_of_value key_of_value; typedef BucketTraits bucket_traits; - typedef bucket_plus_vtraits bucket_plus_vtraits_t; + typedef bucket_plus_vtraits bucket_plus_vtraits_t; typedef detail::ebo_functor_holder base_t; template @@ -1105,7 +1257,7 @@ struct bucket_hash_t BOOST_INTRUSIVE_FORCEINLINE const hasher &priv_hasher() const { return this->base_t::get(); } - hasher &priv_hasher() + BOOST_INTRUSIVE_FORCEINLINE hasher &priv_hasher() { return this->base_t::get(); } using bucket_plus_vtraits_t::priv_stored_or_compute_hash; //For store_hash == true @@ -1114,11 +1266,12 @@ struct bucket_hash_t { return this->priv_hasher()(key_of_value()(v)); } }; -template +template struct hashtable_equal_holder { typedef detail::ebo_functor_holder - < typename hash_key_equal < typename bucket_plus_vtraits::value_traits::value_type + < typename hash_key_equal < typename bucket_plus_vtraits + ::value_traits::value_type , VoidOrKeyOfValue , VoidOrKeyEqual >::type @@ -1129,24 +1282,30 @@ struct hashtable_equal_holder //bucket_hash_equal_t //Stores bucket_hash_t and the equality function when the first //non-empty bucket shall not be cached. -template +template struct bucket_hash_equal_t //Use public inheritance to avoid MSVC bugs with closures - : public bucket_hash_t //3 - , public hashtable_equal_holder::type //equal + : public bucket_hash_t //3 + , public hashtable_equal_holder::type //equal { typedef typename hashtable_equal_holder - ::type equal_holder_t; - typedef bucket_hash_t bucket_hash_type; - typedef bucket_plus_vtraits bucket_plus_vtraits_t; - typedef ValueTraits value_traits; - typedef typename equal_holder_t::functor_type key_equal; - typedef typename bucket_hash_type::hasher hasher; - typedef BucketTraits bucket_traits; - typedef typename bucket_plus_vtraits_t::slist_impl slist_impl; - typedef typename slist_impl::iterator siterator; - typedef bucket_impl bucket_type; - typedef typename detail::unordered_bucket_ptr_impl::type bucket_ptr; + < ValueTraits, BucketTraits, VoidOrKeyOfValue + , VoidOrKeyEqual, LinearBuckets>::type equal_holder_t; + typedef bucket_hash_t< ValueTraits, VoidOrKeyOfValue + , VoidOrKeyHash, BucketTraits + , LinearBuckets> bucket_hash_type; + typedef bucket_plus_vtraits + bucket_plus_vtraits_t; + typedef ValueTraits value_traits; + typedef typename equal_holder_t::functor_type key_equal; + typedef typename bucket_hash_type::hasher hasher; + typedef BucketTraits bucket_traits; + typedef typename bucket_plus_vtraits_t::siterator siterator; + typedef typename bucket_plus_vtraits_t::const_siterator const_siterator; + typedef typename bucket_plus_vtraits_t::bucket_type bucket_type; + typedef typename bucket_plus_vtraits_t::slist_node_algorithms slist_node_algorithms; + typedef typename unordered_bucket_ptr_impl + ::type bucket_ptr; template bucket_hash_equal_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h, const key_equal &e) @@ -1155,7 +1314,7 @@ struct bucket_hash_equal_t {} BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_get_cache() - { return this->bucket_hash_type::priv_bucket_pointer(); } + { return this->priv_bucket_pointer(); } BOOST_INTRUSIVE_FORCEINLINE void priv_set_cache(bucket_ptr) {} @@ -1163,23 +1322,25 @@ struct bucket_hash_equal_t BOOST_INTRUSIVE_FORCEINLINE std::size_t priv_get_cache_bucket_num() { return 0u; } - BOOST_INTRUSIVE_FORCEINLINE void priv_initialize_cache() + BOOST_INTRUSIVE_FORCEINLINE void priv_init_cache() {} BOOST_INTRUSIVE_FORCEINLINE void priv_swap_cache(bucket_hash_equal_t &) {} - siterator priv_begin() const + siterator priv_begin(bucket_ptr &pbucketptr) const { std::size_t n = 0; - std::size_t bucket_cnt = this->bucket_hash_type::priv_bucket_count(); + std::size_t bucket_cnt = this->priv_bucket_count(); for (n = 0; n < bucket_cnt; ++n){ - bucket_type &b = this->bucket_hash_type::priv_bucket(n); - if(!b.empty()){ - return b.begin(); + bucket_type &b = this->priv_bucket(n); + if(!slist_node_algorithms::is_empty(b.get_node_ptr())){ + pbucketptr = this->to_ptr(b); + return siterator(b.begin_ptr()); } } - return this->bucket_hash_type::priv_invalid_local_it(); + pbucketptr = this->priv_invalid_bucket(); + return this->priv_invalid_local_it(); } BOOST_INTRUSIVE_FORCEINLINE void priv_insertion_update_cache(std::size_t) @@ -1188,6 +1349,9 @@ struct bucket_hash_equal_t BOOST_INTRUSIVE_FORCEINLINE void priv_erasure_update_cache_range(std::size_t, std::size_t) {} + BOOST_INTRUSIVE_FORCEINLINE void priv_erasure_update_cache(bucket_ptr) + {} + BOOST_INTRUSIVE_FORCEINLINE void priv_erasure_update_cache() {} @@ -1201,22 +1365,27 @@ struct bucket_hash_equal_t //bucket_hash_equal_t //Stores bucket_hash_t and the equality function when the first //non-empty bucket shall be cached. -template //cache_begin == true version -struct bucket_hash_equal_t +template //cache_begin == true version +struct bucket_hash_equal_t //Use public inheritance to avoid MSVC bugs with closures - : bucket_hash_t //2 - , hashtable_equal_holder::type + : bucket_hash_t //2 + , hashtable_equal_holder::type { typedef typename hashtable_equal_holder - ::type equal_holder_t; + < ValueTraits, BucketTraits + , VoidOrKeyOfValue, VoidOrKeyEqual, LinearBuckets>::type equal_holder_t; - typedef bucket_plus_vtraits bucket_plus_vtraits_t; + typedef bucket_plus_vtraits + < ValueTraits, BucketTraits, LinearBuckets> bucket_plus_vtraits_t; typedef ValueTraits value_traits; typedef typename equal_holder_t::functor_type key_equal; - typedef bucket_hash_t bucket_hash_type; + typedef bucket_hash_t + < ValueTraits, VoidOrKeyOfValue + , VoidOrKeyHash, BucketTraits, LinearBuckets> bucket_hash_type; typedef typename bucket_hash_type::hasher hasher; typedef BucketTraits bucket_traits; - typedef typename bucket_plus_vtraits_t::slist_impl::iterator siterator; + typedef typename bucket_plus_vtraits_t::siterator siterator; + typedef typename bucket_plus_vtraits_t::slist_node_algorithms slist_node_algorithms; template bucket_hash_equal_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h, const key_equal &e) @@ -1224,7 +1393,7 @@ struct bucket_hash_equal_t::type bucket_ptr; BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_get_cache() const @@ -1234,28 +1403,29 @@ struct bucket_hash_equal_tcached_begin_ - this->bucket_hash_type::priv_bucket_pointer()); } + { return std::size_t(this->cached_begin_ - this->priv_bucket_pointer()); } - BOOST_INTRUSIVE_FORCEINLINE void priv_initialize_cache() - { this->cached_begin_ = this->bucket_hash_type::priv_invalid_bucket(); } + BOOST_INTRUSIVE_FORCEINLINE void priv_init_cache() + { this->cached_begin_ = this->priv_invalid_bucket(); } BOOST_INTRUSIVE_FORCEINLINE void priv_swap_cache(bucket_hash_equal_t &other) { ::boost::adl_move_swap(this->cached_begin_, other.cached_begin_); } - siterator priv_begin() const + siterator priv_begin(bucket_ptr& pbucketptr) const { - if(this->cached_begin_ == this->bucket_hash_type::priv_invalid_bucket()){ - return this->bucket_hash_type::priv_invalid_local_it(); + pbucketptr = this->cached_begin_; + if(this->cached_begin_ == this->priv_invalid_bucket()){ + return this->priv_invalid_local_it(); } else{ - return this->cached_begin_->begin(); + return siterator(cached_begin_->begin_ptr()); } } void priv_insertion_update_cache(std::size_t insertion_bucket) { - BOOST_INTRUSIVE_INVARIANT_ASSERT(insertion_bucket < this->bucket_hash_type::priv_bucket_count()); - bucket_ptr p = this->bucket_hash_type::priv_bucket_pointer() + std::ptrdiff_t(insertion_bucket); + BOOST_INTRUSIVE_INVARIANT_ASSERT(insertion_bucket < this->priv_bucket_count()); + bucket_ptr p = this->priv_bucket_pointer() + std::ptrdiff_t(insertion_bucket); if(p < this->cached_begin_){ this->cached_begin_ = p; } @@ -1272,24 +1442,35 @@ struct bucket_hash_equal_tpriv_get_cache_bucket_num() == first_bucket_num && - this->bucket_hash_type::priv_bucket(first_bucket_num).empty() ){ - this->priv_set_cache(this->bucket_hash_type::priv_bucket_pointer() + std::ptrdiff_t(last_bucket_num)); + this->priv_bucket_empty(first_bucket_num) ){ + this->priv_set_cache(this->priv_bucket_pointer() + std::ptrdiff_t(last_bucket_num)); + this->priv_erasure_update_cache(); + } + } + + void priv_erasure_update_cache(bucket_ptr first_bucket) + { + //If the last bucket is the end, the cache must be updated + //to the last position if all + if (this->priv_get_cache() == first_bucket && + this->priv_bucket_empty(first_bucket)) { + this->priv_set_cache(first_bucket); this->priv_erasure_update_cache(); } } void priv_erasure_update_cache() { - if(this->cached_begin_ != this->bucket_hash_type::priv_invalid_bucket()){ - std::size_t current_n = std::size_t(this->priv_get_cache() - this->bucket_hash_type::priv_bucket_pointer()); - for( const std::size_t num_buckets = this->bucket_hash_type::priv_bucket_count() + if(this->cached_begin_ != this->priv_invalid_bucket()){ + std::size_t current_n = std::size_t(this->priv_get_cache() - this->priv_bucket_pointer()); + for( const std::size_t num_buckets = this->priv_bucket_count() ; current_n < num_buckets ; ++current_n, ++cached_begin_){ - if(!cached_begin_->empty()){ + if (!slist_node_algorithms::is_empty(cached_begin_->get_node_ptr())) { return; } } - this->priv_initialize_cache(); + this->priv_init_cache(); } } @@ -1355,16 +1536,19 @@ struct hashdata_internal < bucket_hash_equal_t < ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual , BucketTraits + , 0 != (BoolFlags & hash_bool_flags::linear_buckets_pos) , 0 != (BoolFlags & hash_bool_flags::cache_begin_pos) > //2 , SizeType , (BoolFlags & hash_bool_flags::incremental_pos) != 0 > { + static const bool linear_buckets = 0 != (BoolFlags & hash_bool_flags::linear_buckets_pos); typedef hashtable_size_traits_wrapper < bucket_hash_equal_t < ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual , BucketTraits + , 0 != (BoolFlags & hash_bool_flags::linear_buckets_pos) , 0 != (BoolFlags & hash_bool_flags::cache_begin_pos) > //2 , SizeType @@ -1372,7 +1556,8 @@ struct hashdata_internal > internal_type; typedef typename internal_type::key_equal key_equal; typedef typename internal_type::hasher hasher; - typedef bucket_plus_vtraits bucket_plus_vtraits_t; + typedef bucket_plus_vtraits + bucket_plus_vtraits_t; typedef SizeType size_type; typedef typename internal_type::size_traits split_traits; typedef typename bucket_plus_vtraits_t::bucket_ptr bucket_ptr; @@ -1389,15 +1574,11 @@ struct hashdata_internal ::reference const_reference; typedef typename value_traits::node_traits node_traits; typedef typename node_traits::node node; - typedef typename node_traits::node_ptr node_ptr; - typedef typename node_traits::const_node_ptr const_node_ptr; - typedef detail::node_functions node_functions_t; - typedef typename get_slist_impl - ::type - >::type slist_impl; - typedef typename slist_impl::node_algorithms node_algorithms; - typedef typename slist_impl::node_ptr slist_node_ptr; + typedef typename node_traits::node_ptr node_ptr; + typedef typename node_traits::const_node_ptr const_node_ptr; + typedef node_functions node_functions_t; + typedef typename bucket_plus_vtraits_t::slist_node_algorithms slist_node_algorithms; + typedef typename bucket_plus_vtraits_t::slist_node_ptr slist_node_ptr; typedef hash_key_types_base < typename ValueTraits::value_type @@ -1405,25 +1586,27 @@ struct hashdata_internal > hash_types_base; typedef typename hash_types_base::key_of_value key_of_value; - static const bool store_hash = detail::store_hash_is_true::value; + static const bool store_hash = store_hash_is_true::value; static const bool safemode_or_autounlink = is_safe_autounlink::value; static const bool stateful_value_traits = detail::is_stateful_value_traits::value; typedef detail::bool_ store_hash_t; typedef detail::transform_iterator - < typename slist_impl::iterator + < siterator , downcast_node_to_value_t < value_traits - , false> > local_iterator; + , false> > local_iterator; typedef detail::transform_iterator - < typename slist_impl::iterator + < siterator , downcast_node_to_value_t < value_traits - , true> > const_local_iterator; + , true> > const_local_iterator; // + typedef detail::bool_ linear_buckets_t; + template hashdata_internal( const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits , const hasher & h, const key_equal &e) @@ -1439,68 +1622,97 @@ struct hashdata_internal ~hashdata_internal() { this->priv_clear_buckets(); } + using internal_type::priv_clear_buckets; + void priv_clear_buckets() { - const std::size_t cache_num = std::size_t(this->priv_get_cache() - this->internal_type::priv_bucket_pointer()); - this->internal_type::priv_clear_buckets - ( this->priv_get_cache() - , this->internal_type::priv_bucket_count() - cache_num); + const std::size_t cache_num = std::size_t(this->priv_get_cache() - this->priv_bucket_pointer()); + this->priv_clear_buckets(this->priv_get_cache(), this->priv_bucket_count() - cache_num); } void priv_clear_buckets_and_cache() { this->priv_clear_buckets(); - this->priv_initialize_cache(); + this->priv_init_cache(); } - void priv_initialize_buckets_and_cache() + void priv_init_buckets_and_cache() { - this->internal_type::priv_clear_buckets - ( this->internal_type::priv_bucket_pointer() - , this->internal_type::priv_bucket_count()); - this->priv_initialize_cache(); + this->priv_init_buckets(this->priv_bucket_pointer(), this->priv_bucket_count()); + this->priv_init_cache(); } - - typedef hashtable_iterator iterator; - typedef hashtable_iterator const_iterator; - - static std::size_t priv_stored_hash(slist_node_ptr n, detail::true_ true_value) - { return bucket_plus_vtraits::priv_stored_hash(n, true_value); } - - static std::size_t priv_stored_hash(slist_node_ptr n, detail::false_ false_value) - { return bucket_plus_vtraits::priv_stored_hash(n, false_value); } + + typedef typename bucket_plus_vtraits_t::iterator iterator; + typedef typename bucket_plus_vtraits_t::const_iterator const_iterator; //public functions BOOST_INTRUSIVE_FORCEINLINE SizeType split_count() const BOOST_NOEXCEPT { return this->priv_split_traits().get_size(); } - BOOST_INTRUSIVE_FORCEINLINE iterator iterator_to(reference value) BOOST_NOEXCEPT + BOOST_INTRUSIVE_FORCEINLINE iterator iterator_to(reference value) BOOST_NOEXCEPT_IF(!linear_buckets) + { return iterator_to(value, linear_buckets_t()); } + + const_iterator iterator_to(const_reference value) const BOOST_NOEXCEPT_IF(!linear_buckets) + { return iterator_to(value, linear_buckets_t()); } + + iterator iterator_to(reference value, detail::true_) BOOST_NOEXCEPT_IF(!linear_buckets) { - return iterator(bucket_type::s_iterator_to - (this->priv_value_to_node(value)), &this->get_bucket_value_traits()); + const std::size_t h = this->priv_stored_or_compute_hash(value, store_hash_t()); + const size_type bn = this->priv_hash_to_bucket(h); + bucket_type &b = this->priv_bucket(bn); + const bucket_ptr bp = this->to_ptr(b); + siterator sit(this->priv_value_to_node_ptr(value)); + return this->build_iterator(sit, bp); } - const_iterator iterator_to(const_reference value) const BOOST_NOEXCEPT + const_iterator iterator_to(const_reference value, detail::true_) const BOOST_NOEXCEPT_IF(!linear_buckets) { - siterator const sit = bucket_type::s_iterator_to - ( *pointer_traits::const_cast_from - (pointer_traits::pointer_to(this->priv_value_to_node(value))) + const std::size_t h = this->priv_stored_or_compute_hash(value, store_hash_t()); + const size_type bn = this->priv_hash_to_bucket(h); + bucket_type& b = this->priv_bucket(bn); + const bucket_ptr bp = this->to_ptr(b); + + siterator const sit = siterator + ( pointer_traits::const_cast_from(this->priv_value_to_node_ptr(value)) ); + return this->build_const_iterator(sit, bp); + } + + static const bool incremental = 0 != (BoolFlags & hash_bool_flags::incremental_pos); + static const bool power_2_buckets = incremental || (0 != (BoolFlags & hash_bool_flags::power_2_buckets_pos)); + + BOOST_INTRUSIVE_FORCEINLINE size_type priv_hash_to_bucket(std::size_t hash_value) const + { + return static_cast(hash_to_bucket_split + (hash_value, this->priv_bucket_count(), this->split_count())); + } + + BOOST_INTRUSIVE_FORCEINLINE iterator iterator_to(reference value, detail::false_) BOOST_NOEXCEPT + { + return iterator( siterator(this->priv_value_to_node_ptr(value)) + , &this->get_bucket_value_traits()); + } + + const_iterator iterator_to(const_reference value, detail::false_) const BOOST_NOEXCEPT + { + siterator const sit = siterator + ( pointer_traits::const_cast_from(this->priv_value_to_node_ptr(value)) ); return const_iterator(sit, &this->get_bucket_value_traits()); } + static local_iterator s_local_iterator_to(reference value) BOOST_NOEXCEPT { BOOST_STATIC_ASSERT((!stateful_value_traits)); - siterator sit = bucket_type::s_iterator_to(*value_traits::to_node_ptr(value)); + siterator sit(value_traits::to_node_ptr(value)); return local_iterator(sit, const_value_traits_ptr()); } static const_local_iterator s_local_iterator_to(const_reference value) BOOST_NOEXCEPT { BOOST_STATIC_ASSERT((!stateful_value_traits)); - siterator const sit = bucket_type::s_iterator_to - ( *pointer_traits::const_cast_from + siterator const sit = siterator + ( pointer_traits::const_cast_from (value_traits::to_node_ptr(value)) ); return const_local_iterator(sit, const_value_traits_ptr()); @@ -1508,16 +1720,14 @@ struct hashdata_internal local_iterator local_iterator_to(reference value) BOOST_NOEXCEPT { - siterator sit = bucket_type::s_iterator_to(this->priv_value_to_node(value)); + siterator sit(this->priv_value_to_node_ptr(value)); return local_iterator(sit, this->priv_value_traits_ptr()); } const_local_iterator local_iterator_to(const_reference value) const BOOST_NOEXCEPT { - siterator sit = bucket_type::s_iterator_to - ( *pointer_traits::const_cast_from - (pointer_traits::pointer_to(this->priv_value_to_node(value))) - ); + siterator sit + ( pointer_traits::const_cast_from(this->priv_value_to_node_ptr(value)) ); return const_local_iterator(sit, this->priv_value_traits_ptr()); } @@ -1525,13 +1735,13 @@ struct hashdata_internal { return size_type(this->priv_bucket_count()); } BOOST_INTRUSIVE_FORCEINLINE size_type bucket_size(size_type n) const BOOST_NOEXCEPT - { return (size_type)this->priv_bucket(n).size(); } + { return (size_type)this->priv_bucket_size(n); } BOOST_INTRUSIVE_FORCEINLINE bucket_ptr bucket_pointer() const BOOST_NOEXCEPT { return this->priv_bucket_pointer(); } BOOST_INTRUSIVE_FORCEINLINE local_iterator begin(size_type n) BOOST_NOEXCEPT - { return local_iterator(this->priv_bucket(n).begin(), this->priv_value_traits_ptr()); } + { return local_iterator(this->priv_bucket_lbegin(n), this->priv_value_traits_ptr()); } BOOST_INTRUSIVE_FORCEINLINE const_local_iterator begin(size_type n) const BOOST_NOEXCEPT { return this->cbegin(n); } @@ -1549,7 +1759,7 @@ struct hashdata_internal const_local_iterator cbegin(size_type n) const BOOST_NOEXCEPT { return const_local_iterator - ( this->priv_bucket(n).begin() + (this->priv_bucket_lbegin(n) , this->priv_value_traits_ptr()); } @@ -1557,7 +1767,7 @@ struct hashdata_internal using internal_type::cend; local_iterator end(size_type n) BOOST_NOEXCEPT - { return local_iterator(this->priv_bucket(n).end(), this->priv_value_traits_ptr()); } + { return local_iterator(this->priv_bucket_lend(n), this->priv_value_traits_ptr()); } BOOST_INTRUSIVE_FORCEINLINE const_local_iterator end(size_type n) const BOOST_NOEXCEPT { return this->cend(n); } @@ -1565,20 +1775,28 @@ struct hashdata_internal const_local_iterator cend(size_type n) const BOOST_NOEXCEPT { return const_local_iterator - ( this->priv_bucket(n).end() + ( this->priv_bucket_lend(n) , this->priv_value_traits_ptr()); } //Public functions for hashtable_impl BOOST_INTRUSIVE_FORCEINLINE iterator begin() BOOST_NOEXCEPT - { return iterator(this->priv_begin(), &this->get_bucket_value_traits()); } + { + bucket_ptr p; + siterator s = this->priv_begin(p); + return this->build_iterator(s, p); + } BOOST_INTRUSIVE_FORCEINLINE const_iterator begin() const BOOST_NOEXCEPT { return this->cbegin(); } BOOST_INTRUSIVE_FORCEINLINE const_iterator cbegin() const BOOST_NOEXCEPT - { return const_iterator(this->priv_begin(), &this->get_bucket_value_traits()); } + { + bucket_ptr p; + siterator s = this->priv_begin(p); + return this->build_const_iterator(s, p); + } BOOST_INTRUSIVE_FORCEINLINE hasher hash_function() const { return this->priv_hasher(); } @@ -1636,18 +1854,19 @@ class hashtable_impl < ValueTraits , VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual , BucketTraits, SizeType - , BoolFlags & (hash_bool_flags::incremental_pos | hash_bool_flags::cache_begin_pos) //1 + , BoolFlags & ~(hash_bool_flags::constant_time_size_pos) //1 > , SizeType , (BoolFlags & hash_bool_flags::constant_time_size_pos) != 0 > { + static const bool linear_buckets_flag = (BoolFlags & hash_bool_flags::linear_buckets_pos) != 0; typedef hashtable_size_traits_wrapper < hashdata_internal < ValueTraits , VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual , BucketTraits, SizeType - , BoolFlags & (hash_bool_flags::incremental_pos | hash_bool_flags::cache_begin_pos) //1 + , BoolFlags & ~(hash_bool_flags::constant_time_size_pos) //1 > , SizeType , (BoolFlags & hash_bool_flags::constant_time_size_pos) != 0 @@ -1663,10 +1882,14 @@ class hashtable_impl /// @cond typedef BucketTraits bucket_traits; - typedef typename internal_type::slist_impl slist_impl; - typedef bucket_plus_vtraits bucket_plus_vtraits_t; + typedef bucket_plus_vtraits + bucket_plus_vtraits_t; typedef typename bucket_plus_vtraits_t::const_value_traits_ptr const_value_traits_ptr; + typedef detail::bool_ linear_buckets_t; + + typedef typename internal_type::siterator siterator; + typedef typename internal_type::const_siterator const_siterator; using internal_type::begin; using internal_type::cbegin; using internal_type::end; @@ -1696,10 +1919,8 @@ class hashtable_impl typedef SizeType size_type; typedef typename internal_type::key_equal key_equal; typedef typename internal_type::hasher hasher; - typedef bucket_impl bucket_type; + typedef typename internal_type::bucket_type bucket_type; typedef typename internal_type::bucket_ptr bucket_ptr; - typedef typename slist_impl::iterator siterator; - typedef typename slist_impl::const_iterator const_siterator; typedef typename internal_type::iterator iterator; typedef typename internal_type::const_iterator const_iterator; typedef typename internal_type::local_iterator local_iterator; @@ -1716,7 +1937,7 @@ class hashtable_impl ::reference node_reference; typedef typename pointer_traits ::reference const_node_reference; - typedef typename slist_impl::node_algorithms node_algorithms; + typedef typename internal_type::slist_node_algorithms slist_node_algorithms; static const bool stateful_value_traits = internal_type::stateful_value_traits; static const bool store_hash = internal_type::store_hash; @@ -1727,9 +1948,10 @@ class hashtable_impl static const bool compare_hash = 0 != (BoolFlags & hash_bool_flags::compare_hash_pos); static const bool incremental = 0 != (BoolFlags & hash_bool_flags::incremental_pos); static const bool power_2_buckets = incremental || (0 != (BoolFlags & hash_bool_flags::power_2_buckets_pos)); + static const bool optimize_multikey = optimize_multikey_is_true::value && !unique_keys; + static const bool linear_buckets = linear_buckets_flag; + static const std::size_t bucket_overhead = internal_type::bucket_overhead; - static const bool optimize_multikey - = detail::optimize_multikey_is_true::value && !unique_keys; /// @cond static const bool is_multikey = !unique_keys; @@ -1739,7 +1961,7 @@ class hashtable_impl //See documentation for more explanations BOOST_STATIC_ASSERT((!compare_hash || store_hash)); - typedef typename slist_impl::node_ptr slist_node_ptr; + typedef typename internal_type::slist_node_ptr slist_node_ptr; typedef typename pointer_traits ::template rebind_pointer < void >::type void_pointer; @@ -1751,9 +1973,10 @@ class hashtable_impl typedef detail::bool_ optimize_multikey_t; typedef detail::bool_ cache_begin_t; typedef detail::bool_ power_2_buckets_t; + typedef detail::bool_ compare_hash_t; typedef typename internal_type::split_traits split_traits; - typedef detail::group_functions group_functions_t; - typedef detail::node_functions node_functions_t; + typedef group_functions group_functions_t; + typedef node_functions node_functions_t; private: //noncopyable, movable @@ -1766,27 +1989,24 @@ class hashtable_impl //Cache begin is incompatible with auto-unlink hooks! BOOST_STATIC_ASSERT(!(cache_begin && ((int)value_traits::link_mode == (int)auto_unlink))); - template - struct typeof_node_disposer - { - typedef node_cast_adaptor - < detail::node_disposer< Disposer, value_traits, CircularSListAlgorithms> - , slist_node_ptr, node_ptr > type; - }; - - template - typename typeof_node_disposer::type - make_node_disposer(const Disposer &disposer) const - { - typedef typename typeof_node_disposer::type return_t; - return return_t(disposer, &this->priv_value_traits()); - } /// @endcond - + public: - typedef detail::insert_commit_data_impl insert_commit_data; + typedef insert_commit_data_impl insert_commit_data; + void default_init_actions() + { + this->priv_set_sentinel_bucket(); + this->priv_init_buckets_and_cache(); + this->priv_size_traits().set_size(size_type(0)); + size_type bucket_sz = this->bucket_count(); + BOOST_INTRUSIVE_INVARIANT_ASSERT(bucket_sz != 0); + //Check power of two bucket array if the option is activated + BOOST_INTRUSIVE_INVARIANT_ASSERT + (!power_2_buckets || (0 == (bucket_sz & (bucket_sz - 1)))); + this->priv_split_traits().set_size(size_type(bucket_sz >> 1u)); + } public: @@ -1808,16 +2028,7 @@ class hashtable_impl , const key_equal &equal_func = key_equal() , const value_traits &v_traits = value_traits()) : internal_type(v_traits, b_traits, hash_func, equal_func) - { - this->priv_initialize_buckets_and_cache(); - this->priv_size_traits().set_size(size_type(0)); - size_type bucket_sz = this->bucket_count(); - BOOST_INTRUSIVE_INVARIANT_ASSERT(bucket_sz != 0); - //Check power of two bucket array if the option is activated - BOOST_INTRUSIVE_INVARIANT_ASSERT - (!power_2_buckets || (0 == (bucket_sz & (bucket_sz-1)))); - this->priv_split_traits().set_size(size_type(bucket_sz>>1u)); - } + { this->default_init_actions(); } //! Requires: buckets must not be being used by any other resource //! and dereferencing iterator must yield an lvalue of type value_type. @@ -1842,14 +2053,8 @@ class hashtable_impl , const value_traits &v_traits = value_traits()) : internal_type(v_traits, b_traits, hash_func, equal_func) { - this->priv_initialize_buckets_and_cache(); - this->priv_size_traits().set_size(size_type(0)); - size_type bucket_sz = this->bucket_count(); - BOOST_INTRUSIVE_INVARIANT_ASSERT(bucket_sz != 0); - //Check power of two bucket array if the option is activated - BOOST_INTRUSIVE_INVARIANT_ASSERT - (!power_2_buckets || (0 == (bucket_sz & (bucket_sz-1)))); - this->priv_split_traits().set_size(size_type(bucket_sz>>1u)); + this->default_init_actions(); + //Now insert if(unique) this->insert_unique(b, e); @@ -1874,10 +2079,10 @@ class hashtable_impl ) { this->priv_swap_cache(x); - x.priv_initialize_cache(); + x.priv_init_cache(); this->priv_size_traits().set_size(x.priv_size_traits().get_size()); x.priv_size_traits().set_size(size_type(0)); - this->priv_split_traits().set_size(x.priv_split_traits().get_size()); + this->priv_split_traits().set_size(x.split_count()); x.priv_split_traits().set_size(size_type(0)); } @@ -1886,7 +2091,6 @@ class hashtable_impl hashtable_impl& operator=(BOOST_RV_REF(hashtable_impl) x) { this->swap(x); return *this; } - #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) //! Effects: Detaches all elements from this. The objects in the unordered_set //! are not deleted (i.e. no destructors are called). //! @@ -1894,8 +2098,10 @@ class hashtable_impl //! it's a safe-mode or auto-unlink value. Otherwise constant. //! //! Throws: Nothing. - ~hashtable_impl(); + ~hashtable_impl() + {} + #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) //! Effects: Returns an iterator pointing to the beginning of the unordered_set. //! //! Complexity: Amortized constant time. @@ -1978,7 +2184,7 @@ class hashtable_impl size_type bucket_cnt = this->bucket_count(); const bucket_type *b = boost::movelib::to_raw_pointer(this->priv_bucket_pointer()); for (size_type n = 0; n < bucket_cnt; ++n, ++b){ - if(!b->empty()){ + if(!slist_node_algorithms::is_empty(b->get_node_ptr())){ return false; } } @@ -2001,7 +2207,7 @@ class hashtable_impl std::size_t bucket_cnt = this->bucket_count(); const bucket_type *b = boost::movelib::to_raw_pointer(this->priv_bucket_pointer()); for (std::size_t n = 0; n < bucket_cnt; ++n, ++b){ - len += b->size(); + len += slist_node_algorithms::count(b->get_node_ptr()) - 1u; } BOOST_INTRUSIVE_INVARIANT_ASSERT((len <= SizeType(-1))); return size_type(len); @@ -2206,12 +2412,16 @@ class hashtable_impl , KeyEqual equal_func , insert_commit_data &commit_data) { - size_type bucket_num; - siterator prev; - siterator const pos = this->priv_find(key, hash_func, equal_func, bucket_num, commit_data.hash, prev); + const std::size_t h = hash_func(key); + const std::size_t bn = this->priv_hash_to_bucket(h); + bucket_type& b = this->priv_bucket(bn); + siterator const pos = this->priv_find_in_bucket(b, key, equal_func, h); + commit_data.bucket_idx = bn; + commit_data.set_hash(h); + return std::pair - ( iterator(pos, &this->get_bucket_value_traits()) - , pos == this->priv_invalid_local_it()); + ( this->build_iterator(pos, pointer_traits::pointer_to(b)) + , pos == this->priv_invalid_local_it()); } //! Effects: Checks if a value can be inserted in the unordered_set, using @@ -2266,15 +2476,15 @@ class hashtable_impl //! After a successful rehashing insert_commit_data remains valid. iterator insert_unique_commit(reference value, const insert_commit_data &commit_data) BOOST_NOEXCEPT { - size_type bucket_num = this->priv_hash_to_bucket(commit_data.hash); - bucket_type &b = this->priv_bucket(bucket_num); this->priv_size_traits().increment(); - node_ptr const n = pointer_traits::pointer_to(this->priv_value_to_node(value)); - BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(!safemode_or_autounlink || node_algorithms::unique(n)); - node_functions_t::store_hash(n, commit_data.hash, store_hash_t()); - this->priv_insertion_update_cache(bucket_num); + node_ptr const n = this->priv_value_to_node_ptr(value); + BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(!safemode_or_autounlink || slist_node_algorithms::unique(n)); + node_functions_t::store_hash(n, commit_data.get_hash(), store_hash_t()); + this->priv_insertion_update_cache(static_cast(commit_data.bucket_idx)); group_functions_t::insert_in_group(n, n, optimize_multikey_t()); - return iterator(b.insert_after(b.before_begin(), *n), &this->get_bucket_value_traits()); + bucket_type& b = this->priv_bucket(commit_data.bucket_idx); + slist_node_algorithms::link_after(b.get_node_ptr(), n); + return this->build_iterator(siterator(n), this->to_ptr(b)); } //! Effects: Erases the element pointed to by i. @@ -2356,11 +2566,10 @@ class hashtable_impl erase_and_dispose(const_iterator i, Disposer disposer) BOOST_NOEXCEPT { //Get the bucket number and local iterator for both iterators - siterator const first_local_it(i.slist_it()); - size_type const first_bucket_num = this->priv_get_bucket_num(first_local_it); - this->priv_erase_node(this->priv_bucket(first_bucket_num), first_local_it, make_node_disposer(disposer), optimize_multikey_t()); + const bucket_ptr bp = this->priv_get_bucket_ptr(i); + this->priv_erase_node(*bp, i.slist_it(), this->make_node_disposer(disposer), optimize_multikey_t()); this->priv_size_traits().decrement(); - this->priv_erasure_update_cache_range(first_bucket_num, first_bucket_num); + this->priv_erasure_update_cache(bp); } //! Requires: Disposer::operator()(pointer) shouldn't throw. @@ -2380,11 +2589,10 @@ class hashtable_impl { if(b != e){ //Get the bucket number and local iterator for both iterators - siterator first_local_it(b.slist_it()); - size_type first_bucket_num = this->priv_get_bucket_num(first_local_it); + size_type first_bucket_num = this->priv_get_bucket_num(b); siterator before_first_local_it - = this->priv_get_previous(this->priv_bucket(first_bucket_num), first_local_it); + = this->priv_get_previous(this->priv_bucket(first_bucket_num), b.slist_it(), optimize_multikey_t()); size_type last_bucket_num; siterator last_local_it; @@ -2392,15 +2600,15 @@ class hashtable_impl //of the last bucket if(e == this->end()){ last_bucket_num = size_type(this->bucket_count() - 1u); - last_local_it = this->priv_bucket(last_bucket_num).end(); + last_local_it = this->sit_end(this->priv_bucket(last_bucket_num)); } else{ last_local_it = e.slist_it(); - last_bucket_num = this->priv_get_bucket_num(last_local_it); + last_bucket_num = this->priv_get_bucket_num(e); } size_type const num_erased = (size_type)this->priv_erase_node_range ( before_first_local_it, first_bucket_num, last_local_it, last_bucket_num - , make_node_disposer(disposer), optimize_multikey_t()); + , this->make_node_disposer(disposer), optimize_multikey_t()); this->priv_size_traits().set_size(size_type(this->priv_size_traits().get_size()-num_erased)); this->priv_erasure_update_cache_range(first_bucket_num, last_bucket_num); } @@ -2454,18 +2662,19 @@ class hashtable_impl if(success){ if(optimize_multikey){ cnt = this->priv_erase_from_single_bucket - (this->priv_bucket(bucket_num), prev, ++(priv_last_in_group)(it), make_node_disposer(disposer), optimize_multikey_t()); + ( this->priv_bucket(bucket_num), prev + , ++(priv_last_in_group)(it, optimize_multikey_t()) + , this->make_node_disposer(disposer), optimize_multikey_t()); } else{ - bucket_type &b = this->priv_bucket(bucket_num); - siterator const end_sit = b.end(); + siterator const end_sit = this->priv_bucket_lend(bucket_num); do{ ++cnt; ++it; }while(it != end_sit && - this->priv_is_value_equal_to_key - (this->priv_value_from_slist_node(it.pointed_node()), h, key, equal_func)); - bucket_type::s_erase_after_and_dispose(prev, it, make_node_disposer(disposer)); + this->priv_is_value_equal_to_key + (this->priv_value_from_siterator(it), h, key, equal_func, compare_hash_t())); + slist_node_algorithms::unlink_after_and_dispose(prev.pointed_node(), it.pointed_node(), this->make_node_disposer(disposer)); } this->priv_size_traits().set_size(size_type(this->priv_size_traits().get_size()-cnt)); this->priv_erasure_update_cache(); @@ -2506,13 +2715,14 @@ class hashtable_impl if(!constant_time_size || !this->empty()){ size_type num_buckets = this->bucket_count(); bucket_ptr b = this->priv_bucket_pointer(); - typename typeof_node_disposer::type d(disposer, &this->priv_value_traits()); - for(; num_buckets--; ++b){ - b->clear_and_dispose(d); + typename internal_type::template typeof_node_disposer::type d(disposer, &this->priv_value_traits()); + for(; num_buckets; ++b){ + --num_buckets; + slist_node_algorithms::detach_and_dispose(b->get_node_ptr(), d); } this->priv_size_traits().set_size(size_type(0)); } - this->priv_initialize_cache(); + this->priv_init_cache(); } //! Effects: Returns the number of contained elements with the given value @@ -2576,11 +2786,10 @@ class hashtable_impl template iterator find(const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) { - size_type bucket_n; - std::size_t h; - siterator prev; - return iterator( this->priv_find(key, hash_func, equal_func, bucket_n, h, prev) - , &this->get_bucket_value_traits()); + std::size_t h = hash_func(key); + bucket_type& b = this->priv_bucket(this->priv_hash_to_bucket(h)); + return this->build_iterator( this->priv_find_in_bucket(b, key, equal_func, h) + , pointer_traits::pointer_to(b)); } //! Effects: Finds a const_iterator to the first element whose key is @@ -2615,11 +2824,12 @@ class hashtable_impl const_iterator find (const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) const { - size_type bucket_n; - std::size_t hash_value; - siterator prev; - return const_iterator( this->priv_find(key, hash_func, equal_func, bucket_n, hash_value, prev) - , &this->get_bucket_value_traits()); + std::size_t h = hash_func(key); + bucket_ptr bp = this->priv_bucket_ptr(this->priv_hash_to_bucket(h)); + siterator s = this->priv_find_in_bucket(*bp, key, equal_func, h); + return s == this->priv_invalid_local_it() + ? this->end() + : this->build_const_iterator(s, bp); } //! Effects: Returns a range containing all elements with values equivalent @@ -2656,11 +2866,11 @@ class hashtable_impl std::pair equal_range (const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) { - std::pair ret = + priv_equal_range_result ret = this->priv_equal_range(key, hash_func, equal_func); return std::pair - ( iterator(ret.first, &this->get_bucket_value_traits()) - , iterator(ret.second, &this->get_bucket_value_traits())); + ( this->build_iterator(ret.first, ret.bucket_first) + , this->build_iterator(ret.second, ret.bucket_second)); } //! Effects: Returns a range containing all elements with values equivalent @@ -2698,11 +2908,11 @@ class hashtable_impl std::pair equal_range (const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) const { - std::pair ret = + priv_equal_range_result ret = this->priv_equal_range(key, hash_func, equal_func); return std::pair - ( const_iterator(ret.first, &this->get_bucket_value_traits()) - , const_iterator(ret.second, &this->get_bucket_value_traits())); + ( this->build_const_iterator(ret.first, ret.bucket_first) + , this->build_const_iterator(ret.second, ret.bucket_second)); } #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) @@ -2940,7 +3150,7 @@ class hashtable_impl //! //! Throws: If the hasher functor throws. Basic guarantee. BOOST_INTRUSIVE_FORCEINLINE void rehash(const bucket_traits &new_bucket_traits) - { this->rehash_impl(new_bucket_traits, false); } + { this->priv_rehash_impl(new_bucket_traits, false); } //! Note: This function is used when keys from inserted elements are changed //! (e.g. a language change when key is a string) but uniqueness and hash properties are @@ -2963,7 +3173,7 @@ class hashtable_impl //! //! Throws: If the hasher functor throws. Basic guarantee. BOOST_INTRUSIVE_FORCEINLINE void full_rehash() - { this->rehash_impl(this->priv_bucket_traits(), true); } + { this->priv_rehash_impl(this->priv_bucket_traits(), true); } //! Requires: //! @@ -2978,7 +3188,7 @@ class hashtable_impl { //This function is only available for containers with incremental hashing BOOST_STATIC_ASSERT(( incremental && power_2_buckets )); - const std::size_t split_idx = this->priv_split_traits().get_size(); + const std::size_t split_idx = this->split_count(); const std::size_t bucket_cnt = this->bucket_count(); bool ret = false; @@ -2992,20 +3202,22 @@ class hashtable_impl //Anti-exception stuff: if an exception is thrown while //moving elements from old_bucket to the target bucket, all moved //elements are moved back to the original one. - detail::incremental_rehash_rollback rollback + incremental_rehash_rollback rollback ( this->priv_bucket(split_idx), old_bucket, this->priv_split_traits()); - for( siterator before_i(old_bucket.before_begin()), i(old_bucket.begin()), end_sit(old_bucket.end()) - ; i != end_sit; i = before_i, ++i){ - const value_type &v = this->priv_value_from_slist_node(i.pointed_node()); + siterator before_i(old_bucket.get_node_ptr()); + siterator i(before_i); ++i; + siterator end_sit = linear_buckets ? siterator() : before_i; + for( ; i != end_sit; i = before_i, ++i){ + const value_type &v = this->priv_value_from_siterator(i); const std::size_t hash_value = this->priv_stored_or_compute_hash(v, store_hash_t()); const std::size_t new_n = this->priv_hash_to_bucket(hash_value); - siterator const last = (priv_last_in_group)(i); + siterator const last = (priv_last_in_group)(i, optimize_multikey_t()); if(new_n == bucket_to_rehash){ before_i = last; } else{ bucket_type &new_b = this->priv_bucket(new_n); - new_b.splice_after(new_b.before_begin(), old_bucket, before_i, last); + slist_node_algorithms::transfer_after(new_b.get_node_ptr(), before_i.pointed_node(), last.pointed_node()); } } rollback.release(); @@ -3016,7 +3228,7 @@ class hashtable_impl const std::size_t target_bucket_num = split_idx - 1u - bucket_cnt/2u; bucket_type &target_bucket = this->priv_bucket(target_bucket_num); bucket_type &source_bucket = this->priv_bucket(split_idx-1u); - target_bucket.splice_after(target_bucket.cbefore_begin(), source_bucket); + slist_node_algorithms::transfer_after(target_bucket.get_node_ptr(), source_bucket.get_node_ptr()); this->priv_split_traits().decrement(); this->priv_insertion_update_cache(target_bucket_num); } @@ -3040,19 +3252,20 @@ class hashtable_impl { //This function is only available for containers with incremental hashing BOOST_STATIC_ASSERT(( incremental && power_2_buckets )); - std::size_t new_bucket_count = new_bucket_traits.bucket_count(); - BOOST_INTRUSIVE_INVARIANT_ASSERT(sizeof(SizeType) >= sizeof(std::size_t) || new_bucket_count <= SizeType(-1)); - size_type const new_bucket_traits_size = static_cast(new_bucket_count); - size_type const cur_bucket_traits = this->bucket_count(); + const bucket_ptr new_buckets = new_bucket_traits.bucket_begin(); + const size_type new_bucket_count_stdszt = static_cast(new_bucket_traits.bucket_count() - bucket_overhead); + BOOST_INTRUSIVE_INVARIANT_ASSERT(sizeof(size_type) >= sizeof(std::size_t) || new_bucket_count_stdszt <= size_type(-1)); + size_type new_bucket_count = static_cast(new_bucket_count_stdszt); + const size_type old_bucket_count = static_cast(this->priv_bucket_count()); const size_type split_idx = this->split_count(); //Test new bucket size is consistent with internal bucket size and split count - if(new_bucket_traits_size/2 == cur_bucket_traits){ - if(!(split_idx >= cur_bucket_traits)) + if(new_bucket_count/2 == old_bucket_count){ + if(!(split_idx >= old_bucket_count)) return false; } - else if(new_bucket_traits_size == cur_bucket_traits/2){ - if(!(split_idx <= new_bucket_traits_size)) + else if(new_bucket_count == old_bucket_count/2){ + if(!(split_idx <= new_bucket_count)) return false; } else{ @@ -3061,17 +3274,24 @@ class hashtable_impl const size_type ini_n = (size_type)this->priv_get_cache_bucket_num(); const bucket_ptr old_buckets = this->priv_bucket_pointer(); + + + this->priv_unset_sentinel_bucket(); + this->priv_initialize_new_buckets(old_buckets, old_bucket_count, new_buckets, new_bucket_count); this->priv_bucket_traits() = new_bucket_traits; - if(new_bucket_traits.bucket_begin() != old_buckets){ + + if(old_buckets != new_buckets){ for(size_type n = ini_n; n < split_idx; ++n){ - bucket_type &new_bucket = new_bucket_traits.bucket_begin()[difference_type(n)]; - bucket_type &old_bucket = old_buckets[difference_type(n)]; - new_bucket.splice_after(new_bucket.cbefore_begin(), old_bucket); + slist_node_ptr new_bucket_nodeptr = new_bucket_traits.bucket_begin()[difference_type(n)].get_node_ptr(); + slist_node_ptr old_bucket_node_ptr = old_buckets[difference_type(n)].get_node_ptr(); + slist_node_algorithms::transfer_after(new_bucket_nodeptr, old_bucket_node_ptr); } //Put cache to safe position - this->priv_initialize_cache(); + this->priv_init_cache(); this->priv_insertion_update_cache(ini_n); } + + this->priv_set_sentinel_bucket(); return true; } @@ -3116,6 +3336,10 @@ class hashtable_impl if(constant_time_size && x.size() != y.size()){ return false; } + + if (boost::intrusive::iterator_udistance(x.begin(), x.end()) != x.size()) + return false; + for (const_iterator ix = x.cbegin(), ex = x.cend(); ix != ex; ++ix){ std::pair eqx(x.equal_range(key_of_value()(*ix))), eqy(y.equal_range(key_of_value()(*ix))); @@ -3148,21 +3372,45 @@ class hashtable_impl BOOST_INTRUSIVE_FORCEINLINE void check() const {} private: - void rehash_impl(const bucket_traits &new_bucket_traits, bool do_full_rehash) + static void priv_initialize_new_buckets + ( bucket_ptr old_buckets, size_type old_bucket_count + , bucket_ptr new_buckets, size_type new_bucket_count) { - std::size_t nbc = new_bucket_traits.bucket_count(); + //Initialize new buckets + const bool same_buffer = old_buckets == new_buckets; + if (same_buffer && new_bucket_count <= old_bucket_count) { + //Nothing to do here + } + else { + bucket_ptr p; + size_type c; + + if (same_buffer) { + p = old_buckets + std::ptrdiff_t(old_bucket_count); + c = size_type(new_bucket_count - old_bucket_count); + } + else { + p = new_buckets; + c = new_bucket_count; + } + internal_type::priv_init_buckets(p, c); + } + } + + void priv_rehash_impl(const bucket_traits &new_bucket_traits, bool do_full_rehash) + { + const std::size_t nbc = new_bucket_traits.bucket_count() - bucket_overhead; BOOST_INTRUSIVE_INVARIANT_ASSERT(sizeof(SizeType) >= sizeof(std::size_t) || nbc <= SizeType(-1)); const bucket_ptr new_buckets = new_bucket_traits.bucket_begin(); - size_type new_bucket_count = static_cast(nbc); + const size_type new_bucket_count = static_cast(nbc); const bucket_ptr old_buckets = this->priv_bucket_pointer(); - size_type old_bucket_count = this->bucket_count(); + const size_type old_bucket_count = this->bucket_count(); //Check power of two bucket array if the option is activated BOOST_INTRUSIVE_INVARIANT_ASSERT (!power_2_buckets || (0 == (new_bucket_count & (new_bucket_count-1u)))); - size_type n = (size_type)this->priv_get_cache_bucket_num(); const bool same_buffer = old_buckets == new_buckets; //If the new bucket length is a common factor //of the old one we can avoid hash calculations. @@ -3171,49 +3419,58 @@ class hashtable_impl //If we are shrinking the same bucket array and it's //is a fast shrink, just rehash the last nodes size_type new_first_bucket_num = new_bucket_count; - if(same_buffer && fast_shrink && (n < new_bucket_count)){ - new_first_bucket_num = n; - n = new_bucket_count; + size_type old_bucket_cache = (size_type)this->priv_get_cache_bucket_num(); + if(same_buffer && fast_shrink && (old_bucket_cache < new_bucket_count)){ + new_first_bucket_num = old_bucket_cache; + old_bucket_cache = new_bucket_count; } + if (!do_full_rehash) + this->priv_initialize_new_buckets(old_buckets, old_bucket_count, new_buckets, new_bucket_count); + //Anti-exception stuff: they destroy the elements if something goes wrong. //If the source and destination buckets are the same, the second rollback function //is harmless, because all elements have been already unlinked and destroyed - typedef detail::init_disposer NodeDisposer; - typedef detail::exception_array_disposer ArrayDisposer; - NodeDisposer node_disp; - ArrayDisposer rollback1(new_buckets[0], node_disp, new_bucket_count); - ArrayDisposer rollback2(old_buckets[0], node_disp, old_bucket_count); + + typedef typename internal_type::template typeof_node_disposer::type NodeDisposer; + typedef exception_bucket_disposer ArrayDisposer; + NodeDisposer nd(this->make_node_disposer(detail::null_disposer())); + ArrayDisposer rollback1(new_buckets[0], nd, new_bucket_count); + ArrayDisposer rollback2(old_buckets[0], nd, old_bucket_count); //Put size in a safe value for rollback exception size_type const size_backup = this->priv_size_traits().get_size(); this->priv_size_traits().set_size(0); //Put cache to safe position - this->priv_initialize_cache(); + this->priv_init_cache(); this->priv_insertion_update_cache(size_type(0u)); + this->priv_unset_sentinel_bucket(); //Iterate through nodes - for(; n < old_bucket_count; ++n){ + for(size_type n = old_bucket_cache; n < old_bucket_count; ++n){ bucket_type &old_bucket = old_buckets[difference_type(n)]; if(!fast_shrink){ - for( siterator before_i(old_bucket.before_begin()), i(old_bucket.begin()), end_sit(old_bucket.end()) + siterator before_i(old_bucket.get_node_ptr()); + siterator i(before_i); ++i; + siterator end_sit(this->sit_end(old_bucket)); + for( // ; i != end_sit ; i = before_i, ++i){ //First obtain hash value (and store it if do_full_rehash) std::size_t hash_value; if(do_full_rehash){ - value_type &v = this->priv_value_from_slist_node(i.pointed_node()); + value_type &v = this->priv_value_from_siterator(i); hash_value = this->priv_hasher()(key_of_value()(v)); - node_functions_t::store_hash(pointer_traits::pointer_to(this->priv_value_to_node(v)), hash_value, store_hash_t()); + node_functions_t::store_hash(this->priv_value_to_node_ptr(v), hash_value, store_hash_t()); } else{ - const value_type &v = this->priv_value_from_slist_node(i.pointed_node()); + const value_type &v = this->priv_value_from_siterator(i); hash_value = this->priv_stored_or_compute_hash(v, store_hash_t()); } //Now calculate the new bucket position - const size_type new_n = (size_type)detail::hash_to_bucket_split + const size_type new_n = (size_type)hash_to_bucket_split (hash_value, new_bucket_count, new_bucket_count); //Update first used bucket cache @@ -3221,27 +3478,25 @@ class hashtable_impl new_first_bucket_num = new_n; //If the target bucket is new, transfer the whole group - siterator const last = (priv_last_in_group)(i); + siterator const last = (priv_last_in_group)(i, optimize_multikey_t()); if(same_buffer && new_n == n){ before_i = last; } else{ bucket_type &new_b = new_buckets[difference_type(new_n)]; - new_b.splice_after(new_b.before_begin(), old_bucket, before_i, last); + slist_node_algorithms::transfer_after(new_b.get_node_ptr(), before_i.pointed_node(), last.pointed_node()); } } } else{ - const size_type new_n = (size_type)detail::hash_to_bucket_split + const size_type new_n = (size_type)hash_to_bucket_split (n, new_bucket_count, new_bucket_count); if(cache_begin && new_n < new_first_bucket_num) new_first_bucket_num = new_n; bucket_type &new_b = new_buckets[difference_type(new_n)]; - new_b.splice_after( new_b.before_begin() - , old_bucket - , old_bucket.before_begin() - , bucket_plus_vtraits_t::priv_get_last(old_bucket, optimize_multikey_t())); + siterator last = this->priv_get_last(old_bucket, optimize_multikey_t()); + slist_node_algorithms::transfer_after(new_b.get_node_ptr(), old_bucket.get_node_ptr(), last.pointed_node()); } } @@ -3250,7 +3505,9 @@ class hashtable_impl if(&new_bucket_traits != &this->priv_bucket_traits()){ this->priv_bucket_traits() = new_bucket_traits; } - this->priv_initialize_cache(); + + this->priv_set_sentinel_bucket(); + this->priv_init_cache(); this->priv_insertion_update_cache(new_first_bucket_num); rollback1.release(); rollback2.release(); @@ -3308,13 +3565,13 @@ class hashtable_impl //std::size_t const hash_value = this->priv_stored_or_compute_hash(src_ref, store_hash_t());; //size_type const bucket_number = this->priv_hash_to_bucket(hash_value); bucket_type &cur_bucket = this->priv_bucket(bucket_number); - siterator const prev(cur_bucket.before_begin()); + siterator const prev(cur_bucket.get_node_ptr()); //Just check if the cloned node is equal to the first inserted value in the new bucket //as equal src values were contiguous and they should be already inserted in the //destination bucket. - bool const next_is_in_group = optimize_multikey && !cur_bucket.empty() && + bool const next_is_in_group = optimize_multikey && !this->priv_bucket_empty(bucket_number) && this->priv_equal()( key_of_value()(src_ref) - , key_of_value()(this->priv_value_from_slist_node((++siterator(prev)).pointed_node()))); + , key_of_value()(this->priv_value_from_siterator(++siterator(prev)))); this->priv_insert_equal_after_find(*cloner(src_ref), bucket_number, hash_to_store, prev, next_is_in_group); } @@ -3325,26 +3582,24 @@ class hashtable_impl const size_type src_bucket_count = src.bucket_count(); const size_type dst_bucket_count = this->bucket_count(); size_type constructed = 0; - typedef node_cast_adaptor< detail::node_disposer - , slist_node_ptr, node_ptr > NodeDisposer; + typedef typename internal_type::template typeof_node_disposer::type NodeDisposer; NodeDisposer node_disp(disposer, &this->priv_value_traits()); - detail::exception_array_disposer + exception_bucket_disposer rollback(this->priv_bucket(0), node_disp, constructed); //Now insert the remaining ones using the modulo trick for( //"constructed" already initialized ; constructed < src_bucket_count ; ++constructed){ //Since incremental hashing can't be structurally copied, avoid hash_to_bucket_split - const size_type new_n = (size_type) detail::hash_to_bucket(constructed, dst_bucket_count, detail::bool_()); + const size_type new_n = (size_type) hash_to_bucket(constructed, dst_bucket_count, detail::bool_()); bucket_type &src_b = src.priv_bucket(constructed); - for( siterator b(src_b.begin()), e(src_b.end()); b != e; ++b){ - slist_node_ptr const n(b.pointed_node()); + for( siterator b(this->priv_bucket_lbegin(src_b)), e(this->priv_bucket_lend(src_b)); b != e; ++b){ typedef typename detail::if_c ::value, const_reference, reference>::type reference_type; - reference_type r = this->priv_value_from_slist_node(n); + reference_type r = this->priv_value_from_siterator(b); this->priv_clone_front_in_bucket - (new_n, r, this->priv_stored_hash(n, store_hash_t()), cloner); + (new_n, r, this->priv_stored_hash(b, store_hash_t()), cloner); } } this->priv_hasher() = src.priv_hasher(); @@ -3356,74 +3611,94 @@ class hashtable_impl this->priv_erasure_update_cache(); } - size_type priv_hash_to_bucket(std::size_t hash_value) const - { - return static_cast(detail::hash_to_bucket_split - (hash_value, this->priv_bucket_traits().bucket_count(), this->priv_split_traits().get_size())); - } - iterator priv_insert_equal_after_find(reference value, size_type bucket_num, std::size_t hash_value, siterator prev, bool const next_is_in_group) { //Now store hash if needed - node_ptr n = pointer_traits::pointer_to(this->priv_value_to_node(value)); + node_ptr n = this->priv_value_to_node_ptr(value); node_functions_t::store_hash(n, hash_value, store_hash_t()); //Checks for some modes - BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(!safemode_or_autounlink || node_algorithms::unique(n)); + BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(!safemode_or_autounlink || slist_node_algorithms::unique(n)); //Shortcut to optimize_multikey cases group_functions_t::insert_in_group - ( next_is_in_group ? detail::dcast_bucket_ptr((++siterator(prev)).pointed_node()) : n + ( next_is_in_group ? dcast_bucket_ptr((++siterator(prev)).pointed_node()) : n , n, optimize_multikey_t()); //Update cache and increment size if needed this->priv_insertion_update_cache(bucket_num); this->priv_size_traits().increment(); - //Insert the element in the bucket after it - return iterator(bucket_type::s_insert_after(prev, *n), &this->get_bucket_value_traits()); + slist_node_algorithms::link_after(prev.pointed_node(), n); + return this->build_iterator(siterator(n), this->priv_bucket_ptr(bucket_num)); } template - siterator priv_find //In case it is not found previt is bucket.before_begin() + siterator priv_find //In case it is not found previt is priv_invalid_local_it() ( const KeyType &key, KeyHasher hash_func , KeyEqual equal_func, size_type &bucket_number, std::size_t &h, siterator &previt) const { h = hash_func(key); - return this->priv_find_with_hash(key, equal_func, bucket_number, h, previt); - } - template - bool priv_is_value_equal_to_key(const value_type &v, const std::size_t h, const KeyType &key, KeyEqual equal_func) const - { - (void)h; - return (!compare_hash || this->priv_stored_or_compute_hash(v, store_hash_t()) == h) && equal_func(key, key_of_value()(v)); + bucket_number = this->priv_hash_to_bucket(h); + bucket_type& b = this->priv_bucket(bucket_number); + siterator prev = this->sit_bbegin(b); + siterator it = prev; + siterator const endit = this->sit_end(b); + + while (++it != endit) { + if (this->priv_is_value_equal_to_key + (this->priv_value_from_siterator(it), h, key, equal_func, compare_hash_t())) { + previt = prev; + return it; + } + prev = it = (priv_last_in_group)(it, optimize_multikey_t()); + } + previt = b.get_node_ptr(); + return this->priv_invalid_local_it(); } - //return previous iterator to the next equal range group in case - static siterator priv_last_in_group(const siterator &it_first_in_group) BOOST_NOEXCEPT - { - return bucket_type::s_iterator_to - (*group_functions_t::get_last_in_group - (detail::dcast_bucket_ptr(it_first_in_group.pointed_node()), optimize_multikey_t())); - } template - siterator priv_find_with_hash //In case it is not found previt is bucket.before_begin() - ( const KeyType &key, KeyEqual equal_func, size_type &bucket_number, const std::size_t h, siterator &previt) const + siterator priv_find_in_bucket //In case it is not found previt is priv_invalid_local_it() + (bucket_type& b, const KeyType& key, KeyEqual equal_func, const std::size_t h) const { - bucket_number = this->priv_hash_to_bucket(h); - bucket_type &b = this->priv_bucket(bucket_number); - previt = b.before_begin(); - siterator it = previt; - siterator const endit = b.end(); + siterator it(this->sit_bbegin(b)); + siterator const endit(this->sit_end(b)); - while(++it != endit){ - if(this->priv_is_value_equal_to_key(this->priv_value_from_slist_node(it.pointed_node()), h, key, equal_func)){ + while (++it != endit) { + if (this->priv_is_value_equal_to_key + (this->priv_value_from_siterator(it), h, key, equal_func, compare_hash_t())) { return it; } - previt = it = (priv_last_in_group)(it); + it = (priv_last_in_group)(it, optimize_multikey_t()); } - previt = b.before_begin(); return this->priv_invalid_local_it(); } + template + BOOST_INTRUSIVE_FORCEINLINE bool priv_is_value_equal_to_key + (const value_type &v, const std::size_t h, const KeyType &key, KeyEqual equal_func, detail::true_) const //compare_hash + { + return this->priv_stored_or_compute_hash(v, store_hash_t()) == h && equal_func(key, key_of_value()(v)); + } + + template + BOOST_INTRUSIVE_FORCEINLINE bool priv_is_value_equal_to_key + (const value_type& v, const std::size_t , const KeyType& key, KeyEqual equal_func, detail::false_) const //compare_hash + { return equal_func(key, key_of_value()(v)); } + + + //return previous iterator to the next equal range group in case + BOOST_INTRUSIVE_FORCEINLINE static siterator priv_last_in_group + (const siterator &it_first_in_group, detail::true_) BOOST_NOEXCEPT //optimize_multikey + { + return siterator + (group_functions_t::get_last_in_group + (dcast_bucket_ptr(it_first_in_group.pointed_node()), optimize_multikey_t())); + } + + //return previous iterator to the next equal range group in case + BOOST_INTRUSIVE_FORCEINLINE static siterator &priv_last_in_group //!optimize_multikey + (siterator &it_first_in_group, detail::false_) BOOST_NOEXCEPT + { return it_first_in_group; } + template std::pair priv_local_equal_range ( const KeyType &key @@ -3446,28 +3721,39 @@ class hashtable_impl found_bucket = n_bucket; //If it's present, find the first that it's not equal in //the same bucket - bucket_type &b = this->priv_bucket(n_bucket); siterator it = to_return.first; - ++internal_cnt; //At least one is found - if(optimize_multikey){ - to_return.second = ++(priv_last_in_group)(it); - internal_cnt += boost::intrusive::iterator_udistance(++it, to_return.second); + siterator const bend = this->priv_bucket_lend(n_bucket); + BOOST_IF_CONSTEXPR(optimize_multikey){ + const siterator past_last_in_group_it = ++(priv_last_in_group)(it, optimize_multikey_t()); + internal_cnt += boost::intrusive::iterator_udistance(++it, past_last_in_group_it) + 1u; + if (past_last_in_group_it != bend) + to_return.second = past_last_in_group_it; } else{ - siterator const bend = b.end(); - while(++it != bend && - this->priv_is_value_equal_to_key(this->priv_value_from_slist_node(it.pointed_node()), h, key, equal_func)){ - ++internal_cnt; - } - to_return.second = it; + do { + ++internal_cnt; //At least one is found + ++it; + } while(it != bend && + this->priv_is_value_equal_to_key + (this->priv_value_from_siterator(it), h, key, equal_func, compare_hash_t())); + if (it != bend) + to_return.second = it; } } cnt = size_type(internal_cnt); return to_return; } + struct priv_equal_range_result + { + siterator first; + siterator second; + bucket_ptr bucket_first; + bucket_ptr bucket_second; + }; + template - std::pair priv_equal_range + priv_equal_range_result priv_equal_range ( const KeyType &key , KeyHasher hash_func , KeyEqual equal_func) const @@ -3476,70 +3762,103 @@ class hashtable_impl size_type cnt; //Let's see if the element is present - std::pair to_return + const std::pair to_return (this->priv_local_equal_range(key, hash_func, equal_func, n_bucket, cnt)); + priv_equal_range_result r; + r.first = to_return.first; + r.second = to_return.second; + //If not, find the next element as ".second" if ".second" local iterator //is not pointing to an element. - if(to_return.first != to_return.second && - to_return.second == this->priv_bucket(n_bucket).end()){ - to_return.second = this->priv_invalid_local_it(); - ++n_bucket; - for( const size_type max_bucket = this->bucket_count() - ; n_bucket != max_bucket - ; ++n_bucket){ - bucket_type &b = this->priv_bucket(n_bucket); - if(!b.empty()){ - to_return.second = b.begin(); - break; - } + if(to_return.first == to_return.second) { + r.bucket_first = r.bucket_second = this->priv_invalid_bucket(); + } + else if (to_return.second != this->priv_invalid_local_it()) { + r.bucket_first = this->priv_bucket_ptr(n_bucket); + } + else{ + r.bucket_first = this->priv_bucket_ptr(n_bucket); + const size_type max_bucket = this->bucket_count(); + do{ + ++n_bucket; + } while (n_bucket != max_bucket && this->priv_bucket_empty(n_bucket)); + + if (n_bucket == max_bucket){ + r.bucket_second = this->priv_invalid_bucket(); + } + else{ + r.bucket_second = this->priv_bucket_ptr(n_bucket); + r.second = siterator(r.bucket_second->begin_ptr()); } } - return to_return; + + return r; } - size_type priv_get_bucket_num(siterator it) BOOST_NOEXCEPT - { return this->priv_get_bucket_num_hash_dispatch(it, store_hash_t()); } + BOOST_INTRUSIVE_FORCEINLINE size_type priv_get_bucket_num(const_iterator it) BOOST_NOEXCEPT + { return this->priv_get_bucket_num(it, linear_buckets_t()); } + + BOOST_INTRUSIVE_FORCEINLINE size_type priv_get_bucket_num(const_iterator it, detail::true_) BOOST_NOEXCEPT //linear + { return size_type(it.get_bucket_ptr() - this->priv_bucket_pointer()); } + + BOOST_INTRUSIVE_FORCEINLINE size_type priv_get_bucket_num(const_iterator it, detail::false_) BOOST_NOEXCEPT //!linear + { return this->priv_get_bucket_num_hash_dispatch(it.slist_it(), store_hash_t()); } - size_type priv_get_bucket_num_hash_dispatch(siterator it, detail::true_) BOOST_NOEXCEPT //store_hash + BOOST_INTRUSIVE_FORCEINLINE size_type priv_get_bucket_num_hash_dispatch(siterator it, detail::true_) BOOST_NOEXCEPT //store_hash + { return (size_type)this->priv_hash_to_bucket(this->priv_stored_hash(it, store_hash_t())); } + + size_type priv_get_bucket_num_hash_dispatch(siterator it, detail::false_) BOOST_NOEXCEPT //NO store_hash { - return (size_type)this->priv_hash_to_bucket - (this->priv_stored_hash(it.pointed_node(), store_hash_t())); + const bucket_type &f = this->priv_bucket(0u); + slist_node_ptr bb = group_functions_t::get_bucket_before_begin + ( this->priv_bucket_lbbegin(0u).pointed_node() + , this->priv_bucket_lbbegin(this->priv_bucket_count() - 1u).pointed_node() + , it.pointed_node() + , optimize_multikey_t()); + + //Now get the bucket_impl from the iterator + const bucket_type &b = static_cast(*bb); + //Now just calculate the index b has in the bucket array + return static_cast(&b - &f); } - size_type priv_get_bucket_num_hash_dispatch(siterator it, detail::false_) BOOST_NOEXCEPT //NO store_hash - { return (size_type)this->priv_get_bucket_num_no_hash_store(it, optimize_multikey_t()); } - static siterator priv_get_previous(bucket_type &b, siterator i) BOOST_NOEXCEPT - { return bucket_plus_vtraits_t::priv_get_previous(b, i, optimize_multikey_t()); } + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_get_bucket_ptr(const_iterator it) BOOST_NOEXCEPT + { return this->priv_get_bucket_ptr(it, linear_buckets_t()); } + + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_get_bucket_ptr(const_iterator it, detail::true_) BOOST_NOEXCEPT //linear + { return it.get_bucket_ptr(); } + + BOOST_INTRUSIVE_FORCEINLINE bucket_ptr priv_get_bucket_ptr(const_iterator it, detail::false_) BOOST_NOEXCEPT //!linear + { return this->priv_bucket_ptr(this->priv_get_bucket_num_hash_dispatch(it.slist_it(), store_hash_t())); } /// @endcond }; /// @cond template < class T - , bool UniqueKeys , class PackedOptions > struct make_bucket_traits { //Real value traits must be calculated from options typedef typename detail::get_value_traits - ::type value_traits; + ::type value_traits; typedef typename PackedOptions::bucket_traits specified_bucket_traits; //Real bucket traits must be calculated from options and calculated value_traits - typedef typename get_slist_impl - ::type - >::type slist_impl; + typedef bucket_traits_impl + < typename unordered_bucket_ptr_impl + ::type + , std::size_t> bucket_traits_t; typedef typename detail::if_c< detail::is_same < specified_bucket_traits , default_bucket_traits >::value - , bucket_traits_impl + , bucket_traits_t , specified_bucket_traits >::type type; }; @@ -3555,6 +3874,7 @@ template #endif struct make_hashtable @@ -3563,7 +3883,7 @@ struct make_hashtable typedef typename pack_options < hashtable_defaults, #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) - O1, O2, O3, O4, O5, O6, O7, O8, O9, O10 + O1, O2, O3, O4, O5, O6, O7, O8, O9, O10, O11 #else Options... #endif @@ -3573,7 +3893,7 @@ struct make_hashtable ::type value_traits; typedef typename make_bucket_traits - ::type bucket_traits; + ::type bucket_traits; typedef hashtable_impl < value_traits @@ -3588,6 +3908,7 @@ struct make_hashtable |(std::size_t(packed_options::cache_begin)*hash_bool_flags::cache_begin_pos) |(std::size_t(packed_options::compare_hash)*hash_bool_flags::compare_hash_pos) |(std::size_t(packed_options::incremental)*hash_bool_flags::incremental_pos) + |(std::size_t(packed_options::linear_buckets)*hash_bool_flags::linear_buckets_pos) > implementation_defined; /// @endcond diff --git a/include/boost/intrusive/intrusive_fwd.hpp b/include/boost/intrusive/intrusive_fwd.hpp index 37cdb661..127dbc9e 100644 --- a/include/boost/intrusive/intrusive_fwd.hpp +++ b/include/boost/intrusive/intrusive_fwd.hpp @@ -570,6 +570,7 @@ template , class O8 = void , class O9 = void , class O10 = void + , class O11 = void > #else template @@ -589,6 +590,7 @@ template , class O8 = void , class O9 = void , class O10 = void + , class O11 = void > #else template diff --git a/include/boost/intrusive/options.hpp b/include/boost/intrusive/options.hpp index 8bf9ca8a..99aa3cdf 100644 --- a/include/boost/intrusive/options.hpp +++ b/include/boost/intrusive/options.hpp @@ -241,6 +241,11 @@ BOOST_INTRUSIVE_OPTION_CONSTANT(compare_hash, bool, Enabled, compare_hash) //!(rehashing the whole bucket array) is not admisible. BOOST_INTRUSIVE_OPTION_CONSTANT(incremental, bool, Enabled, incremental) +//!This option setter specifies if the buckets (which form a singly linked lists of nodes) +//!are linear (true) or circular (false, default value). Linear buckets can improve performance +//!in some cases, but the container loses some features like obtaining an iterator from a value. +BOOST_INTRUSIVE_OPTION_CONSTANT(linear_buckets, bool, Enabled, linear_buckets) + /// @cond struct hook_defaults diff --git a/include/boost/intrusive/priority_compare.hpp b/include/boost/intrusive/priority_compare.hpp index 3cf2b1c6..3e83ada1 100644 --- a/include/boost/intrusive/priority_compare.hpp +++ b/include/boost/intrusive/priority_compare.hpp @@ -28,8 +28,13 @@ namespace intrusive { /// @cond -template -void priority_order(); +namespace adldft { + +template +BOOST_INTRUSIVE_FORCEINLINE bool priority_order(const T &t, const U &u) +{ return t < u; } + +} //namespace adldft { /// @endcond @@ -43,6 +48,7 @@ struct priority_compare BOOST_INTRUSIVE_FORCEINLINE bool operator()(const T &val, const T &val2) const { + using adldft::priority_order; return priority_order(val, val2); } }; @@ -53,6 +59,7 @@ struct priority_compare template BOOST_INTRUSIVE_FORCEINLINE bool operator()(const T &t, const U &u) const { + using adldft::priority_order; return priority_order(t, u); } }; diff --git a/include/boost/intrusive/unordered_set.hpp b/include/boost/intrusive/unordered_set.hpp index 154704a1..4369751c 100644 --- a/include/boost/intrusive/unordered_set.hpp +++ b/include/boost/intrusive/unordered_set.hpp @@ -67,7 +67,9 @@ template template #endif class unordered_set_impl + #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED : public hashtable_impl + #endif { /// @cond private: @@ -115,7 +117,6 @@ class unordered_set_impl typedef typename implementation_defined::node node; typedef typename implementation_defined::node_ptr node_ptr; typedef typename implementation_defined::const_node_ptr const_node_ptr; - typedef typename implementation_defined::node_algorithms node_algorithms; public: @@ -420,6 +421,7 @@ template #endif struct make_unordered_set @@ -428,7 +430,7 @@ struct make_unordered_set typedef typename pack_options < hashtable_defaults, #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) - O1, O2, O3, O4, O5, O6, O7, O8, O9, O10 + O1, O2, O3, O4, O5, O6, O7, O8, O9, O10, O11 #else Options... #endif @@ -438,7 +440,7 @@ struct make_unordered_set ::type value_traits; typedef typename make_bucket_traits - ::type bucket_traits; + ::type bucket_traits; typedef unordered_set_impl < value_traits @@ -453,6 +455,7 @@ struct make_unordered_set | (std::size_t(packed_options::cache_begin)*hash_bool_flags::cache_begin_pos) | (std::size_t(packed_options::compare_hash)*hash_bool_flags::compare_hash_pos) | (std::size_t(packed_options::incremental)*hash_bool_flags::incremental_pos) + | (std::size_t(packed_options::linear_buckets)*hash_bool_flags::linear_buckets_pos) > implementation_defined; /// @endcond @@ -462,27 +465,26 @@ struct make_unordered_set #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) -template +template #else template #endif class unordered_set : public make_unordered_set::type { - typedef typename make_unordered_set - ::type Base; + >::type Base; //Assert if passed value traits are compatible with the type BOOST_STATIC_ASSERT((detail::is_same::value)); @@ -526,11 +528,11 @@ class unordered_set template BOOST_INTRUSIVE_FORCEINLINE void clone_from(const unordered_set &src, Cloner cloner, Disposer disposer) - { Base::clone_from(src, cloner, disposer); } + { this->Base::clone_from(src, cloner, disposer); } template BOOST_INTRUSIVE_FORCEINLINE void clone_from(BOOST_RV_REF(unordered_set) src, Cloner cloner, Disposer disposer) - { Base::clone_from(BOOST_MOVE_BASE(Base, src), cloner, disposer); } + { this->Base::clone_from(BOOST_MOVE_BASE(Base, src), cloner, disposer); } }; #endif @@ -577,7 +579,9 @@ template template #endif class unordered_multiset_impl + #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED : public hashtable_impl + #endif { /// @cond private: @@ -613,7 +617,6 @@ class unordered_multiset_impl typedef typename implementation_defined::node node; typedef typename implementation_defined::node_ptr node_ptr; typedef typename implementation_defined::const_node_ptr const_node_ptr; - typedef typename implementation_defined::node_algorithms node_algorithms; public: @@ -872,6 +875,7 @@ template #endif struct make_unordered_multiset @@ -880,7 +884,7 @@ struct make_unordered_multiset typedef typename pack_options < hashtable_defaults, #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) - O1, O2, O3, O4, O5, O6, O7, O8, O9, O10 + O1, O2, O3, O4, O5, O6, O7, O8, O9, O10, O11 #else Options... #endif @@ -890,7 +894,7 @@ struct make_unordered_multiset ::type value_traits; typedef typename make_bucket_traits - ::type bucket_traits; + ::type bucket_traits; typedef unordered_multiset_impl < value_traits @@ -905,6 +909,7 @@ struct make_unordered_multiset | (std::size_t(packed_options::cache_begin)*hash_bool_flags::cache_begin_pos) | (std::size_t(packed_options::compare_hash)*hash_bool_flags::compare_hash_pos) | (std::size_t(packed_options::incremental)*hash_bool_flags::incremental_pos) + | (std::size_t(packed_options::linear_buckets)*hash_bool_flags::linear_buckets_pos) > implementation_defined; /// @endcond @@ -914,14 +919,14 @@ struct make_unordered_multiset #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) -template +template #else template #endif class unordered_multiset : public make_unordered_multiset BOOST_INTRUSIVE_FORCEINLINE void clone_from(const unordered_multiset &src, Cloner cloner, Disposer disposer) - { Base::clone_from(src, cloner, disposer); } + { this->Base::clone_from(src, cloner, disposer); } template BOOST_INTRUSIVE_FORCEINLINE void clone_from(BOOST_RV_REF(unordered_multiset) src, Cloner cloner, Disposer disposer) - { Base::clone_from(BOOST_MOVE_BASE(Base, src), cloner, disposer); } + { this->Base::clone_from(BOOST_MOVE_BASE(Base, src), cloner, disposer); } }; #endif diff --git a/include/boost/intrusive/unordered_set_hook.hpp b/include/boost/intrusive/unordered_set_hook.hpp index 08b07024..cfa4f259 100644 --- a/include/boost/intrusive/unordered_set_hook.hpp +++ b/include/boost/intrusive/unordered_set_hook.hpp @@ -108,10 +108,10 @@ struct unordered_group_adapter typedef typename NodeTraits::node_ptr node_ptr; typedef typename NodeTraits::const_node_ptr const_node_ptr; - static node_ptr get_next(const_node_ptr n) + BOOST_INTRUSIVE_FORCEINLINE static node_ptr get_next(const_node_ptr n) { return NodeTraits::get_prev_in_group(n); } - static void set_next(node_ptr n, node_ptr next) + BOOST_INTRUSIVE_FORCEINLINE static void set_next(node_ptr n, node_ptr next) { NodeTraits::set_prev_in_group(n, next); } }; diff --git a/test/bptr_value.hpp b/test/bptr_value.hpp index d4313c57..8e86e3b3 100644 --- a/test/bptr_value.hpp +++ b/test/bptr_value.hpp @@ -22,8 +22,6 @@ #include -namespace boost { -namespace intrusive { struct BPtr_Value { @@ -210,6 +208,8 @@ struct ValueContainer< BPtr_Value > typedef bounded_reference_cont< BPtr_Value > type; }; +namespace boost { +namespace intrusive { namespace test{ template <> diff --git a/test/int_holder.hpp b/test/int_holder.hpp index 785a3672..5ae2272a 100644 --- a/test/int_holder.hpp +++ b/test/int_holder.hpp @@ -14,9 +14,6 @@ #include -namespace boost{ -namespace intrusive{ - struct int_holder { explicit int_holder(int value = 0) @@ -111,7 +108,4 @@ struct int_priority_of_value { return tv.int_value(); } }; -} //namespace boost{ -} //namespace intrusive{ - #endif //BOOST_INTRUSIVE_DETAIL_INT_HOLDER_HPP diff --git a/test/itestvalue.hpp b/test/itestvalue.hpp index 6188dfa5..c9e39e49 100644 --- a/test/itestvalue.hpp +++ b/test/itestvalue.hpp @@ -22,9 +22,6 @@ #include #include -namespace boost{ -namespace intrusive{ - struct testvalue_filler { void *dummy_[3]; @@ -134,35 +131,48 @@ struct testvalue boost::hash hasher; return hasher((&t)->int_value()); } -}; + /* + static std::size_t priority_hash(const testvalue &t) + { return boost::hash()((&t)->int_value()); } -template -std::size_t priority_hash(const T &t) -{ return boost::hash()((&t)->int_value()); } + static std::size_t priority_hash(int i) + { return boost::hash()(i); } -std::size_t priority_hash(int i) -{ return boost::hash()(i); } + template + static bool priority_order_impl(const T& t1, const U& t2) + { + std::size_t hash1 = (priority_hash)(t1); + boost::hash_combine(hash1, -hash1); + std::size_t hash2 = (priority_hash)(t2); + boost::hash_combine(hash2, -hash2); + return hash1 < hash2; + } -template -bool priority_order(const T& t1, const U& t2) -{ - std::size_t hash1 = (priority_hash)(t1); - boost::hash_combine(hash1, -hash1); - std::size_t hash2 = (priority_hash)(t2); - boost::hash_combine(hash2, -hash2); - return hash1 < hash2; -} + friend bool priority_order(const testvalue &t1, int t2) + { return (priority_order_impl)(t1, t2); } -template < typename Node_Algorithms, class Hooks> -void swap_nodes(testvalue& lhs, testvalue& rhs) + friend bool priority_order(int t1, const testvalue &t2) + { return (priority_order_impl)(t1, t2); } +*/ + template < typename Node_Algorithms > + friend void swap_nodes(testvalue& lhs, testvalue& rhs) + { lhs.swap_nodes(rhs); } + + friend std::ostream& operator<< + (std::ostream& s, const testvalue& t) + { return s << t.value_.int_value(); } +}; + +/* +bool priority_order(int t1, int t2) { - lhs.swap_nodes(rhs); + std::size_t hash1 = boost::hash()(t1); + boost::hash_combine(hash1, &t1); + std::size_t hash2 = boost::hash()(t2); + boost::hash_combine(hash2, &t2); + return hash1 < hash2; } - -template -std::ostream& operator<< - (std::ostream& s, const testvalue& t) -{ return s << t.value_.int_value(); } +*/ struct even_odd { @@ -206,13 +216,14 @@ struct ValueContainer< testvalue > template < typename Pointer > class heap_node_holder { - typedef typename pointer_traits::element_type element_type; + typedef boost::intrusive::pointer_traits ptrtraits_t; + typedef typename ptrtraits_t::element_type element_type; typedef Pointer pointer; - typedef typename pointer_rebind::type const_pointer; + typedef typename boost::intrusive::pointer_rebind::type const_pointer; public: heap_node_holder() - : m_ptr(pointer_traits::pointer_to(*new element_type)) + : m_ptr(ptrtraits_t::pointer_to(*new element_type)) {} ~heap_node_holder() @@ -235,50 +246,38 @@ struct testvalue_traits typedef testvalue< Hooks > value_type; //base - typedef typename detail::get_base_value_traits + typedef typename boost::intrusive::detail::get_base_value_traits < value_type , typename Hooks::base_hook_type >::type base_value_traits; //auto-base - typedef typename detail::get_base_value_traits + typedef typename boost::intrusive::detail::get_base_value_traits < value_type , typename Hooks::auto_base_hook_type >::type auto_base_value_traits; //member - typedef typename detail::get_member_value_traits - < member_hook + typedef typename boost::intrusive::detail::get_member_value_traits + < boost::intrusive::member_hook < value_type , typename Hooks::member_hook_type , &value_type::node_ > >::type member_value_traits; //auto-member - typedef typename detail::get_member_value_traits - < member_hook + typedef typename boost::intrusive::detail::get_member_value_traits + < boost::intrusive::member_hook < value_type , typename Hooks::auto_member_hook_type , &value_type::auto_node_ > >::type auto_member_value_traits; //nonmember - typedef nonhook_node_member_value_traits + typedef boost::intrusive::nonhook_node_member_value_traits < value_type , typename Hooks::nonhook_node_member_type , &value_type::nhn_member_ - , safe_link + , boost::intrusive::safe_link > nonhook_value_traits; }; -} //namespace intrusive{ -} //namespace boost{ - -bool priority_order(int t1, int t2) -{ - std::size_t hash1 = boost::hash()(t1); - boost::hash_combine(hash1, &t1); - std::size_t hash2 = boost::hash()(t2); - boost::hash_combine(hash2, &t2); - return hash1 < hash2; -} - #endif diff --git a/test/stateful_value_traits_test.cpp b/test/stateful_value_traits_test.cpp index 98f557ec..a9e8bfbf 100644 --- a/test/stateful_value_traits_test.cpp +++ b/test/stateful_value_traits_test.cpp @@ -113,8 +113,8 @@ int main() Slist my_slist(slist_traits(values, slist_hook_array)); Set my_set (std::less(), rbtree_traits(values, rbtree_hook_array)); Uset my_uset ( Uset::bucket_traits(buckets, NumElements) - , boost::hash() - , std::equal_to() + , Uset::hasher() + , Uset::key_equal() , slist_traits(values, uset_hook_array) ); diff --git a/test/test_container.hpp b/test/test_container.hpp index 36a62894..138e9309 100644 --- a/test/test_container.hpp +++ b/test/test_container.hpp @@ -240,12 +240,15 @@ void test_common_unordered_and_associative_container(Container & c, Data & d, bo // //Maximum fallbacks to the highest possible value typename Container::size_type sz = Container::suggested_upper_bucket_count(size_type(-1)); - BOOST_TEST( sz > size_type(-1)/2 ); - //In the rest of cases the upper bound is returned - sz = Container::suggested_upper_bucket_count(size_type(-1)/2); - BOOST_TEST( sz >= size_type(-1)/2 ); + //If size_type is big enough the upper bound is returned + BOOST_IF_CONSTEXPR(sizeof(size_type) < sizeof(std::size_t)){ + sz = Container::suggested_upper_bucket_count(size_type(-1)/2); + BOOST_TEST( sz >= size_type(-1)/2 ); + } sz = Container::suggested_upper_bucket_count(size_type(-1)/4); BOOST_TEST( sz >= size_type(-1)/4 ); + sz = Container::suggested_upper_bucket_count(size_type(-1) / 8); + BOOST_TEST(sz >= size_type(-1) / 8); sz = Container::suggested_upper_bucket_count(0); BOOST_TEST( sz > 0 ); // diff --git a/test/unordered_multiset_test.cpp b/test/unordered_multiset_test.cpp index aa544fd8..95b1cbe5 100644 --- a/test/unordered_multiset_test.cpp +++ b/test/unordered_multiset_test.cpp @@ -27,7 +27,8 @@ using namespace boost::intrusive; -template < class ValueTraits, bool ConstantTimeSize, bool CacheBegin, bool CompareHash, bool Incremental, bool Map, bool DefaultHolder > +template < class ValueTraits, bool ConstantTimeSize, bool CacheBegin, bool CompareHash + , bool Incremental, bool Map, bool DefaultHolder, bool LinearBuckets> struct rebinder { typedef unordered_rebinder_common common_t; @@ -45,6 +46,7 @@ struct rebinder , cache_begin , compare_hash , incremental + , linear_buckets , typename common_t::holder_opt , typename common_t::key_of_value_opt , Option1 @@ -62,11 +64,11 @@ enum HookType NonMember }; -template +template class test_main_template; -template -class test_main_template +template +class test_main_template { public: static void execute() @@ -87,13 +89,14 @@ class test_main_template::type base_hook_t; test::test_unordered < //cache_begin, compare_hash, incremental - rebinder + rebinder< base_hook_t, ConstantTimeSize, ConstantTimeSize + , !ConstantTimeSize, !!ConstantTimeSize, Map, DefaultHolder, LinearBuckets> >::test_all(data); } }; -template -class test_main_template +template +class test_main_template { public: static void execute() @@ -114,13 +117,14 @@ class test_main_template::type member_hook_t; test::test_unordered < //cache_begin, compare_hash, incremental - rebinder + rebinder >::test_all(data); } }; -template -class test_main_template +template +class test_main_template { public: @@ -137,7 +141,8 @@ class test_main_template + rebinder< typename testval_traits_t::nonhook_value_traits + , ConstantTimeSize, false, false, false, Map, DefaultHolder, LinearBuckets> >::test_all(data); } }; @@ -147,21 +152,31 @@ int main() //VoidPointer x ConstantTimeSize x Map x DefaultHolder //void pointer - test_main_template::execute(); - test_main_template::execute(); - test_main_template::execute(); - test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); //smart_ptr - test_main_template, false, false, false, Member>::execute(); - test_main_template, false, true, false, NonMember>::execute(); - test_main_template, true, false, false, Base>::execute(); - test_main_template, true, true, false, Member>::execute(); + test_main_template, false, false, false, Member, false>::execute(); + test_main_template, false, true, false, NonMember, false>::execute(); + test_main_template, true, false, false, Base, false>::execute(); + test_main_template, true, true, false, Member, false>::execute(); + test_main_template, true, true, false, Base, true>::execute(); + + test_main_template, false, false, false, Member, true>::execute(); + test_main_template, false, true, false, NonMember, true>::execute(); + test_main_template, true, false, false, Base, true>::execute(); ////bounded_ptr (bool ConstantTimeSize, bool Map) //test_main_template_bptr< false, false >::execute(); //test_main_template_bptr< false, true >::execute(); //test_main_template_bptr< true, false >::execute(); //test_main_template_bptr< true, true >::execute(); + return boost::report_errors(); } diff --git a/test/unordered_set_test.cpp b/test/unordered_set_test.cpp index 10c23e0d..76941444 100644 --- a/test/unordered_set_test.cpp +++ b/test/unordered_set_test.cpp @@ -25,7 +25,8 @@ using namespace boost::intrusive; -template < class ValueTraits, bool ConstantTimeSize, bool CacheBegin, bool CompareHash, bool Incremental, bool Map, bool DefaultHolder > +template < class ValueTraits, bool ConstantTimeSize, bool CacheBegin, bool CompareHash + , bool Incremental, bool Map, bool DefaultHolder, bool LinearBuckets > struct rebinder { typedef unordered_rebinder_common common_t; @@ -43,6 +44,7 @@ struct rebinder , cache_begin , compare_hash , incremental + , linear_buckets , typename common_t::holder_opt , typename common_t::key_of_value_opt , size_type @@ -61,11 +63,11 @@ enum HookType NonMember }; -template +template class test_main_template; -template -class test_main_template +template +class test_main_template { public: static void execute() @@ -86,13 +88,14 @@ class test_main_template::type base_hook_t; test::test_unordered < //cache_begin, compare_hash, incremental - rebinder + rebinder< base_hook_t, ConstantTimeSize, ConstantTimeSize + , !ConstantTimeSize, !!ConstantTimeSize, Map, DefaultHolder, LinearBuckets> >::test_all(data); } }; -template -class test_main_template +template +class test_main_template { public: static void execute() @@ -113,13 +116,13 @@ class test_main_template::type member_hook_t; test::test_unordered < //cache_begin, compare_hash, incremental - rebinder + rebinder >::test_all(data); } }; -template -class test_main_template +template +class test_main_template { public: @@ -136,7 +139,7 @@ class test_main_template + rebinder >::test_all(data); } }; @@ -146,16 +149,25 @@ int main() //VoidPointer x ConstantTimeSize x Map x DefaultHolder //void pointer - test_main_template::execute(); - test_main_template::execute(); - test_main_template::execute(); - test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); + test_main_template::execute(); //smart_ptr - test_main_template, false, false, false, Member>::execute(); - test_main_template, false, true, false, NonMember>::execute(); - test_main_template, true, false, false, Base>::execute(); - test_main_template, true, true, false, Member>::execute(); + test_main_template, false, false, false, Member, false>::execute(); + test_main_template, false, true, false, NonMember, false>::execute(); + test_main_template, true, false, false, Base, false>::execute(); + test_main_template, true, true, false, Member, false>::execute(); + + test_main_template, false, false, false, Member, true>::execute(); + test_main_template, false, true, false, NonMember, true>::execute(); + test_main_template, true, false, false, Base, true>::execute(); ////bounded_ptr (bool ConstantTimeSize, bool Map) //test_main_template_bptr< false, false >::execute(); diff --git a/test/unordered_test.hpp b/test/unordered_test.hpp index 6be01275..e5c91c01 100644 --- a/test/unordered_test.hpp +++ b/test/unordered_test.hpp @@ -50,12 +50,15 @@ void test_unordered::test_all (value_cont_type& values) { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; + typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; + { - typename unordered_type::bucket_type buckets [BucketSize]; + typename unordered_type::bucket_type buckets [BucketSize + ExtraBuckets]; unordered_type testset - (bucket_traits(pointer_traits::pointer_to(buckets[0]), BucketSize)); + (bucket_traits(pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); testset.insert(values.begin(), values.end()); test::test_container(testset); testset.clear(); @@ -74,9 +77,9 @@ void test_unordered::test_all (value_cont_type& values) value_cont_type vals(BucketSize); for (std::size_t i = 0; i < BucketSize; ++i) (&vals[i])->value_ = (int)i; - typename unordered_type::bucket_type buckets [BucketSize]; + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; unordered_type testset(bucket_traits( - pointer_traits::pointer_to(buckets[0]), BucketSize)); + pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); testset.insert(vals.begin(), vals.end()); test::test_iterator_forward(testset); } @@ -95,6 +98,8 @@ void test_unordered::test_impl() { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; + typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; @@ -102,9 +107,9 @@ void test_unordered::test_impl() for (std::size_t i = 0u; i < 5u; ++i) values[i].value_ = (int)i; - typename unordered_type::bucket_type buckets [BucketSize]; + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; unordered_type testset(bucket_traits( - pointer_traits::pointer_to(buckets[0]), BucketSize)); + pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); for (std::size_t i = 0u; i < 5u; ++i) testset.insert (values[i]); @@ -123,13 +128,15 @@ void test_unordered::test_sort(value_cont_type& values) { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; + typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; - typename unordered_type::bucket_type buckets [BucketSize]; + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; unordered_type testset1 (values.begin(), values.end(), bucket_traits - (pointer_traits::pointer_to(buckets[0]), BucketSize)); + (pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); if(unordered_type::incremental){ { int init_values [] = { 4, 5, 1, 2, 2, 3 }; @@ -147,16 +154,16 @@ void test_unordered::test_sort(value_cont_type& values) template void test_unordered::test_insert(value_cont_type& values, detail::false_) //not multikey { - typedef typename ContainerDefiner::template container <>::type unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; typedef typename unordered_set_type::key_of_value key_of_value; - typename unordered_set_type::bucket_type buckets [BucketSize]; + const std::size_t ExtraBuckets = unordered_set_type::bucket_overhead; + typename unordered_set_type::bucket_type buckets[BucketSize + ExtraBuckets]; unordered_set_type testset(bucket_traits( pointer_traits:: - pointer_to(buckets[0]), BucketSize)); + pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); testset.insert(&values[0] + 2, &values[0] + 5); typename unordered_set_type::insert_commit_data commit_data; @@ -206,15 +213,16 @@ void test_unordered::test_insert(value_cont_type& values, deta { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; typedef typename unordered_type::iterator iterator; typedef typename unordered_type::key_type key_type; { - typename unordered_type::bucket_type buckets [BucketSize]; + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; unordered_type testset(bucket_traits( - pointer_traits::pointer_to(buckets[0]), BucketSize)); + pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); testset.insert(&values[0] + 2, &values[0] + 5); @@ -251,9 +259,9 @@ void test_unordered::test_insert(value_cont_type& values, deta BOOST_TEST (testset.empty() == true); //Now with a single bucket - typename unordered_type::bucket_type single_bucket[1]; + typename unordered_type::bucket_type single_bucket[1u + ExtraBuckets]; unordered_type testset2(bucket_traits( - pointer_traits::pointer_to(single_bucket[0]), 1)); + pointer_traits::pointer_to(single_bucket[0]), sizeof(single_bucket)/sizeof(*single_bucket))); testset2.insert(&values[0], &values[0] + values.size()); BOOST_TEST (testset2.erase(key_type(5)) == 1); BOOST_TEST (testset2.erase(key_type(2)) == 2); @@ -294,9 +302,9 @@ void test_unordered::test_insert(value_cont_type& values, deta BOOST_TEST (testset.empty() == true); //Now with a single bucket - typename unordered_type::bucket_type single_bucket[1]; + typename unordered_type::bucket_type single_bucket[1u + ExtraBuckets]; unordered_type testset2(bucket_traits( - pointer_traits::pointer_to(single_bucket[0]), 1)); + pointer_traits::pointer_to(single_bucket[0]), sizeof(single_bucket)/sizeof(*single_bucket))); testset2.insert(&values[0], &values[0] + values.size()); BOOST_TEST (testset2.erase(key_type(5)) == 1); BOOST_TEST (testset2.erase(key_type(2)) == 2); @@ -310,13 +318,13 @@ void test_unordered::test_insert(value_cont_type& values, deta //Now erase just one per loop const int random_init[] = { 3, 2, 4, 1, 5, 2, 2 }; const std::size_t random_size = sizeof(random_init)/sizeof(random_init[0]); - typename unordered_type::bucket_type single_bucket[1]; + typename unordered_type::bucket_type single_bucket[1u + ExtraBuckets]; for(std::size_t i = 0u, max = random_size; i != max; ++i){ value_cont_type data (random_size); for (std::size_t j = 0; j < random_size; ++j) data[j].value_ = random_init[j]; unordered_type testset_new(bucket_traits( - pointer_traits::pointer_to(single_bucket[0]), 1)); + pointer_traits::pointer_to(single_bucket[0]), sizeof(single_bucket)/sizeof(*single_bucket))); testset_new.insert(&data[0], &data[0]+max); testset_new.erase(testset_new.iterator_to(data[i])); BOOST_TEST (testset_new.size() == (max -1)); @@ -327,16 +335,14 @@ void test_unordered::test_insert(value_cont_type& values, deta const std::size_t LoadFactor = 3; const std::size_t NumIterations = BucketSize*LoadFactor; value_cont_type random_init(NumIterations);//Preserve memory - value_cont_type set_tester; - set_tester.reserve(NumIterations); //Initialize values for (std::size_t i = 0u; i < NumIterations; ++i){ - random_init[i].value_ = (int)i*2;//(i/LoadFactor)*LoadFactor; + random_init[i].value_ = (int)i*2; } - typename unordered_type::bucket_type buckets [BucketSize]; - bucket_traits btraits(pointer_traits::pointer_to(buckets[0]), BucketSize); + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; + bucket_traits btraits(pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets)); for(std::size_t initial_pos = 0; initial_pos != (NumIterations+1u); ++initial_pos){ for(std::size_t final_pos = initial_pos; final_pos != (NumIterations+1); ++final_pos){ @@ -370,20 +376,25 @@ void test_unordered::test_insert(value_cont_type& values, deta //Now test... BOOST_TEST ((random_init.size() - erased_cnt) == testset.size()); - //Create an ordered copy of the intrusive container - set_tester.insert(set_tester.end(), testset.begin(), testset.end()); - std::sort(set_tester.begin(), set_tester.end()); - { - typename value_cont_type::iterator it = set_tester.begin(), itend = set_tester.end(); - typename value_cont_type::iterator random_init_it(random_init.begin()); - for( ; it != itend; ++it){ - while(!random_init_it->is_linked()) + //for non-linear buckets is_linked is a reliable marker for a node + //inserted in a hash map, but not for linear buckets, which are null-ended + BOOST_IF_CONSTEXPR(!unordered_type::linear_buckets){ + value_cont_type set_tester; + set_tester.reserve(NumIterations); + //Create an ordered copy of the intrusive container + set_tester.insert(set_tester.end(), testset.begin(), testset.end()); + std::sort(set_tester.begin(), set_tester.end()); + { + typename value_cont_type::iterator it = set_tester.begin(), itend = set_tester.end(); + typename value_cont_type::iterator random_init_it(random_init.begin()); + for( ; it != itend; ++it){ + while(!random_init_it->is_linked()) + ++random_init_it; + BOOST_TEST(*it == *random_init_it); ++random_init_it; - BOOST_TEST(*it == *random_init_it); - ++random_init_it; + } } } - set_tester.clear(); } } } @@ -395,16 +406,17 @@ void test_unordered::test_swap(value_cont_type& values) { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; - typename unordered_type::bucket_type buckets [BucketSize]; + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; - typename unordered_type::bucket_type buckets2 [BucketSize]; + typename unordered_type::bucket_type buckets2[BucketSize + ExtraBuckets]; unordered_type testset1(&values[0], &values[0] + 2, - bucket_traits(pointer_traits::pointer_to(buckets[0]), BucketSize)); + bucket_traits(pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); unordered_type testset2(bucket_traits( - pointer_traits::pointer_to(buckets2[0]), BucketSize)); + pointer_traits::pointer_to(buckets2[0]), sizeof(buckets2)/sizeof(*buckets2))); testset2.insert (&values[0] + 2, &values[0] + 6); testset1.swap (testset2); @@ -443,14 +455,16 @@ void test_unordered::test_rehash(value_cont_type& values, deta typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; + typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; //Build a uset - typename unordered_type::bucket_type buckets1 [BucketSize]; - typename unordered_type::bucket_type buckets2 [BucketSize*2]; + typename unordered_type::bucket_type buckets1[BucketSize + ExtraBuckets]; + typename unordered_type::bucket_type buckets2[BucketSize*2u + ExtraBuckets]; unordered_type testset1(&values[0], &values[0] + values.size(), bucket_traits(pointer_traits:: - pointer_to(buckets1[0]), BucketSize)); + pointer_to(buckets1[0]), sizeof(buckets1)/sizeof(*buckets1))); //Test current state BOOST_TEST(testset1.split_count() == BucketSize/2); { int init_values [] = { 4, 5, 1, 2, 2, 3 }; @@ -478,8 +492,8 @@ void test_unordered::test_rehash(value_cont_type& values, deta // //This incremental rehash should fail because the new size is not twice the original BOOST_TEST(testset1.incremental_rehash(bucket_traits( - pointer_traits:: - pointer_to(buckets1[0]), BucketSize)) == false); + pointer_traits::pointer_to(buckets1[0]) + , sizeof(buckets1)/sizeof(*buckets1))) == false); BOOST_TEST(testset1.split_count() == BucketSize); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -490,7 +504,8 @@ void test_unordered::test_rehash(value_cont_type& values, deta //This incremental rehash should fail because the new size is not twice the original BOOST_TEST(testset1.incremental_rehash(bucket_traits( pointer_traits:: - pointer_to(buckets2[0]), BucketSize)) == false); + pointer_to(buckets2[0]) + , BucketSize + ExtraBuckets)) == false); BOOST_TEST(testset1.split_count() == BucketSize); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -499,7 +514,8 @@ void test_unordered::test_rehash(value_cont_type& values, deta //and split_count is the same as the old bucket count BOOST_TEST(testset1.incremental_rehash(bucket_traits( pointer_traits:: - pointer_to(buckets2[0]), BucketSize*2)) == true); + pointer_to(buckets2[0]) + , sizeof(buckets2)/sizeof(*buckets2))) == true); BOOST_TEST(testset1.split_count() == BucketSize); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -508,7 +524,8 @@ void test_unordered::test_rehash(value_cont_type& values, deta //and split_count is the same as the new bucket count BOOST_TEST(testset1.incremental_rehash(bucket_traits( pointer_traits:: - pointer_to(buckets1[0]), BucketSize)) == true); + pointer_to(buckets1[0]) + , sizeof(buckets1)/sizeof(*buckets1))) == true); BOOST_TEST(testset1.split_count() == BucketSize); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -516,7 +533,8 @@ void test_unordered::test_rehash(value_cont_type& values, deta //Shrink rehash testset1.rehash(bucket_traits( pointer_traits:: - pointer_to(buckets1[0]), 4)); + pointer_to(buckets1[0]) + , (sizeof(buckets1) / sizeof(*buckets1)- ExtraBuckets) / 2u + ExtraBuckets)); BOOST_TEST (testset1.incremental_rehash() == false); { int init_values [] = { 4, 5, 1, 2, 2, 3 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -524,7 +542,8 @@ void test_unordered::test_rehash(value_cont_type& values, deta //Shrink rehash again testset1.rehash(bucket_traits( pointer_traits:: - pointer_to(buckets1[0]), 2)); + pointer_to(buckets1[0]) + , (sizeof(buckets1) / sizeof(*buckets1) - ExtraBuckets) / 4u + ExtraBuckets)); BOOST_TEST (testset1.incremental_rehash() == false); { int init_values [] = { 2, 2, 4, 3, 5, 1 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -532,7 +551,11 @@ void test_unordered::test_rehash(value_cont_type& values, deta //Growing rehash testset1.rehash(bucket_traits( pointer_traits:: - pointer_to(buckets1[0]), BucketSize)); + pointer_to(buckets1[0]) + , sizeof(buckets1)/sizeof(*buckets1))); + + { int init_values [] = { 1, 2, 2, 3, 4, 5 }; + TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } //Full rehash (no effects) testset1.full_rehash(); @@ -571,39 +594,40 @@ void test_unordered::test_rehash(value_cont_type& values, deta { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; typedef typename unordered_type::bucket_traits bucket_traits; typedef typename unordered_type::bucket_ptr bucket_ptr; - typename unordered_type::bucket_type buckets1 [BucketSize]; - typename unordered_type::bucket_type buckets2 [2]; - typename unordered_type::bucket_type buckets3 [BucketSize*2]; + typename unordered_type::bucket_type buckets1[BucketSize + ExtraBuckets]; + typename unordered_type::bucket_type buckets2 [2 + ExtraBuckets]; + typename unordered_type::bucket_type buckets3[BucketSize*2 + ExtraBuckets]; unordered_type testset1(&values[0], &values[0] + 6, bucket_traits( pointer_traits:: - pointer_to(buckets1[0]), BucketSize)); + pointer_to(buckets1[0]), sizeof(buckets1)/sizeof(*buckets1))); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } testset1.rehash(bucket_traits( - pointer_traits::pointer_to(buckets2[0]), 2)); + pointer_traits::pointer_to(buckets2[0]), BucketSize/4 + ExtraBuckets)); { int init_values [] = { 4, 2, 2, 5, 3, 1 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } testset1.rehash(bucket_traits( - pointer_traits::pointer_to(buckets3[0]), BucketSize*2)); + pointer_traits::pointer_to(buckets3[0]), sizeof(buckets3) / sizeof(*buckets3))); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } //Now rehash reducing the buckets testset1.rehash(bucket_traits( - pointer_traits::pointer_to(buckets3[0]), 2)); + pointer_traits::pointer_to(buckets3[0]), BucketSize / 4 + ExtraBuckets)); { int init_values [] = { 4, 2, 2, 5, 3, 1 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } //Now rehash increasing the buckets testset1.rehash(bucket_traits( - pointer_traits::pointer_to(buckets3[0]), BucketSize*2)); + pointer_traits::pointer_to(buckets3[0]), sizeof(buckets3) / sizeof(*buckets3))); { int init_values [] = { 1, 2, 2, 3, 4, 5 }; TEST_INTRUSIVE_SEQUENCE_MAYBEUNIQUE( init_values, testset1 ); } @@ -625,10 +649,11 @@ void test_unordered::test_find(value_cont_type& values) typedef typename unordered_type::bucket_ptr bucket_ptr; typedef typename unordered_type::key_of_value key_of_value; const bool is_multikey = boost::intrusive::test::is_multikey_true::value; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; - typename unordered_type::bucket_type buckets[BucketSize]; + typename unordered_type::bucket_type buckets[BucketSize + ExtraBuckets]; unordered_type testset(values.begin(), values.end(), bucket_traits( - pointer_traits::pointer_to(buckets[0]), BucketSize)); + pointer_traits::pointer_to(buckets[0]), sizeof(buckets)/sizeof(*buckets))); typedef typename unordered_type::iterator iterator; @@ -657,6 +682,8 @@ void test_unordered::test_clone(value_cont_type& values) { typedef typename ContainerDefiner::template container <>::type unordered_type; + const std::size_t ExtraBuckets = unordered_type::bucket_overhead; + typedef typename unordered_type::value_type value_type; typedef std::multiset std_multiset_t; @@ -665,12 +692,12 @@ void test_unordered::test_clone(value_cont_type& values) { //Test with equal bucket arrays - typename unordered_type::bucket_type buckets1 [BucketSize]; - typename unordered_type::bucket_type buckets2 [BucketSize]; + typename unordered_type::bucket_type buckets1[BucketSize + ExtraBuckets]; + typename unordered_type::bucket_type buckets2[BucketSize + ExtraBuckets]; unordered_type testset1 (values.begin(), values.end(), bucket_traits( - pointer_traits::pointer_to(buckets1[0]), BucketSize)); + pointer_traits::pointer_to(buckets1[0]), sizeof(buckets1)/sizeof(*buckets1))); unordered_type testset2 (bucket_traits( - pointer_traits::pointer_to(buckets2[0]), BucketSize)); + pointer_traits::pointer_to(buckets2[0]), sizeof(buckets2)/sizeof(*buckets2))); testset2.clone_from(testset1, test::new_cloner(), test::delete_disposer()); BOOST_TEST(testset1 == testset2); @@ -692,12 +719,12 @@ void test_unordered::test_clone(value_cont_type& values) } { //Test with bigger source bucket arrays - typename unordered_type::bucket_type buckets1 [BucketSize*2]; - typename unordered_type::bucket_type buckets2 [BucketSize]; + typename unordered_type::bucket_type buckets1[BucketSize*2u + ExtraBuckets]; + typename unordered_type::bucket_type buckets2[BucketSize + ExtraBuckets]; unordered_type testset1 (values.begin(), values.end(), bucket_traits( - pointer_traits::pointer_to(buckets1[0]), BucketSize*2)); + pointer_traits::pointer_to(buckets1[0]), sizeof(buckets1)/sizeof(*buckets1))); unordered_type testset2 (bucket_traits( - pointer_traits::pointer_to(buckets2[0]), BucketSize)); + pointer_traits::pointer_to(buckets2[0]), sizeof(buckets2)/sizeof(*buckets2))); testset2.clone_from(testset1, test::new_cloner(), test::delete_disposer()); BOOST_TEST(testset1 == testset2); @@ -719,12 +746,12 @@ void test_unordered::test_clone(value_cont_type& values) } { //Test with smaller source bucket arrays - typename unordered_type::bucket_type buckets1 [BucketSize]; - typename unordered_type::bucket_type buckets2 [BucketSize*2]; + typename unordered_type::bucket_type buckets1[BucketSize + ExtraBuckets]; + typename unordered_type::bucket_type buckets2[BucketSize*2u + ExtraBuckets]; unordered_type testset1 (values.begin(), values.end(), bucket_traits( - pointer_traits::pointer_to(buckets1[0]), BucketSize)); + pointer_traits::pointer_to(buckets1[0]), sizeof(buckets1)/sizeof(*buckets1))); unordered_type testset2 (bucket_traits( - pointer_traits::pointer_to(buckets2[0]), BucketSize*2)); + pointer_traits::pointer_to(buckets2[0]), sizeof(buckets2)/sizeof(*buckets2))); testset2.clone_from(testset1, test::new_cloner(), test::delete_disposer()); BOOST_TEST(testset1 == testset2);