summaryrefslogtreecommitdiff
path: root/boost/intrusive/hashtable.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/intrusive/hashtable.hpp')
-rw-r--r--boost/intrusive/hashtable.hpp2320
1 files changed, 1255 insertions, 1065 deletions
diff --git a/boost/intrusive/hashtable.hpp b/boost/intrusive/hashtable.hpp
index 1d4f3d3f0c..125330ff6e 100644
--- a/boost/intrusive/hashtable.hpp
+++ b/boost/intrusive/hashtable.hpp
@@ -1,6 +1,6 @@
/////////////////////////////////////////////////////////////////////////////
//
-// (C) Copyright Ion Gaztanaga 2006-2013
+// (C) Copyright Ion Gaztanaga 2006-2015
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
@@ -26,6 +26,7 @@
#include <boost/intrusive/detail/node_cloner_disposer.hpp>
#include <boost/intrusive/detail/simple_disposers.hpp>
#include <boost/intrusive/detail/size_holder.hpp>
+#include <boost/intrusive/detail/iterator.hpp>
//Implementation utilities
#include <boost/intrusive/unordered_set_hook.hpp>
@@ -55,6 +56,62 @@ namespace intrusive {
/// @cond
+template<class InputIt, class T>
+InputIt priv_algo_find(InputIt first, InputIt last, const T& value)
+{
+ for (; first != last; ++first) {
+ if (*first == value) {
+ return first;
+ }
+ }
+ return last;
+}
+
+template<class InputIt, class T>
+typename boost::intrusive::iterator_traits<InputIt>::difference_type
+ priv_algo_count(InputIt first, InputIt last, const T& value)
+{
+ typename boost::intrusive::iterator_traits<InputIt>::difference_type ret = 0;
+ for (; first != last; ++first) {
+ if (*first == value) {
+ ret++;
+ }
+ }
+ return ret;
+}
+
+template <class ForwardIterator1, class ForwardIterator2>
+bool priv_algo_is_permutation(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2)
+{
+ typedef typename
+ boost::intrusive::iterator_traits<ForwardIterator2>::difference_type
+ distance_type;
+ //Efficiently compare identical prefixes: O(N) if sequences
+ //have the same elements in the same order.
+ for ( ; first1 != last1; ++first1, ++first2){
+ if (! (*first1 == *first2))
+ break;
+ }
+ if (first1 == last1){
+ return true;
+ }
+
+ //Establish last2 assuming equal ranges by iterating over the
+ //rest of the list.
+ ForwardIterator2 last2 = first2;
+ boost::intrusive::iterator_advance(last2, boost::intrusive::iterator_distance(first1, last1));
+ for(ForwardIterator1 scan = first1; scan != last1; ++scan){
+ if (scan != (priv_algo_find)(first1, scan, *scan)){
+ continue; //We've seen this one before.
+ }
+ distance_type matches = (priv_algo_count)(first2, last2, *scan);
+ if (0 == matches || (priv_algo_count)(scan, last1, *scan != matches)){
+ return false;
+ }
+ }
+ return true;
+}
+
template<int Dummy = 0>
struct prime_list_holder
{
@@ -169,35 +226,23 @@ struct unordered_bucket_ptr_impl
};
template <class T>
-struct store_hash_bool
+struct store_hash_is_true
{
template<bool Add>
- struct two_or_three {one _[2 + Add];};
- template <class U> static one test(...);
+ struct two_or_three {yes_type _[2 + Add];};
+ template <class U> static yes_type test(...);
template <class U> static two_or_three<U::store_hash> test (int);
- static const std::size_t value = sizeof(test<T>(0));
+ static const bool value = sizeof(test<T>(0)) > sizeof(yes_type)*2;
};
template <class T>
-struct store_hash_is_true
-{
- static const bool value = store_hash_bool<T>::value > sizeof(one)*2;
-};
-
-template <class T>
-struct optimize_multikey_bool
+struct optimize_multikey_is_true
{
template<bool Add>
- struct two_or_three {one _[2 + Add];};
- template <class U> static one test(...);
+ struct two_or_three {yes_type _[2 + Add];};
+ template <class U> static yes_type test(...);
template <class U> static two_or_three<U::optimize_multikey> test (int);
- static const std::size_t value = sizeof(test<T>(0));
-};
-
-template <class T>
-struct optimize_multikey_is_true
-{
- static const bool value = optimize_multikey_bool<T>::value > sizeof(one)*2;
+ static const bool value = sizeof(test<T>(0)) > sizeof(yes_type)*2;
};
struct insert_commit_data_impl
@@ -216,6 +261,23 @@ inline typename pointer_traits<SlistNodePtr>::template rebind_pointer<Node>::typ
template<class NodeTraits>
struct group_functions
{
+ // A group is reverse-linked
+ //
+ // A is "first in group"
+ // C is "last in group"
+ // __________________
+ // | _____ _____ |
+ // | | | | | | <- Group links
+ // ^ V ^ V ^ V
+ // _ _ _ _
+ // A|_| B|_| C|_| D|_|
+ //
+ // ^ | ^ | ^ | ^ V <- Bucket links
+ // _ _____| |_____| |______| |____| |
+ // |B| |
+ // ^________________________________|
+ //
+
typedef NodeTraits node_traits;
typedef unordered_group_adapter<node_traits> group_traits;
typedef typename node_traits::node_ptr node_ptr;
@@ -225,6 +287,7 @@ struct group_functions
typedef typename reduced_node_traits::node_ptr slist_node_ptr;
typedef typename reduced_node_traits::node slist_node;
typedef circular_slist_algorithms<group_traits> group_algorithms;
+ typedef circular_slist_algorithms<node_traits> node_algorithms;
static slist_node_ptr get_bucket_before_begin
(const slist_node_ptr &bucket_beg, const slist_node_ptr &bucket_end, const node_ptr &p)
@@ -260,53 +323,20 @@ struct group_functions
static node_ptr get_prev_to_first_in_group(const slist_node_ptr &bucket_node, const node_ptr &first_in_group)
{
- //Just iterate using group links and obtain the node
- //before "first_in_group)"
- node_ptr prev_node = detail::dcast_bucket_ptr<node>(bucket_node);
- node_ptr nxt(node_traits::get_next(prev_node));
- while(nxt != first_in_group){
- prev_node = group_traits::get_next(nxt);
- nxt = node_traits::get_next(prev_node);
+ node_ptr nb = detail::dcast_bucket_ptr<node>(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
}
- return prev_node;
- }
-
- static node_ptr get_first_in_group_of_last_in_group(const node_ptr &last_in_group)
- {
- //Just iterate using group links and obtain the node
- //before "last_in_group"
- node_ptr possible_first = group_traits::get_next(last_in_group);
- node_ptr possible_first_prev = group_traits::get_next(possible_first);
- // The deleted node is at the end of the group, so the
- // node in the group pointing to it is at the beginning
- // of the group. Find that to change its pointer.
- while(possible_first_prev != last_in_group){
- possible_first = possible_first_prev;
- possible_first_prev = group_traits::get_next(possible_first);
- }
- return possible_first;
+ return nb;
}
static void erase_from_group(const slist_node_ptr &end_ptr, const node_ptr &to_erase_ptr, detail::true_)
{
- node_ptr nxt_ptr(node_traits::get_next(to_erase_ptr));
- node_ptr prev_in_group_ptr(group_traits::get_next(to_erase_ptr));
- bool last_in_group = (end_ptr == nxt_ptr) ||
- (group_traits::get_next(nxt_ptr) != to_erase_ptr);
- bool is_first_in_group = node_traits::get_next(prev_in_group_ptr) != to_erase_ptr;
-
- if(is_first_in_group && last_in_group){
- group_algorithms::init(to_erase_ptr);
- }
- else if(is_first_in_group){
- group_algorithms::unlink_after(nxt_ptr);
- }
- else if(last_in_group){
- node_ptr first_in_group =
- get_first_in_group_of_last_in_group(to_erase_ptr);
- group_algorithms::unlink_after(first_in_group);
- }
- else{
+ node_ptr const nxt_ptr(node_traits::get_next(to_erase_ptr));
+ //Check if the next node is in the group (not end node) and reverse linked to
+ //'to_erase_ptr'. Erase if that's the case.
+ if(nxt_ptr != end_ptr && to_erase_ptr == group_traits::get_next(nxt_ptr)){
group_algorithms::unlink_after(nxt_ptr);
}
}
@@ -317,81 +347,42 @@ struct group_functions
static node_ptr get_last_in_group(const node_ptr &first_in_group, detail::true_)
{ return group_traits::get_next(first_in_group); }
- static node_ptr get_last_in_group(const node_ptr &n, detail::false_)
+ static node_ptr get_last_in_group(node_ptr n, detail::false_)
{ return n; }
- static void init_group(const node_ptr &n, true_)
- { group_algorithms::init(n); }
-
- static void init_group(const node_ptr &, false_)
- {}
-
- static void insert_in_group(const node_ptr &first_in_group, const node_ptr &n, true_)
+ static node_ptr get_first_in_group(node_ptr n, detail::true_)
{
- if(first_in_group){
- if(group_algorithms::unique(first_in_group))
- group_algorithms::link_after(first_in_group, n);
- else{
- group_algorithms::link_after(node_traits::get_next(first_in_group), n);
- }
- }
- else{
- group_algorithms::init_header(n);
+ node_ptr ng;
+ while(n == node_traits::get_next((ng = group_traits::get_next(n)))){
+ n = ng;
}
+ return n;
}
- static slist_node_ptr get_previous_and_next_in_group
- ( const slist_node_ptr &i, node_ptr &nxt_in_group
- //If first_end_ptr == last_end_ptr, then first_end_ptr is the bucket of i
- //Otherwise first_end_ptr is the first bucket and last_end_ptr the last one.
- , const slist_node_ptr &first_end_ptr, const slist_node_ptr &last_end_ptr)
- {
- slist_node_ptr prev;
- node_ptr elem(detail::dcast_bucket_ptr<node>(i));
-
- //It's the last in group if the next_node is a bucket
- slist_node_ptr nxt(node_traits::get_next(elem));
- bool last_in_group = (first_end_ptr <= nxt && nxt <= last_end_ptr) ||
- (group_traits::get_next(detail::dcast_bucket_ptr<node>(nxt)) != elem);
- //It's the first in group if group_previous's next_node is not
- //itself, as group list does not link bucket
- node_ptr prev_in_group(group_traits::get_next(elem));
- bool first_in_group = node_traits::get_next(prev_in_group) != elem;
-
- if(first_in_group){
- node_ptr start_pos;
- if(last_in_group){
- start_pos = elem;
- nxt_in_group = node_ptr();
- }
- else{
- start_pos = prev_in_group;
- nxt_in_group = node_traits::get_next(elem);
- }
- slist_node_ptr bucket_node;
- if(first_end_ptr != last_end_ptr){
- bucket_node = group_functions::get_bucket_before_begin
- (first_end_ptr, last_end_ptr, start_pos);
- }
- else{
- bucket_node = first_end_ptr;
- }
- prev = group_functions::get_prev_to_first_in_group(bucket_node, elem);
- }
- else{
- if(last_in_group){
- nxt_in_group = group_functions::get_first_in_group_of_last_in_group(elem);
- }
- else{
- nxt_in_group = node_traits::get_next(elem);
- }
- prev = group_traits::get_next(elem);
- }
- return prev;
+ static node_ptr next_group_if_first_in_group(node_ptr ptr)
+ {
+ return node_traits::get_next(group_traits::get_next(ptr));
}
+ static node_ptr get_first_in_group(const node_ptr &n, detail::false_)
+ { return n; }
+
+ static void insert_in_group(const node_ptr &first_in_group, const node_ptr &n, true_)
+ { group_algorithms::link_after(first_in_group, n); }
+
static void insert_in_group(const node_ptr&, const node_ptr&, false_)
{}
+
+ static node_ptr split_group(node_ptr const new_first_in_group)
+ {
+ 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);
+ }
+ return first;
+ }
};
template<class BucketType, class SplitTraits>
@@ -453,8 +444,7 @@ inline std::size_t hash_to_bucket_split(std::size_t hash_value, std::size_t buck
{
std::size_t bucket_number = detail::hash_to_bucket(hash_value, bucket_cnt, detail::bool_<Power2Buckets>());
if(Incremental)
- if(bucket_number >= split)
- bucket_number -= bucket_cnt/2;
+ bucket_number -= static_cast<std::size_t>(bucket_number >= split)*(bucket_cnt/2);
return bucket_number;
}
@@ -513,6 +503,7 @@ struct hashtable_defaults
{
typedef default_hashtable_hook_applier proto_value_traits;
typedef std::size_t size_type;
+ typedef void key_of_value;
typedef void equal;
typedef void hash;
typedef default_bucket_traits bucket_traits;
@@ -576,17 +567,11 @@ struct node_cast_adaptor
}
};
-static const std::size_t hashtable_data_bool_flags_mask =
- ( hash_bool_flags::cache_begin_pos
- | hash_bool_flags::constant_time_size_pos
- | hash_bool_flags::incremental_pos
- );
-
//bucket_plus_vtraits stores ValueTraits + BucketTraits
//this data is needed by iterators to obtain the
//value from the iterator and detect the bucket
template<class ValueTraits, class BucketTraits>
-struct bucket_plus_vtraits : public ValueTraits
+struct bucket_plus_vtraits
{
typedef BucketTraits bucket_traits;
typedef ValueTraits value_traits;
@@ -607,6 +592,11 @@ struct bucket_plus_vtraits : public ValueTraits
typedef typename node_traits::node_ptr node_ptr;
typedef typename node_traits::node node;
typedef typename value_traits::value_type value_type;
+ typedef typename value_traits::pointer pointer;
+ typedef typename value_traits::const_pointer const_pointer;
+ typedef typename pointer_traits<pointer>::reference reference;
+ typedef typename pointer_traits
+ <const_pointer>::reference const_reference;
typedef circular_slist_algorithms<group_traits> group_algorithms;
typedef typename pointer_traits
<typename value_traits::pointer>::
@@ -618,16 +608,14 @@ struct bucket_plus_vtraits : public ValueTraits
<const bucket_plus_vtraits>::type const_bucket_value_traits_ptr;
typedef typename detail::unordered_bucket_ptr_impl
<value_traits>::type bucket_ptr;
- typedef detail::bool_<detail::optimize_multikey_is_true
- <node_traits>::value> optimize_multikey_t;
template<class BucketTraitsType>
bucket_plus_vtraits(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits)
- : ValueTraits(val_traits), bucket_traits_(::boost::forward<BucketTraitsType>(b_traits))
+ : data(val_traits, ::boost::forward<BucketTraitsType>(b_traits))
{}
bucket_plus_vtraits & operator =(const bucket_plus_vtraits &x)
- { bucket_traits_ = x.bucket_traits_; return *this; }
+ { data.bucket_traits_ = x.data.bucket_traits_; return *this; }
const_value_traits_ptr priv_value_traits_ptr() const
{ return pointer_traits<const_value_traits_ptr>::pointer_to(this->priv_value_traits()); }
@@ -646,18 +634,18 @@ struct bucket_plus_vtraits : public ValueTraits
//value traits
//
const value_traits &priv_value_traits() const
- { return *this; }
+ { return this->data; }
value_traits &priv_value_traits()
- { return *this; }
+ { return this->data; }
//bucket_traits
//
const bucket_traits &priv_bucket_traits() const
- { return this->bucket_traits_; }
+ { return this->data.bucket_traits_; }
bucket_traits &priv_bucket_traits()
- { return this->bucket_traits_; }
+ { return this->data.bucket_traits_; }
//bucket operations
bucket_ptr priv_bucket_pointer() const
@@ -674,6 +662,119 @@ struct bucket_plus_vtraits : public ValueTraits
siterator priv_invalid_local_it() const
{ return this->priv_bucket_traits().bucket_begin()->before_begin(); }
+ template<class NodeDisposer>
+ static size_type priv_erase_from_single_bucket(bucket_type &b, siterator sbefore_first, siterator slast, NodeDisposer node_disposer, detail::true_) //optimize multikey
+ {
+ size_type n = 0;
+ siterator const sfirst(++siterator(sbefore_first));
+ if(sfirst != slast){
+ node_ptr const nf = detail::dcast_bucket_ptr<node>(sfirst.pointed_node());
+ node_ptr const nl = detail::dcast_bucket_ptr<node>(slast.pointed_node());
+ node_ptr const ne = detail::dcast_bucket_ptr<node>(b.end().pointed_node());
+
+ if(group_functions_t::next_group_if_first_in_group(nf) != nf) {
+ // The node is at the beginning of a group.
+ if(nl != ne){
+ group_functions_t::split_group(nl);
+ }
+ }
+ else {
+ node_ptr const group1 = group_functions_t::split_group(nf);
+ if(nl != ne) {
+ node_ptr const group2 = group_functions_t::split_group(ne);
+ 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);
+ }
+ }
+ }
+
+ siterator it(++siterator(sbefore_first));
+ while(it != slast){
+ node_disposer((it++).pointed_node());
+ ++n;
+ }
+ b.erase_after(sbefore_first, slast);
+ }
+ return n;
+ }
+
+ template<class NodeDisposer>
+ static size_type priv_erase_from_single_bucket(bucket_type &b, siterator sbefore_first, siterator slast, NodeDisposer node_disposer, detail::false_) //optimize multikey
+ {
+ size_type n = 0;
+ siterator it(++siterator(sbefore_first));
+ while(it != slast){
+ node_disposer((it++).pointed_node());
+ ++n;
+ }
+ b.erase_after(sbefore_first, slast);
+ return n;
+ }
+
+ template<class NodeDisposer>
+ 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<node>(b.end().pointed_node()));
+ node_ptr n(detail::dcast_bucket_ptr<node>(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);
+
+ //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){
+ group_algorithms::unlink_after(nn);
+ }
+ }
+ else if(nn != ne && group_traits::get_next(nn) == n){
+ //Node is not the end of the group
+ bn = group_traits::get_next(n);
+ group_algorithms::unlink_after(nn);
+ }
+ else{
+ //Node is the end of the group
+ bn = group_traits::get_next(n);
+ 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);
+ }
+
+ template<class NodeDisposer>
+ 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); }
+
+ template<class NodeDisposer, bool OptimizeMultikey>
+ size_type priv_erase_node_range( siterator const &before_first_it, size_type const first_bucket
+ , siterator const &last_it, size_type const last_bucket
+ , NodeDisposer node_disposer, detail::bool_<OptimizeMultikey> optimize_multikey_tag)
+ {
+ size_type num_erased(0);
+ siterator last_step_before_it;
+ if(first_bucket != last_bucket){
+ bucket_type *b = (&this->priv_bucket_pointer()[0]);
+ num_erased += this->priv_erase_from_single_bucket
+ (b[first_bucket], before_first_it, b[first_bucket].end(), node_disposer, optimize_multikey_tag);
+ for(size_type 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();
+ }
+ else{
+ last_step_before_it = before_first_it;
+ }
+ num_erased += this->priv_erase_from_single_bucket
+ (this->priv_bucket_pointer()[last_bucket], last_step_before_it, last_it, node_disposer, optimize_multikey_tag);
+ return num_erased;
+ }
+
static siterator priv_get_last(bucket_type &b, detail::true_) //optimize multikey
{
//First find the last node of p's group.
@@ -690,14 +791,30 @@ struct bucket_plus_vtraits : public ValueTraits
return bucket_type::s_iterator_to(*last_node_group);
}
+ template<class NodeDisposer>
+ size_type priv_erase_whole_bucket(bucket_type &b, NodeDisposer node_disposer)
+ {
+ size_type 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;
+ }
+
static siterator priv_get_last(bucket_type &b, detail::false_) //NOT optimize multikey
{ return b.previous(b.end()); }
static siterator priv_get_previous(bucket_type &b, siterator i, detail::true_) //optimize multikey
{
- node_ptr elem(detail::dcast_bucket_ptr<node>(i.pointed_node()));
- node_ptr prev_in_group(group_traits::get_next(elem));
- bool first_in_group = node_traits::get_next(prev_in_group) != elem;
+ node_ptr const elem(detail::dcast_bucket_ptr<node>(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)
@@ -708,19 +825,6 @@ struct bucket_plus_vtraits : public ValueTraits
static siterator priv_get_previous(bucket_type &b, siterator i, detail::false_) //NOT optimize multikey
{ return b.previous(i); }
- static void priv_clear_group_nodes(bucket_type &b, detail::true_) //optimize multikey
- {
- siterator it(b.begin()), itend(b.end());
- while(it != itend){
- node_ptr to_erase(detail::dcast_bucket_ptr<node>(it.pointed_node()));
- ++it;
- group_algorithms::init(to_erase);
- }
- }
-
- static void priv_clear_group_nodes(bucket_type &, detail::false_) //NOT optimize multikey
- {}
-
std::size_t priv_get_bucket_num_no_hash_store(siterator it, detail::true_) //optimize multikey
{
const bucket_ptr f(this->priv_bucket_pointer()), l(f + this->priv_bucket_count() - 1);
@@ -757,19 +861,19 @@ struct bucket_plus_vtraits : public ValueTraits
static std::size_t priv_stored_hash(slist_node_ptr n, detail::true_) //store_hash
{ return node_traits::get_hash(detail::dcast_bucket_ptr<node>(n)); }
- static std::size_t priv_stored_hash(slist_node_ptr, detail::false_) //NO store_hash (This should never be called)
- { BOOST_INTRUSIVE_INVARIANT_ASSERT(0); return 0; }
+ static std::size_t priv_stored_hash(slist_node_ptr, detail::false_) //NO store_hash
+ { return std::size_t(-1); }
- node &priv_value_to_node(value_type &v)
+ node &priv_value_to_node(reference v)
{ return *this->priv_value_traits().to_node_ptr(v); }
- const node &priv_value_to_node(const value_type &v) const
+ const node &priv_value_to_node(const_reference v) const
{ return *this->priv_value_traits().to_node_ptr(v); }
- value_type &priv_value_from_slist_node(slist_node_ptr n)
+ reference priv_value_from_slist_node(slist_node_ptr n)
{ return *this->priv_value_traits().to_value_ptr(detail::dcast_bucket_ptr<node>(n)); }
- const value_type &priv_value_from_slist_node(slist_node_ptr n) const
+ const_reference priv_value_from_slist_node(slist_node_ptr n) const
{ return *this->priv_value_traits().to_value_ptr(detail::dcast_bucket_ptr<node>(n)); }
void priv_clear_buckets(const bucket_ptr buckets_ptr, const size_type bucket_cnt)
@@ -777,7 +881,6 @@ struct bucket_plus_vtraits : public ValueTraits
bucket_ptr buckets_it = buckets_ptr;
for(size_type bucket_i = 0; bucket_i != bucket_cnt; ++buckets_it, ++bucket_i){
if(safemode_or_autounlink){
- bucket_plus_vtraits::priv_clear_group_nodes(*buckets_it, optimize_multikey_t());
buckets_it->clear_and_dispose(detail::init_disposer<node_algorithms>());
}
else{
@@ -786,10 +889,51 @@ struct bucket_plus_vtraits : public ValueTraits
}
}
- bucket_traits bucket_traits_;
+ 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<bucket_plus_vtraits, false> iterator;
+ typedef hashtable_iterator<bucket_plus_vtraits, true> const_iterator;
+
+ iterator end()
+ { return iterator(this->priv_invalid_local_it(), 0); }
+
+ const_iterator end() const
+ { return this->cend(); }
+
+ const_iterator cend() const
+ { return const_iterator(this->priv_invalid_local_it(), 0); }
+
+ static size_type suggested_upper_bucket_count(size_type n)
+ {
+ 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);
+ bound -= (bound == primes_end);
+ return size_type(*bound);
+ }
+
+ static size_type suggested_lower_bucket_count(size_type n)
+ {
+ 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;
+ size_type const* bound = std::upper_bound(primes, primes_end, n);
+ bound -= (bound != primes);
+ return size_type(*bound);
+ }
+
+ //Public functions:
+ struct data_type : public ValueTraits
+ {
+ template<class BucketTraitsType>
+ data_type(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits)
+ : ValueTraits(val_traits), bucket_traits_(::boost::forward<BucketTraitsType>(b_traits))
+ {}
+ bucket_traits bucket_traits_;
+ } data;
};
-template<class Hash, class T>
+template<class Hash, class>
struct get_hash
{
typedef Hash type;
@@ -801,76 +945,124 @@ struct get_hash<void, T>
typedef ::boost::hash<T> type;
};
+template<class EqualTo, class>
+struct get_equal_to
+{
+ typedef EqualTo type;
+};
+
+template<class T>
+struct get_equal_to<void, T>
+{
+ typedef std::equal_to<T> type;
+};
+
+template<class KeyOfValue, class T>
+struct get_hash_key_of_value
+{
+ typedef KeyOfValue type;
+};
+
+template<class T>
+struct get_hash_key_of_value<void, T>
+{
+ typedef ::boost::intrusive::detail::identity<T> type;
+};
+
+template<class T, class VoidOrKeyOfValue>
+struct hash_key_types_base
+{
+ typedef typename get_hash_key_of_value
+ < VoidOrKeyOfValue, T>::type key_of_value;
+ typedef typename key_of_value::type key_type;
+};
+
+template<class T, class VoidOrKeyOfValue, class VoidOrKeyHash>
+struct hash_key_hash
+ : get_hash
+ < VoidOrKeyHash
+ , typename hash_key_types_base<T, VoidOrKeyOfValue>::key_type
+ >
+{};
+
+template<class T, class VoidOrKeyOfValue, class VoidOrKeyEqual>
+struct hash_key_equal
+ : get_equal_to
+ < VoidOrKeyEqual
+ , typename hash_key_types_base<T, VoidOrKeyOfValue>::key_type
+ >
+
+{};
+
//bucket_hash_t
//Stores bucket_plus_vtraits plust the hash function
-template<class VoidOrKeyHash, class ValueTraits, class BucketTraits>
+template<class ValueTraits, class VoidOrKeyOfValue, class VoidOrKeyHash, class BucketTraits>
struct bucket_hash_t
//Use public inheritance to avoid MSVC bugs with closures
: public detail::ebo_functor_holder
- <typename get_hash< VoidOrKeyHash
- , typename bucket_plus_vtraits<ValueTraits,BucketTraits>::value_traits::value_type
- >::type
- >
+ <typename hash_key_hash < typename bucket_plus_vtraits<ValueTraits,BucketTraits>::value_traits::value_type
+ , VoidOrKeyOfValue
+ , VoidOrKeyHash
+ >::type
+ >
+ , bucket_plus_vtraits<ValueTraits, BucketTraits> //4
{
typedef typename bucket_plus_vtraits<ValueTraits,BucketTraits>::value_traits value_traits;
typedef typename value_traits::value_type value_type;
typedef typename value_traits::node_traits node_traits;
- typedef typename get_hash< VoidOrKeyHash, value_type>::type hasher;
+ typedef hash_key_hash
+ < value_type, VoidOrKeyOfValue, VoidOrKeyHash> hash_key_hash_t;
+ typedef typename hash_key_hash_t::type hasher;
+ typedef typename hash_key_types_base<value_type, VoidOrKeyOfValue>::key_of_value key_of_value;
+
typedef BucketTraits bucket_traits;
typedef bucket_plus_vtraits<ValueTraits, BucketTraits> bucket_plus_vtraits_t;
+ typedef detail::ebo_functor_holder<hasher> base_t;
template<class BucketTraitsType>
bucket_hash_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h)
- : detail::ebo_functor_holder<hasher>(h), internal(val_traits, ::boost::forward<BucketTraitsType>(b_traits))
+ : detail::ebo_functor_holder<hasher>(h), bucket_plus_vtraits_t(val_traits, ::boost::forward<BucketTraitsType>(b_traits))
{}
const hasher &priv_hasher() const
- { return this->detail::ebo_functor_holder<hasher>::get(); }
+ { return this->base_t::get(); }
hasher &priv_hasher()
- { return this->detail::ebo_functor_holder<hasher>::get(); }
+ { return this->base_t::get(); }
- 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->internal.priv_value_traits().to_node_ptr(v)); }
+ using bucket_plus_vtraits_t::priv_stored_or_compute_hash; //For store_hash == true
std::size_t priv_stored_or_compute_hash(const value_type &v, detail::false_) const //For store_hash == false
- { return this->priv_hasher()(v); }
-
- bucket_plus_vtraits_t internal; //4
+ { return this->priv_hasher()(key_of_value()(v)); }
};
-
-template<class EqualTo, class T>
-struct get_equal_to
+template<class ValueTraits, class BucketTraits, class VoidOrKeyOfValue, class VoidOrKeyEqual>
+struct hashtable_equal_holder
{
- typedef EqualTo type;
-};
-
-template<class T>
-struct get_equal_to<void, T>
-{
- typedef ::std::equal_to<T> type;
+ typedef detail::ebo_functor_holder
+ < typename hash_key_equal < typename bucket_plus_vtraits<ValueTraits, BucketTraits>::value_traits::value_type
+ , VoidOrKeyOfValue
+ , VoidOrKeyEqual
+ >::type
+ > type;
};
//bucket_hash_equal_t
//Stores bucket_hash_t and the equality function when the first
//non-empty bucket shall not be cached.
-template<class VoidOrKeyHash, class VoidOrKeyEqual, class ValueTraits, class BucketTraits, bool>
+template<class ValueTraits, class VoidOrKeyOfValue, class VoidOrKeyHash, class VoidOrKeyEqual, class BucketTraits, bool>
struct bucket_hash_equal_t
//Use public inheritance to avoid MSVC bugs with closures
- : public detail::ebo_functor_holder //equal
- <typename get_equal_to< VoidOrKeyEqual
- , typename bucket_plus_vtraits<ValueTraits,BucketTraits>::value_traits::value_type
- >::type
- >
+ : public bucket_hash_t<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, BucketTraits> //3
+ , public hashtable_equal_holder<ValueTraits, BucketTraits, VoidOrKeyOfValue, VoidOrKeyEqual>::type //equal
{
- typedef bucket_hash_t<VoidOrKeyHash, ValueTraits, BucketTraits> bucket_hash_type;
+ typedef typename hashtable_equal_holder
+ <ValueTraits, BucketTraits, VoidOrKeyOfValue, VoidOrKeyEqual>::type equal_holder_t;
+ typedef bucket_hash_t<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, BucketTraits> bucket_hash_type;
typedef bucket_plus_vtraits<ValueTraits,BucketTraits> bucket_plus_vtraits_t;
- typedef typename bucket_plus_vtraits_t::value_traits value_traits;
- typedef typename get_equal_to< VoidOrKeyEqual
- , typename value_traits::value_type
- >::type value_equal;
+ 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;
@@ -880,13 +1072,13 @@ struct bucket_hash_equal_t
typedef typename detail::unordered_bucket_ptr_impl<value_traits>::type bucket_ptr;
template<class BucketTraitsType>
- bucket_hash_equal_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h, const value_equal &e)
- : detail::ebo_functor_holder<value_equal>(e)
- , internal(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h)
+ bucket_hash_equal_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h, const key_equal &e)
+ : bucket_hash_type(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h)
+ , equal_holder_t(e)
{}
bucket_ptr priv_get_cache()
- { return this->internal.internal.priv_bucket_pointer(); }
+ { return this->bucket_hash_type::priv_bucket_pointer(); }
void priv_set_cache(const bucket_ptr &)
{}
@@ -903,14 +1095,14 @@ struct bucket_hash_equal_t
siterator priv_begin() const
{
size_type n = 0;
- size_type bucket_cnt = this->internal.internal.priv_bucket_count();
+ size_type bucket_cnt = this->bucket_hash_type::priv_bucket_count();
for (n = 0; n < bucket_cnt; ++n){
- bucket_type &b = this->internal.internal.priv_bucket_pointer()[n];
+ bucket_type &b = this->bucket_hash_type::priv_bucket_pointer()[n];
if(!b.empty()){
return b.begin();
}
}
- return this->internal.internal.priv_invalid_local_it();
+ return this->bucket_hash_type::priv_invalid_local_it();
}
void priv_insertion_update_cache(size_type)
@@ -922,43 +1114,38 @@ struct bucket_hash_equal_t
void priv_erasure_update_cache()
{}
- const value_equal &priv_equal() const
- { return this->detail::ebo_functor_holder<value_equal>::get(); }
+ const key_equal &priv_equal() const
+ { return this->equal_holder_t::get(); }
- value_equal &priv_equal()
- { return this->detail::ebo_functor_holder<value_equal>::get(); }
-
- bucket_hash_t<VoidOrKeyHash, ValueTraits, BucketTraits> internal; //3
+ key_equal &priv_equal()
+ { return this->equal_holder_t::get(); }
};
//bucket_hash_equal_t
//Stores bucket_hash_t and the equality function when the first
//non-empty bucket shall be cached.
-template<class VoidOrKeyHash, class VoidOrKeyEqual, class ValueTraits, class BucketTraits> //cache_begin == true version
-struct bucket_hash_equal_t<VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTraits, true>
+template<class ValueTraits, class VoidOrKeyOfValue, class VoidOrKeyHash, class VoidOrKeyEqual, class BucketTraits> //cache_begin == true version
+struct bucket_hash_equal_t<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual, BucketTraits, true>
//Use public inheritance to avoid MSVC bugs with closures
- : public detail::ebo_functor_holder //equal
- <typename get_equal_to< VoidOrKeyEqual
- , typename bucket_plus_vtraits<ValueTraits,BucketTraits>::value_traits::value_type
- >::type
- >
+ : bucket_hash_t<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, BucketTraits> //2
+ , hashtable_equal_holder<ValueTraits, BucketTraits, VoidOrKeyOfValue, VoidOrKeyEqual>::type
{
- typedef bucket_plus_vtraits<ValueTraits, BucketTraits> bucket_plus_vtraits_t;
- typedef bucket_hash_t<VoidOrKeyHash, ValueTraits, BucketTraits> bucket_hash_type;
- typedef typename bucket_plus_vtraits
- <ValueTraits,BucketTraits>::value_traits value_traits;
- typedef typename get_equal_to
- < VoidOrKeyEqual
- , typename value_traits::value_type>::type value_equal;
+ typedef typename hashtable_equal_holder
+ <ValueTraits, BucketTraits, VoidOrKeyOfValue, VoidOrKeyEqual>::type equal_holder_t;
+
+ typedef bucket_plus_vtraits<ValueTraits,BucketTraits> bucket_plus_vtraits_t;
+ typedef ValueTraits value_traits;
+ typedef typename equal_holder_t::functor_type key_equal;
+ typedef bucket_hash_t<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, BucketTraits> bucket_hash_type;
typedef typename bucket_hash_type::hasher hasher;
typedef BucketTraits bucket_traits;
typedef typename bucket_plus_vtraits_t::slist_impl::size_type size_type;
typedef typename bucket_plus_vtraits_t::slist_impl::iterator siterator;
template<class BucketTraitsType>
- bucket_hash_equal_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h, const value_equal &e)
- : detail::ebo_functor_holder<value_equal>(e)
- , internal(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h)
+ bucket_hash_equal_t(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h, const key_equal &e)
+ : bucket_hash_type(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h)
+ , equal_holder_t(e)
{}
typedef typename detail::unordered_bucket_ptr_impl
@@ -974,10 +1161,10 @@ struct bucket_hash_equal_t<VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTra
{ cached_begin_ = p; }
std::size_t priv_get_cache_bucket_num()
- { return this->cached_begin_ - this->internal.internal.priv_bucket_pointer(); }
+ { return this->cached_begin_ - this->bucket_hash_type::priv_bucket_pointer(); }
void priv_initialize_cache()
- { this->cached_begin_ = this->internal.internal.priv_invalid_bucket(); }
+ { this->cached_begin_ = this->bucket_hash_type::priv_invalid_bucket(); }
void priv_swap_cache(bucket_hash_equal_t &other)
{
@@ -986,8 +1173,8 @@ struct bucket_hash_equal_t<VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTra
siterator priv_begin() const
{
- if(this->cached_begin_ == this->internal.internal.priv_invalid_bucket()){
- return this->internal.internal.priv_invalid_local_it();
+ if(this->cached_begin_ == this->bucket_hash_type::priv_invalid_bucket()){
+ return this->bucket_hash_type::priv_invalid_local_it();
}
else{
return this->cached_begin_->begin();
@@ -996,34 +1183,34 @@ struct bucket_hash_equal_t<VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTra
void priv_insertion_update_cache(size_type insertion_bucket)
{
- bucket_ptr p = this->internal.internal.priv_bucket_pointer() + insertion_bucket;
+ bucket_ptr p = this->bucket_hash_type::priv_bucket_pointer() + insertion_bucket;
if(p < this->cached_begin_){
this->cached_begin_ = p;
}
}
- const value_equal &priv_equal() const
- { return this->detail::ebo_functor_holder<value_equal>::get(); }
+ const key_equal &priv_equal() const
+ { return this->equal_holder_t::get(); }
- value_equal &priv_equal()
- { return this->detail::ebo_functor_holder<value_equal>::get(); }
+ key_equal &priv_equal()
+ { return this->equal_holder_t::get(); }
void priv_erasure_update_cache_range(size_type first_bucket_num, size_type last_bucket_num)
{
//If the last bucket is the end, the cache must be updated
//to the last position if all
if(this->priv_get_cache_bucket_num() == first_bucket_num &&
- this->internal.internal.priv_bucket_pointer()[first_bucket_num].empty() ){
- this->priv_set_cache(this->internal.internal.priv_bucket_pointer() + last_bucket_num);
+ this->bucket_hash_type::priv_bucket_pointer()[first_bucket_num].empty() ){
+ this->priv_set_cache(this->bucket_hash_type::priv_bucket_pointer() + last_bucket_num);
this->priv_erasure_update_cache();
}
}
void priv_erasure_update_cache()
{
- if(this->cached_begin_ != this->internal.internal.priv_invalid_bucket()){
- size_type current_n = this->priv_get_cache() - this->internal.internal.priv_bucket_pointer();
- for( const size_type num_buckets = this->internal.internal.priv_bucket_count()
+ if(this->cached_begin_ != this->bucket_hash_type::priv_invalid_bucket()){
+ size_type current_n = this->priv_get_cache() - this->bucket_hash_type::priv_bucket_pointer();
+ for( const size_type num_buckets = this->bucket_hash_type::priv_bucket_count()
; current_n < num_buckets
; ++current_n, ++this->priv_get_cache()){
if(!this->priv_get_cache()->empty()){
@@ -1035,103 +1222,291 @@ struct bucket_hash_equal_t<VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTra
}
bucket_ptr cached_begin_;
- bucket_hash_t<VoidOrKeyHash, ValueTraits, BucketTraits> internal; //2
};
+//This wrapper around size_traits is used
+//to maintain minimal container size with compilers like MSVC
+//that have problems with EBO and multiple empty base classes
+template<class DeriveFrom, class SizeType, bool>
+struct hashtable_size_traits_wrapper
+ : public DeriveFrom
+{
+ template<class Base, class Arg0, class Arg1, class Arg2>
+ hashtable_size_traits_wrapper( BOOST_FWD_REF(Base) base, BOOST_FWD_REF(Arg0) arg0
+ , BOOST_FWD_REF(Arg1) arg1, BOOST_FWD_REF(Arg2) arg2)
+ : DeriveFrom(::boost::forward<Base>(base)
+ , ::boost::forward<Arg0>(arg0)
+ , ::boost::forward<Arg1>(arg1)
+ , ::boost::forward<Arg2>(arg2))
+ {}
+ typedef detail::size_holder < true, SizeType> size_traits;//size_traits
+
+ size_traits size_traits_;
+
+ const size_traits &priv_size_traits() const
+ { return size_traits_; }
+
+ size_traits &priv_size_traits()
+ { return size_traits_; }
+};
+
+template<class DeriveFrom, class SizeType>
+struct hashtable_size_traits_wrapper<DeriveFrom, SizeType, false>
+ : public DeriveFrom
+{
+ template<class Base, class Arg0, class Arg1, class Arg2>
+ hashtable_size_traits_wrapper( BOOST_FWD_REF(Base) base, BOOST_FWD_REF(Arg0) arg0
+ , BOOST_FWD_REF(Arg1) arg1, BOOST_FWD_REF(Arg2) arg2)
+ : DeriveFrom(::boost::forward<Base>(base)
+ , ::boost::forward<Arg0>(arg0)
+ , ::boost::forward<Arg1>(arg1)
+ , ::boost::forward<Arg2>(arg2))
+ {}
+
+ typedef detail::size_holder< false, SizeType> size_traits;
+
+ const size_traits &priv_size_traits() const
+ { return size_traits_; }
+
+ size_traits &priv_size_traits()
+ { return size_traits_; }
+
+ static size_traits size_traits_;
+};
+
+template<class DeriveFrom, class SizeType>
+detail::size_holder< false, SizeType > hashtable_size_traits_wrapper<DeriveFrom, SizeType, false>::size_traits_;
+
//hashdata_internal
//Stores bucket_hash_equal_t and split_traits
-template<class SizeType, std::size_t BoolFlags, class VoidOrKeyHash, class VoidOrKeyEqual, class ValueTraits, class BucketTraits>
+template<class ValueTraits, class VoidOrKeyOfValue, class VoidOrKeyHash, class VoidOrKeyEqual, class BucketTraits, class SizeType, std::size_t BoolFlags>
struct hashdata_internal
- : public detail::size_holder< 0 != (BoolFlags & hash_bool_flags::incremental_pos), SizeType, int> //split_traits
+ : public hashtable_size_traits_wrapper
+ < bucket_hash_equal_t
+ < ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual
+ , BucketTraits
+ , 0 != (BoolFlags & hash_bool_flags::cache_begin_pos)
+ > //2
+ , SizeType
+ , (BoolFlags & hash_bool_flags::incremental_pos) != 0
+ >
{
- typedef bucket_hash_equal_t
- < VoidOrKeyHash, VoidOrKeyEqual
- , ValueTraits, BucketTraits
- , 0 != (BoolFlags & hash_bool_flags::cache_begin_pos)
+ typedef hashtable_size_traits_wrapper
+ < bucket_hash_equal_t
+ < ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual
+ , BucketTraits
+ , 0 != (BoolFlags & hash_bool_flags::cache_begin_pos)
+ > //2
+ , SizeType
+ , (BoolFlags & hash_bool_flags::incremental_pos) != 0
> internal_type;
- typedef typename internal_type::value_equal value_equal;
+ typedef typename internal_type::key_equal key_equal;
typedef typename internal_type::hasher hasher;
typedef bucket_plus_vtraits<ValueTraits,BucketTraits> bucket_plus_vtraits_t;
typedef typename bucket_plus_vtraits_t::size_type size_type;
+ typedef typename internal_type::size_traits split_traits;
typedef typename bucket_plus_vtraits_t::bucket_ptr bucket_ptr;
- typedef detail::size_holder
- <0 != (BoolFlags & hash_bool_flags::incremental_pos)
- , SizeType, int> split_traits;
- typedef typename bucket_plus_vtraits_t::
- value_traits::node_traits node_traits;
- typedef detail::bool_<detail::optimize_multikey_is_true
- <node_traits>::value> optimize_multikey_t;
+ typedef typename bucket_plus_vtraits_t::const_value_traits_ptr const_value_traits_ptr;
+ typedef typename bucket_plus_vtraits_t::siterator siterator;
+ typedef typename bucket_plus_vtraits_t::bucket_traits bucket_traits;
+ typedef typename bucket_plus_vtraits_t::value_traits value_traits;
+ typedef typename bucket_plus_vtraits_t::bucket_type bucket_type;
+ typedef typename value_traits::value_type value_type;
+ typedef typename value_traits::pointer pointer;
+ typedef typename value_traits::const_pointer const_pointer;
+ typedef typename pointer_traits<pointer>::reference reference;
+ typedef typename pointer_traits
+ <const_pointer>::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_traits> node_functions_t;
+ typedef typename detail::get_slist_impl
+ <typename detail::reduced_slist_node_traits
+ <typename value_traits::node_traits>::type
+ >::type slist_impl;
+ typedef typename slist_impl::node_algorithms node_algorithms;
+ typedef typename slist_impl::node_ptr slist_node_ptr;
+
+ typedef hash_key_types_base
+ < typename ValueTraits::value_type
+ , VoidOrKeyOfValue
+ > hash_types_base;
+ typedef typename hash_types_base::key_of_value key_of_value;
+
+ static const bool store_hash = detail::store_hash_is_true<node_traits>::value;
+ static const bool safemode_or_autounlink = is_safe_autounlink<value_traits::link_mode>::value;
+ static const bool stateful_value_traits = detail::is_stateful_value_traits<value_traits>::value;
+
+ typedef detail::bool_<store_hash> store_hash_t;
+
+ typedef detail::transform_iterator
+ < typename slist_impl::iterator
+ , downcast_node_to_value_t
+ < value_traits
+ , false> > local_iterator;
+
+ typedef detail::transform_iterator
+ < typename slist_impl::iterator
+ , downcast_node_to_value_t
+ < value_traits
+ , true> > const_local_iterator;
+ //
template<class BucketTraitsType>
hashdata_internal( const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits
- , const hasher & h, const value_equal &e)
- : internal(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h, e)
+ , const hasher & h, const key_equal &e)
+ : internal_type(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h, e)
{}
split_traits &priv_split_traits()
- { return *this; }
+ { return this->priv_size_traits(); }
const split_traits &priv_split_traits() const
- { return *this; }
+ { return this->priv_size_traits(); }
~hashdata_internal()
{ this->priv_clear_buckets(); }
void priv_clear_buckets()
{
- this->internal.internal.internal.priv_clear_buckets
- ( this->internal.priv_get_cache()
- , this->internal.internal.internal.priv_bucket_count()
- - (this->internal.priv_get_cache()
- - this->internal.internal.internal.priv_bucket_pointer()));
+ this->internal_type::priv_clear_buckets
+ ( this->priv_get_cache()
+ , this->internal_type::priv_bucket_count()
+ - (this->priv_get_cache()
+ - this->internal_type::priv_bucket_pointer()));
}
void priv_clear_buckets_and_cache()
{
this->priv_clear_buckets();
- this->internal.priv_initialize_cache();
+ this->priv_initialize_cache();
}
void priv_initialize_buckets_and_cache()
{
- this->internal.internal.internal.priv_clear_buckets
- ( this->internal.internal.internal.priv_bucket_pointer()
- , this->internal.internal.internal.priv_bucket_count());
- this->internal.priv_initialize_cache();
+ this->internal_type::priv_clear_buckets
+ ( this->internal_type::priv_bucket_pointer()
+ , this->internal_type::priv_bucket_count());
+ this->priv_initialize_cache();
}
- internal_type internal; //2
-};
+ typedef hashtable_iterator<bucket_plus_vtraits_t, false> iterator;
+ typedef hashtable_iterator<bucket_plus_vtraits_t, true> const_iterator;
-//hashtable_data_t
-//Stores hashdata_internal and size_traits
-template<class SizeType, std::size_t BoolFlags, class VoidOrKeyHash, class VoidOrKeyEqual, class ValueTraits, class BucketTraits>
-struct hashtable_data_t
- : public detail::size_holder
- < 0 != (BoolFlags & hash_bool_flags::constant_time_size_pos), SizeType> //size_traits
-{
- typedef detail::size_holder
- < 0 != (BoolFlags & hash_bool_flags::constant_time_size_pos)
- , SizeType> size_traits;
- typedef hashdata_internal
- < SizeType
- , BoolFlags & (hash_bool_flags::incremental_pos | hash_bool_flags::cache_begin_pos)
- , VoidOrKeyHash, VoidOrKeyEqual
- , ValueTraits, BucketTraits> internal_type;
- typedef ValueTraits value_traits;
- typedef typename internal_type::value_equal value_equal;
- typedef typename internal_type::hasher hasher;
- typedef BucketTraits bucket_traits;
- typedef bucket_plus_vtraits
- <ValueTraits,BucketTraits> bucket_plus_vtraits_t;
+ static std::size_t priv_stored_hash(slist_node_ptr n, detail::true_ true_value)
+ { return bucket_plus_vtraits<ValueTraits, BucketTraits>::priv_stored_hash(n, true_value); }
- template<class BucketTraitsType>
- hashtable_data_t( BOOST_FWD_REF(BucketTraitsType) b_traits, const hasher & h
- , const value_equal &e, const value_traits &val_traits)
- : internal(val_traits, ::boost::forward<BucketTraitsType>(b_traits), h, e)
- {}
+ static std::size_t priv_stored_hash(slist_node_ptr n, detail::false_ false_value)
+ { return bucket_plus_vtraits<ValueTraits, BucketTraits>::priv_stored_hash(n, false_value); }
+
+ //public functions
+ SizeType split_count() const
+ {
+ return this->priv_split_traits().get_size();
+ }
+
+ iterator iterator_to(reference value)
+ {
+ return iterator(bucket_type::s_iterator_to
+ (this->priv_value_to_node(value)), &this->get_bucket_value_traits());
+ }
+
+ const_iterator iterator_to(const_reference value) const
+ {
+ siterator const sit = bucket_type::s_iterator_to
+ ( *pointer_traits<node_ptr>::const_cast_from
+ (pointer_traits<const_node_ptr>::pointer_to(this->priv_value_to_node(value)))
+ );
+ return const_iterator(sit, &this->get_bucket_value_traits());
+ }
+
+ static local_iterator s_local_iterator_to(reference value)
+ {
+ BOOST_STATIC_ASSERT((!stateful_value_traits));
+ siterator sit = bucket_type::s_iterator_to(*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_STATIC_ASSERT((!stateful_value_traits));
+ siterator const sit = bucket_type::s_iterator_to
+ ( *pointer_traits<node_ptr>::const_cast_from
+ (value_traits::to_node_ptr(value))
+ );
+ return const_local_iterator(sit, const_value_traits_ptr());
+ }
+
+ local_iterator local_iterator_to(reference value)
+ {
+ siterator sit = bucket_type::s_iterator_to(this->priv_value_to_node(value));
+ return local_iterator(sit, this->priv_value_traits_ptr());
+ }
+
+ const_local_iterator local_iterator_to(const_reference value) const
+ {
+ siterator sit = bucket_type::s_iterator_to
+ ( *pointer_traits<node_ptr>::const_cast_from
+ (pointer_traits<const_node_ptr>::pointer_to(this->priv_value_to_node(value)))
+ );
+ return const_local_iterator(sit, this->priv_value_traits_ptr());
+ }
+
+ size_type bucket_count() const
+ { return this->priv_bucket_count(); }
- internal_type internal; //1
+ size_type bucket_size(size_type n) const
+ { return this->priv_bucket_pointer()[n].size(); }
+
+ bucket_ptr bucket_pointer() const
+ { return this->priv_bucket_pointer(); }
+
+ local_iterator begin(size_type n)
+ { return local_iterator(this->priv_bucket_pointer()[n].begin(), this->priv_value_traits_ptr()); }
+
+ const_local_iterator begin(size_type n) const
+ { return this->cbegin(n); }
+
+ const_local_iterator cbegin(size_type n) const
+ {
+ return const_local_iterator
+ ( pointer_traits<bucket_ptr>::const_cast_from(this->priv_bucket_pointer())[n].begin()
+ , this->priv_value_traits_ptr());
+ }
+
+ using internal_type::end;
+ using internal_type::cend;
+ local_iterator end(size_type n)
+ { return local_iterator(this->priv_bucket_pointer()[n].end(), this->priv_value_traits_ptr()); }
+
+ const_local_iterator end(size_type n) const
+ { return this->cend(n); }
+
+ const_local_iterator cend(size_type n) const
+ {
+ return const_local_iterator
+ ( pointer_traits<bucket_ptr>::const_cast_from(this->priv_bucket_pointer())[n].end()
+ , this->priv_value_traits_ptr());
+ }
+
+ //Public functions for hashtable_impl
+
+ iterator begin()
+ { return iterator(this->priv_begin(), &this->get_bucket_value_traits()); }
+
+ const_iterator begin() const
+ { return this->cbegin(); }
+
+ const_iterator cbegin() const
+ { return const_iterator(this->priv_begin(), &this->get_bucket_value_traits()); }
+
+ hasher hash_function() const
+ { return this->priv_hasher(); }
+
+ key_equal key_eq() const
+ { return this->priv_equal(); }
};
/// @endcond
@@ -1175,61 +1550,82 @@ struct hashtable_data_t
#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
template<class T, class ...Options>
#else
-template<class ValueTraits, class VoidOrKeyHash, class VoidOrKeyEqual, class SizeType, class BucketTraits, std::size_t BoolFlags>
+template<class ValueTraits, class VoidOrKeyOfValue, class VoidOrKeyHash, class VoidOrKeyEqual, class BucketTraits, class SizeType, std::size_t BoolFlags>
#endif
class hashtable_impl
- : private hashtable_data_t
- < SizeType
- , BoolFlags & hashtable_data_bool_flags_mask
- , VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTraits>
+ : private 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
+ >
+ , SizeType
+ , (BoolFlags & hash_bool_flags::constant_time_size_pos) != 0
+ >
{
- typedef hashtable_data_t
- < SizeType
- , BoolFlags & hashtable_data_bool_flags_mask
- , VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTraits> data_type;
-
+ 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
+ >
+ , SizeType
+ , (BoolFlags & hash_bool_flags::constant_time_size_pos) != 0
+ > internal_type;
+ typedef typename internal_type::size_traits size_traits;
+ typedef hash_key_types_base
+ < typename ValueTraits::value_type
+ , VoidOrKeyOfValue
+ > hash_types_base;
public:
typedef ValueTraits value_traits;
/// @cond
typedef BucketTraits bucket_traits;
- typedef typename detail::get_slist_impl
- <typename detail::reduced_slist_node_traits
- <typename value_traits::node_traits>::type
- >::type slist_impl;
+ typedef typename internal_type::slist_impl slist_impl;
typedef bucket_plus_vtraits<ValueTraits, BucketTraits> bucket_plus_vtraits_t;
typedef typename bucket_plus_vtraits_t::const_value_traits_ptr const_value_traits_ptr;
+ using internal_type::begin;
+ using internal_type::cbegin;
+ using internal_type::end;
+ using internal_type::cend;
+ using internal_type::hash_function;
+ using internal_type::key_eq;
+ using internal_type::bucket_size;
+ using internal_type::bucket_count;
+ using internal_type::local_iterator_to;
+ using internal_type::s_local_iterator_to;
+ using internal_type::iterator_to;
+ using internal_type::bucket_pointer;
+ using internal_type::suggested_upper_bucket_count;
+ using internal_type::suggested_lower_bucket_count;
+ using internal_type::split_count;
/// @endcond
typedef typename value_traits::pointer pointer;
typedef typename value_traits::const_pointer const_pointer;
typedef typename value_traits::value_type value_type;
+ typedef typename hash_types_base::key_type key_type;
+ typedef typename hash_types_base::key_of_value key_of_value;
typedef typename pointer_traits<pointer>::reference reference;
typedef typename pointer_traits<const_pointer>::reference const_reference;
typedef typename pointer_traits<pointer>::difference_type difference_type;
typedef SizeType size_type;
- typedef value_type key_type;
- typedef typename data_type::value_equal key_equal;
- typedef typename data_type::value_equal value_equal;
- typedef typename data_type::hasher hasher;
+ typedef typename internal_type::key_equal key_equal;
+ typedef typename internal_type::hasher hasher;
typedef detail::bucket_impl<slist_impl> bucket_type;
- typedef typename pointer_traits
- <pointer>::template rebind_pointer
- < bucket_type >::type bucket_ptr;
- typedef typename pointer_traits
- <pointer>::template rebind_pointer
- < const bucket_type >::type const_bucket_ptr;
- typedef typename pointer_traits
- <bucket_ptr>::reference bucket_reference;
- typedef typename pointer_traits
- <bucket_ptr>::reference const_bucket_reference;
+ typedef typename internal_type::bucket_ptr bucket_ptr;
typedef typename slist_impl::iterator siterator;
typedef typename slist_impl::const_iterator const_siterator;
- typedef hashtable_iterator<bucket_plus_vtraits_t, false> iterator;
- typedef hashtable_iterator<bucket_plus_vtraits_t, true> const_iterator;
+ typedef typename internal_type::iterator iterator;
+ typedef typename internal_type::const_iterator const_iterator;
+ typedef typename internal_type::local_iterator local_iterator;
+ typedef typename internal_type::const_local_iterator const_local_iterator;
typedef typename value_traits::node_traits node_traits;
typedef typename node_traits::node node;
typedef typename pointer_traits
@@ -1244,8 +1640,8 @@ class hashtable_impl
<const_node_ptr>::reference const_node_reference;
typedef typename slist_impl::node_algorithms node_algorithms;
- static const bool stateful_value_traits = detail::is_stateful_value_traits<value_traits>::value;
- static const bool store_hash = detail::store_hash_is_true<node_traits>::value;
+ static const bool stateful_value_traits = internal_type::stateful_value_traits;
+ static const bool store_hash = internal_type::store_hash;
static const bool unique_keys = 0 != (BoolFlags & hash_bool_flags::unique_keys_pos);
static const bool constant_time_size = 0 != (BoolFlags & hash_bool_flags::constant_time_size_pos);
@@ -1258,6 +1654,7 @@ class hashtable_impl
= detail::optimize_multikey_is_true<node_traits>::value && !unique_keys;
/// @cond
+ static const bool is_multikey = !unique_keys;
private:
//Configuration error: compare_hash<> can't be specified without store_hash<>
@@ -1272,12 +1669,11 @@ class hashtable_impl
//optimize_multikey is not true
typedef unordered_group_adapter<node_traits> group_traits;
typedef circular_slist_algorithms<group_traits> group_algorithms;
- typedef detail::bool_<store_hash> store_hash_t;
+ typedef typename internal_type::store_hash_t store_hash_t;
typedef detail::bool_<optimize_multikey> optimize_multikey_t;
typedef detail::bool_<cache_begin> cache_begin_t;
typedef detail::bool_<power_2_buckets> power_2_buckets_t;
- typedef detail::size_holder<constant_time_size, size_type> size_traits;
- typedef detail::size_holder<incremental, size_type, int> split_traits;
+ typedef typename internal_type::split_traits split_traits;
typedef detail::group_functions<node_traits> group_functions_t;
typedef detail::node_functions<node_traits> node_functions_t;
@@ -1285,7 +1681,7 @@ class hashtable_impl
//noncopyable, movable
BOOST_MOVABLE_BUT_NOT_COPYABLE(hashtable_impl)
- static const bool safemode_or_autounlink = is_safe_autounlink<value_traits::link_mode>::value;
+ static const bool safemode_or_autounlink = internal_type::safemode_or_autounlink;
//Constant-time size is incompatible with auto-unlink hooks!
BOOST_STATIC_ASSERT(!(constant_time_size && ((int)value_traits::link_mode == (int)auto_unlink)));
@@ -1293,14 +1689,19 @@ class hashtable_impl
BOOST_STATIC_ASSERT(!(cache_begin && ((int)value_traits::link_mode == (int)auto_unlink)));
template<class Disposer>
- node_cast_adaptor< detail::node_disposer<Disposer, value_traits, CircularSListAlgorithms>
- , slist_node_ptr, node_ptr >
+ struct typeof_node_disposer
+ {
+ typedef node_cast_adaptor
+ < detail::node_disposer< Disposer, value_traits, CircularSListAlgorithms>
+ , slist_node_ptr, node_ptr > type;
+ };
+
+ template<class Disposer>
+ typename typeof_node_disposer<Disposer>::type
make_node_disposer(const Disposer &disposer) const
{
- return node_cast_adaptor
- < detail::node_disposer<Disposer, value_traits, CircularSListAlgorithms>
- , slist_node_ptr, node_ptr >
- (disposer, &this->priv_value_traits());
+ typedef typename typeof_node_disposer<Disposer>::type return_t;
+ return return_t(disposer, &this->priv_value_traits());
}
/// @endcond
@@ -1308,17 +1709,6 @@ class hashtable_impl
public:
typedef detail::insert_commit_data_impl insert_commit_data;
- typedef detail::transform_iterator
- < typename slist_impl::iterator
- , downcast_node_to_value_t
- < value_traits
- , false> > local_iterator;
-
- typedef detail::transform_iterator
- < typename slist_impl::iterator
- , downcast_node_to_value_t
- < value_traits
- , true> > const_local_iterator;
public:
@@ -1339,9 +1729,42 @@ class hashtable_impl
, const hasher & hash_func = hasher()
, const key_equal &equal_func = key_equal()
, const value_traits &v_traits = value_traits())
- : data_type(b_traits, hash_func, equal_func, v_traits)
+ : internal_type(v_traits, b_traits, hash_func, equal_func)
{
- this->data_type::internal.priv_initialize_buckets_and_cache();
+ this->priv_initialize_buckets_and_cache();
+ this->priv_size_traits().set_size(size_type(0));
+ size_type bucket_sz = this->priv_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(bucket_sz>>1);
+ }
+
+ //! <b>Requires</b>: buckets must not be being used by any other resource
+ //! and dereferencing iterator must yield an lvalue of type value_type.
+ //!
+ //! <b>Effects</b>: Constructs an empty container and inserts elements from
+ //! [b, e).
+ //!
+ //! <b>Complexity</b>: If N is distance(b, e): Average case is O(N)
+ //! (with a good hash function and with buckets_len >= N),worst case O(N^2).
+ //!
+ //! <b>Throws</b>: If value_traits::node_traits::node
+ //! constructor throws (this does not happen with predefined Boost.Intrusive hooks)
+ //! or the copy constructor or invocation of hasher or key_equal throws.
+ //!
+ //! <b>Notes</b>: buckets array must be disposed only after
+ //! *this is disposed.
+ template<class Iterator>
+ hashtable_impl ( bool unique, Iterator b, Iterator e
+ , const bucket_traits &b_traits
+ , const hasher & hash_func = hasher()
+ , 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->priv_bucket_count();
BOOST_INTRUSIVE_INVARIANT_ASSERT(bucket_sz != 0);
@@ -1349,16 +1772,21 @@ class hashtable_impl
BOOST_INTRUSIVE_INVARIANT_ASSERT
(!power_2_buckets || (0 == (bucket_sz & (bucket_sz-1))));
this->priv_split_traits().set_size(bucket_sz>>1);
+ //Now insert
+ if(unique)
+ this->insert_unique(b, e);
+ else
+ this->insert_equal(b, e);
}
//! <b>Effects</b>: to-do
//!
hashtable_impl(BOOST_RV_REF(hashtable_impl) x)
- : data_type( ::boost::move(x.priv_bucket_traits())
- , ::boost::move(x.priv_hasher())
- , ::boost::move(x.priv_equal())
- , ::boost::move(x.priv_value_traits())
- )
+ : internal_type( ::boost::move(x.priv_value_traits())
+ , ::boost::move(x.priv_bucket_traits())
+ , ::boost::move(x.priv_hasher())
+ , ::boost::move(x.priv_equal())
+ )
{
this->priv_swap_cache(x);
x.priv_initialize_cache();
@@ -1387,7 +1815,6 @@ class hashtable_impl
//!
//! <b>Throws</b>: Nothing.
~hashtable_impl();
- #endif
//! <b>Effects</b>: Returns an iterator pointing to the beginning of the unordered_set.
//!
@@ -1395,8 +1822,7 @@ class hashtable_impl
//! Worst case (empty unordered_set): O(this->bucket_count())
//!
//! <b>Throws</b>: Nothing.
- iterator begin()
- { return iterator(this->priv_begin(), &this->get_bucket_value_traits()); }
+ iterator begin();
//! <b>Effects</b>: Returns a const_iterator pointing to the beginning
//! of the unordered_set.
@@ -1405,8 +1831,7 @@ class hashtable_impl
//! Worst case (empty unordered_set): O(this->bucket_count())
//!
//! <b>Throws</b>: Nothing.
- const_iterator begin() const
- { return this->cbegin(); }
+ const_iterator begin() const;
//! <b>Effects</b>: Returns a const_iterator pointing to the beginning
//! of the unordered_set.
@@ -1415,48 +1840,44 @@ class hashtable_impl
//! Worst case (empty unordered_set): O(this->bucket_count())
//!
//! <b>Throws</b>: Nothing.
- const_iterator cbegin() const
- { return const_iterator(this->priv_begin(), &this->get_bucket_value_traits()); }
+ const_iterator cbegin() const;
//! <b>Effects</b>: Returns an iterator pointing to the end of the unordered_set.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- iterator end()
- { return iterator(this->priv_invalid_local_it(), 0); }
+ iterator end();
//! <b>Effects</b>: Returns a const_iterator pointing to the end of the unordered_set.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- const_iterator end() const
- { return this->cend(); }
+ const_iterator end() const;
//! <b>Effects</b>: Returns a const_iterator pointing to the end of the unordered_set.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- const_iterator cend() const
- { return const_iterator(this->priv_invalid_local_it(), 0); }
+ const_iterator cend() const;
//! <b>Effects</b>: Returns the hasher object used by the unordered_set.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: If hasher copy-constructor throws.
- hasher hash_function() const
- { return this->priv_hasher(); }
+ hasher hash_function() const;
//! <b>Effects</b>: Returns the key_equal object used by the unordered_set.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: If key_equal copy-constructor throws.
- key_equal key_eq() const
- { return this->priv_equal(); }
+ key_equal key_eq() const;
+
+ #endif
//! <b>Effects</b>: Returns true if the container is empty.
//!
@@ -1525,16 +1946,8 @@ class hashtable_impl
::boost::adl_move_swap(this->priv_bucket_traits(), other.priv_bucket_traits());
::boost::adl_move_swap(this->priv_value_traits(), other.priv_value_traits());
this->priv_swap_cache(other);
- if(constant_time_size){
- size_type backup = this->priv_size_traits().get_size();
- this->priv_size_traits().set_size(other.priv_size_traits().get_size());
- other.priv_size_traits().set_size(backup);
- }
- if(incremental){
- size_type backup = this->priv_split_traits().get_size();
- this->priv_split_traits().set_size(other.priv_split_traits().get_size());
- other.priv_split_traits().set_size(backup);
- }
+ ::boost::adl_move_swap(this->priv_size_traits(), other.priv_size_traits());
+ ::boost::adl_move_swap(this->priv_split_traits(), other.priv_split_traits());
}
//! <b>Requires</b>: Disposer::operator()(pointer) shouldn't throw
@@ -1558,87 +1971,30 @@ class hashtable_impl
//! throws. Basic guarantee.
template <class Cloner, class Disposer>
void clone_from(const hashtable_impl &src, Cloner cloner, Disposer disposer)
- {
- this->clear_and_dispose(disposer);
- if(!constant_time_size || !src.empty()){
- const size_type src_bucket_count = src.bucket_count();
- const size_type dst_bucket_count = this->bucket_count();
- //Check power of two bucket array if the option is activated
- BOOST_INTRUSIVE_INVARIANT_ASSERT
- (!power_2_buckets || (0 == (src_bucket_count & (src_bucket_count-1))));
- BOOST_INTRUSIVE_INVARIANT_ASSERT
- (!power_2_buckets || (0 == (dst_bucket_count & (dst_bucket_count-1))));
+ { this->priv_clone_from(src, cloner, disposer); }
- //If src bucket count is bigger or equal, structural copy is possible
- if(!incremental && (src_bucket_count >= dst_bucket_count)){
- //First clone the first ones
- const bucket_ptr src_buckets = src.priv_bucket_pointer();
- const bucket_ptr dst_buckets = this->priv_bucket_pointer();
- size_type constructed;
-
- typedef node_cast_adaptor< detail::node_disposer<Disposer, value_traits, CircularSListAlgorithms>
- , slist_node_ptr, node_ptr > NodeDisposer;
- typedef node_cast_adaptor< detail::node_cloner <Cloner, value_traits, CircularSListAlgorithms>
- , slist_node_ptr, node_ptr > NodeCloner;
- NodeDisposer node_disp(disposer, &this->priv_value_traits());
-
- detail::exception_array_disposer<bucket_type, NodeDisposer, size_type>
- rollback(dst_buckets[0], node_disp, constructed);
- for( constructed = 0
- ; constructed < dst_bucket_count
- ; ++constructed){
- dst_buckets[constructed].clone_from
- ( src_buckets[constructed]
- , NodeCloner(cloner, &this->priv_value_traits()), node_disp);
- }
- if(src_bucket_count != dst_bucket_count){
- //Now insert the remaining ones using the modulo trick
- for(//"constructed" comes from the previous loop
- ; constructed < src_bucket_count
- ; ++constructed){
- bucket_type &dst_b =
- dst_buckets[detail::hash_to_bucket_split<power_2_buckets, incremental>(constructed, dst_bucket_count, dst_bucket_count)];
- bucket_type &src_b = src_buckets[constructed];
- for( siterator b(src_b.begin()), e(src_b.end())
- ; b != e
- ; ++b){
- dst_b.push_front(*(NodeCloner(cloner, &this->priv_value_traits())(*b.pointed_node())));
- }
- }
- }
- this->priv_hasher() = src.priv_hasher();
- this->priv_equal() = src.priv_equal();
- rollback.release();
- this->priv_size_traits().set_size(src.priv_size_traits().get_size());
- this->priv_split_traits().set_size(dst_bucket_count);
- this->priv_insertion_update_cache(0u);
- this->priv_erasure_update_cache();
- }
- else if(store_hash){
- //Unlike previous cloning algorithm, this can throw
- //if cloner, hasher or comparison functor throw
- const_iterator b(src.cbegin()), e(src.cend());
- detail::exception_disposer<hashtable_impl, Disposer>
- rollback(*this, disposer);
- for(; b != e; ++b){
- std::size_t hash_value = this->priv_stored_or_compute_hash(*b, store_hash_t());;
- this->priv_insert_equal_with_hash(*cloner(*b), hash_value);
- }
- rollback.release();
- }
- else{
- //Unlike previous cloning algorithm, this can throw
- //if cloner, hasher or comparison functor throw
- const_iterator b(src.cbegin()), e(src.cend());
- detail::exception_disposer<hashtable_impl, Disposer>
- rollback(*this, disposer);
- for(; b != e; ++b){
- this->insert_equal(*cloner(*b));
- }
- rollback.release();
- }
- }
- }
+ //! <b>Requires</b>: Disposer::operator()(pointer) shouldn't throw
+ //! Cloner should yield to nodes that compare equal and produce the same
+ //! hash than the original node.
+ //!
+ //! <b>Effects</b>: Erases all the elements from *this
+ //! calling Disposer::operator()(pointer), clones all the
+ //! elements from src calling Cloner::operator()(reference)
+ //! and inserts them on *this. The hash function and the equality
+ //! predicate are copied from the source.
+ //!
+ //! If store_hash option is true, this method does not use the hash function.
+ //!
+ //! If any operation throws, all cloned elements are unlinked and disposed
+ //! calling Disposer::operator()(pointer).
+ //!
+ //! <b>Complexity</b>: Linear to erased plus inserted elements.
+ //!
+ //! <b>Throws</b>: If cloner or hasher throw or hash or equality predicate copying
+ //! throws. Basic guarantee.
+ template <class Cloner, class Disposer>
+ void clone_from(BOOST_RV_REF(hashtable_impl) src, Cloner cloner, Disposer disposer)
+ { this->priv_clone_from(static_cast<hashtable_impl&>(src), cloner, disposer); }
//! <b>Requires</b>: value must be an lvalue
//!
@@ -1657,9 +2013,10 @@ class hashtable_impl
size_type bucket_num;
std::size_t hash_value;
siterator prev;
- siterator it = this->priv_find
- (value, this->priv_hasher(), this->priv_equal(), bucket_num, hash_value, prev);
- return this->priv_insert_equal_find(value, bucket_num, hash_value, it);
+ siterator const it = this->priv_find
+ (key_of_value()(value), this->priv_hasher(), this->priv_equal(), bucket_num, hash_value, prev);
+ bool const next_is_in_group = optimize_multikey && it != this->priv_invalid_local_it();
+ return this->priv_insert_equal_after_find(value, bucket_num, hash_value, prev, next_is_in_group);
}
//! <b>Requires</b>: Dereferencing iterator must yield an lvalue
@@ -1701,11 +2058,11 @@ class hashtable_impl
{
insert_commit_data commit_data;
std::pair<iterator, bool> ret = this->insert_unique_check
- (value, this->priv_hasher(), this->priv_equal(), commit_data);
- if(!ret.second)
- return ret;
- return std::pair<iterator, bool>
- (this->insert_unique_commit(value, commit_data), true);
+ (key_of_value()(value), this->priv_hasher(), this->priv_equal(), commit_data);
+ if(ret.second){
+ ret.first = this->insert_unique_commit(value, commit_data);
+ }
+ return ret;
}
//! <b>Requires</b>: Dereferencing iterator must yield an lvalue
@@ -1762,22 +2119,19 @@ class hashtable_impl
//! objects are inserted or erased from the unordered_set.
//!
//! After a successful rehashing insert_commit_data remains valid.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
+ template<class KeyType, class KeyHasher, class KeyEqual>
std::pair<iterator, bool> insert_unique_check
( const KeyType &key
, KeyHasher hash_func
- , KeyValueEqual equal_func
+ , KeyEqual equal_func
, insert_commit_data &commit_data)
{
size_type bucket_num;
siterator prev;
- siterator prev_pos =
- this->priv_find(key, hash_func, equal_func, bucket_num, commit_data.hash, prev);
- bool success = prev_pos == this->priv_invalid_local_it();
- if(success){
- prev_pos = prev;
- }
- return std::pair<iterator, bool>(iterator(prev_pos, &this->get_bucket_value_traits()),success);
+ siterator const pos = this->priv_find(key, hash_func, equal_func, bucket_num, commit_data.hash, prev);
+ return std::pair<iterator, bool>
+ ( iterator(pos, &this->get_bucket_value_traits())
+ , pos == this->priv_invalid_local_it());
}
//! <b>Requires</b>: value must be an lvalue of type value_type. commit_data
@@ -1804,12 +2158,11 @@ class hashtable_impl
size_type bucket_num = this->priv_hash_to_bucket(commit_data.hash);
bucket_type &b = this->priv_bucket_pointer()[bucket_num];
this->priv_size_traits().increment();
- node_ptr n = pointer_traits<node_ptr>::pointer_to(this->priv_value_to_node(value));
+ node_ptr const n = pointer_traits<node_ptr>::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());
- if(safemode_or_autounlink)
- BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(node_algorithms::unique(n));
this->priv_insertion_update_cache(bucket_num);
- group_functions_t::insert_in_group(node_ptr(), n, optimize_multikey_t());
+ 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());
}
@@ -1848,8 +2201,8 @@ class hashtable_impl
//!
//! <b>Note</b>: Invalidates the iterators (but not the references)
//! to the erased elements. No destructors are called.
- size_type erase(const_reference value)
- { return this->erase(value, this->priv_hasher(), this->priv_equal()); }
+ size_type erase(const key_type &key)
+ { return this->erase(key, this->priv_hasher(), this->priv_equal()); }
//! <b>Requires</b>: "hash_func" must be a hash function that induces
//! the same hash values as the stored hasher. The difference is that
@@ -1871,8 +2224,8 @@ class hashtable_impl
//!
//! <b>Note</b>: Invalidates the iterators (but not the references)
//! to the erased elements. No destructors are called.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
- size_type erase(const KeyType& key, KeyHasher hash_func, KeyValueEqual equal_func)
+ template<class KeyType, class KeyHasher, class KeyEqual>
+ size_type erase(const KeyType& key, KeyHasher hash_func, KeyEqual equal_func)
{ return this->erase_and_dispose(key, hash_func, equal_func, detail::null_disposer()); }
//! <b>Requires</b>: Disposer::operator()(pointer) shouldn't throw.
@@ -1887,15 +2240,16 @@ class hashtable_impl
//! <b>Note</b>: Invalidates the iterators
//! to the erased elements.
template<class Disposer>
- void erase_and_dispose(const_iterator i, Disposer disposer
- /// @cond
- , typename detail::enable_if_c<!detail::is_convertible<Disposer, const_iterator>::value >::type * = 0
- /// @endcond
- )
- {
- this->priv_erase(i, disposer, optimize_multikey_t());
+ BOOST_INTRUSIVE_DOC1ST(void
+ , typename detail::disable_if_convertible<Disposer BOOST_INTRUSIVE_I const_iterator>::type)
+ erase_and_dispose(const_iterator i, Disposer disposer)
+ {
+ //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_pointer()[first_bucket_num], first_local_it, make_node_disposer(disposer), optimize_multikey_t());
this->priv_size_traits().decrement();
- this->priv_erasure_update_cache();
+ this->priv_erasure_update_cache_range(first_bucket_num, first_bucket_num);
}
//! <b>Requires</b>: Disposer::operator()(pointer) shouldn't throw.
@@ -1934,7 +2288,10 @@ class hashtable_impl
last_local_it = e.slist_it();
last_bucket_num = this->priv_get_bucket_num(last_local_it);
}
- this->priv_erase_range(before_first_local_it, first_bucket_num, last_local_it, last_bucket_num, disposer);
+ size_type const num_erased = 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->priv_size_traits().set_size(this->priv_size_traits().get_size()-num_erased);
this->priv_erasure_update_cache_range(first_bucket_num, last_bucket_num);
}
}
@@ -1955,8 +2312,8 @@ class hashtable_impl
//! <b>Note</b>: Invalidates the iterators (but not the references)
//! to the erased elements. No destructors are called.
template<class Disposer>
- size_type erase_and_dispose(const_reference value, Disposer disposer)
- { return this->erase_and_dispose(value, this->priv_hasher(), this->priv_equal(), disposer); }
+ size_type erase_and_dispose(const key_type &key, Disposer disposer)
+ { return this->erase_and_dispose(key, this->priv_hasher(), this->priv_equal(), disposer); }
//! <b>Requires</b>: Disposer::operator()(pointer) shouldn't throw.
//!
@@ -1973,45 +2330,37 @@ class hashtable_impl
//!
//! <b>Note</b>: Invalidates the iterators
//! to the erased elements.
- template<class KeyType, class KeyHasher, class KeyValueEqual, class Disposer>
+ template<class KeyType, class KeyHasher, class KeyEqual, class Disposer>
size_type erase_and_dispose(const KeyType& key, KeyHasher hash_func
- ,KeyValueEqual equal_func, Disposer disposer)
+ ,KeyEqual equal_func, Disposer disposer)
{
size_type bucket_num;
std::size_t h;
siterator prev;
siterator it = this->priv_find(key, hash_func, equal_func, bucket_num, h, prev);
- bool success = it != this->priv_invalid_local_it();
+ bool const success = it != this->priv_invalid_local_it();
+
size_type cnt(0);
- if(!success){
- return 0;
- }
- else if(optimize_multikey){
- siterator last = bucket_type::s_iterator_to
- (*node_traits::get_next(group_functions_t::get_last_in_group
- (detail::dcast_bucket_ptr<node>(it.pointed_node()), optimize_multikey_t())));
- this->priv_erase_range_impl(bucket_num, prev, last, disposer, cnt);
- }
- else{
- //If found erase all equal values
- bucket_type &b = this->priv_bucket_pointer()[bucket_num];
- for(siterator end_sit = b.end(); it != end_sit; ++cnt, ++it){
- slist_node_ptr n(it.pointed_node());
- const value_type &v = this->priv_value_from_slist_node(n);
- if(compare_hash){
- std::size_t vh = this->priv_stored_or_compute_hash(v, store_hash_t());
- if(h != vh || !equal_func(key, v)){
- break;
- }
- }
- else if(!equal_func(key, v)){
- break;
- }
- this->priv_size_traits().decrement();
+ if(success){
+ if(optimize_multikey){
+ cnt = this->priv_erase_from_single_bucket
+ (this->priv_bucket_pointer()[bucket_num], prev, ++(priv_last_in_group)(it), make_node_disposer(disposer), optimize_multikey_t());
}
- b.erase_after_and_dispose(prev, it, make_node_disposer(disposer));
+ else{
+ bucket_type &b = this->priv_bucket_pointer()[bucket_num];
+ siterator const end_sit = b.end();
+ 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_size_traits().set_size(this->priv_size_traits().get_size()-cnt);
+ this->priv_erasure_update_cache();
}
- this->priv_erasure_update_cache();
+
return cnt;
}
@@ -2026,7 +2375,7 @@ class hashtable_impl
//! to the erased elements. No destructors are called.
void clear()
{
- this->data_type::internal.priv_clear_buckets_and_cache();
+ this->priv_clear_buckets_and_cache();
this->priv_size_traits().set_size(size_type(0));
}
@@ -2047,8 +2396,9 @@ 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<Disposer>::type d(disposer, &this->priv_value_traits());
for(; num_buckets--; ++b){
- b->clear_and_dispose(make_node_disposer(disposer));
+ b->clear_and_dispose(d);
}
this->priv_size_traits().set_size(size_type(0));
}
@@ -2060,8 +2410,8 @@ class hashtable_impl
//! <b>Complexity</b>: Average case O(1), worst case O(this->size()).
//!
//! <b>Throws</b>: If the internal hasher or the equality functor throws.
- size_type count(const_reference value) const
- { return this->count(value, this->priv_hasher(), this->priv_equal()); }
+ size_type count(const key_type &key) const
+ { return this->count(key, this->priv_hasher(), this->priv_equal()); }
//! <b>Requires</b>: "hash_func" must be a hash function that induces
//! the same hash values as the stored hasher. The difference is that
@@ -2076,11 +2426,12 @@ class hashtable_impl
//! <b>Complexity</b>: Average case O(1), worst case O(this->size()).
//!
//! <b>Throws</b>: If hash_func or equal throw.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
- size_type count(const KeyType &key, const KeyHasher &hash_func, const KeyValueEqual &equal_func) const
+ template<class KeyType, class KeyHasher, class KeyEqual>
+ size_type count(const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) const
{
- size_type bucket_n1, bucket_n2, cnt;
- this->priv_equal_range(key, hash_func, equal_func, bucket_n1, bucket_n2, cnt);
+ size_type cnt;
+ size_type n_bucket;
+ this->priv_local_equal_range(key, hash_func, equal_func, n_bucket, cnt);
return cnt;
}
@@ -2090,8 +2441,8 @@ class hashtable_impl
//! <b>Complexity</b>: Average case O(1), worst case O(this->size()).
//!
//! <b>Throws</b>: If the internal hasher or the equality functor throws.
- iterator find(const_reference value)
- { return this->find(value, this->priv_hasher(), this->priv_equal()); }
+ iterator find(const key_type &key)
+ { return this->find(key, this->priv_hasher(), this->priv_equal()); }
//! <b>Requires</b>: "hash_func" must be a hash function that induces
//! the same hash values as the stored hasher. The difference is that
@@ -2112,14 +2463,14 @@ class hashtable_impl
//! <b>Note</b>: This function is used when constructing a value_type
//! is expensive and the value_type can be compared with a cheaper
//! key type. Usually this key is part of the value_type.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
- iterator find(const KeyType &key, KeyHasher hash_func, KeyValueEqual equal_func)
+ template<class KeyType, class KeyHasher, class KeyEqual>
+ iterator find(const KeyType &key, KeyHasher hash_func, KeyEqual equal_func)
{
size_type bucket_n;
std::size_t hash;
siterator prev;
- siterator local_it = this->priv_find(key, hash_func, equal_func, bucket_n, hash, prev);
- return iterator(local_it, &this->get_bucket_value_traits());
+ return iterator( this->priv_find(key, hash_func, equal_func, bucket_n, hash, prev)
+ , &this->get_bucket_value_traits());
}
//! <b>Effects</b>: Finds a const_iterator to the first element whose key is
@@ -2128,8 +2479,8 @@ class hashtable_impl
//! <b>Complexity</b>: Average case O(1), worst case O(this->size()).
//!
//! <b>Throws</b>: If the internal hasher or the equality functor throws.
- const_iterator find(const_reference value) const
- { return this->find(value, this->priv_hasher(), this->priv_equal()); }
+ const_iterator find(const key_type &key) const
+ { return this->find(key, this->priv_hasher(), this->priv_equal()); }
//! <b>Requires</b>: "hash_func" must be a hash function that induces
//! the same hash values as the stored hasher. The difference is that
@@ -2150,15 +2501,15 @@ class hashtable_impl
//! <b>Note</b>: This function is used when constructing a value_type
//! is expensive and the value_type can be compared with a cheaper
//! key type. Usually this key is part of the value_type.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
+ template<class KeyType, class KeyHasher, class KeyEqual>
const_iterator find
- (const KeyType &key, KeyHasher hash_func, KeyValueEqual equal_func) const
+ (const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) const
{
size_type bucket_n;
std::size_t hash_value;
siterator prev;
- siterator sit = this->priv_find(key, hash_func, equal_func, bucket_n, hash_value, prev);
- return const_iterator(sit, &this->get_bucket_value_traits());
+ return const_iterator( this->priv_find(key, hash_func, equal_func, bucket_n, hash_value, prev)
+ , &this->get_bucket_value_traits());
}
//! <b>Effects</b>: Returns a range containing all elements with values equivalent
@@ -2168,8 +2519,8 @@ class hashtable_impl
//! <b>Complexity</b>: Average case O(this->count(value)). Worst case O(this->size()).
//!
//! <b>Throws</b>: If the internal hasher or the equality functor throws.
- std::pair<iterator,iterator> equal_range(const_reference value)
- { return this->equal_range(value, this->priv_hasher(), this->priv_equal()); }
+ std::pair<iterator,iterator> equal_range(const key_type &key)
+ { return this->equal_range(key, this->priv_hasher(), this->priv_equal()); }
//! <b>Requires</b>: "hash_func" must be a hash function that induces
//! the same hash values as the stored hasher. The difference is that
@@ -2191,15 +2542,15 @@ class hashtable_impl
//! <b>Note</b>: This function is used when constructing a value_type
//! is expensive and the value_type can be compared with a cheaper
//! key type. Usually this key is part of the value_type.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
+ template<class KeyType, class KeyHasher, class KeyEqual>
std::pair<iterator,iterator> equal_range
- (const KeyType &key, KeyHasher hash_func, KeyValueEqual equal_func)
+ (const KeyType &key, KeyHasher hash_func, KeyEqual equal_func)
{
- size_type bucket_n1, bucket_n2, cnt;
- std::pair<siterator, siterator> ret = this->priv_equal_range
- (key, hash_func, equal_func, bucket_n1, bucket_n2, cnt);
+ std::pair<siterator, siterator> ret =
+ this->priv_equal_range(key, hash_func, equal_func);
return std::pair<iterator, iterator>
- (iterator(ret.first, &this->get_bucket_value_traits()), iterator(ret.second, &this->get_bucket_value_traits()));
+ ( iterator(ret.first, &this->get_bucket_value_traits())
+ , iterator(ret.second, &this->get_bucket_value_traits()));
}
//! <b>Effects</b>: Returns a range containing all elements with values equivalent
@@ -2210,8 +2561,8 @@ class hashtable_impl
//!
//! <b>Throws</b>: If the internal hasher or the equality functor throws.
std::pair<const_iterator, const_iterator>
- equal_range(const_reference value) const
- { return this->equal_range(value, this->priv_hasher(), this->priv_equal()); }
+ equal_range(const key_type &key) const
+ { return this->equal_range(key, this->priv_hasher(), this->priv_equal()); }
//! <b>Requires</b>: "hash_func" must be a hash function that induces
//! the same hash values as the stored hasher. The difference is that
@@ -2233,18 +2584,19 @@ class hashtable_impl
//! <b>Note</b>: This function is used when constructing a value_type
//! is expensive and the value_type can be compared with a cheaper
//! key type. Usually this key is part of the value_type.
- template<class KeyType, class KeyHasher, class KeyValueEqual>
+ template<class KeyType, class KeyHasher, class KeyEqual>
std::pair<const_iterator,const_iterator> equal_range
- (const KeyType &key, KeyHasher hash_func, KeyValueEqual equal_func) const
+ (const KeyType &key, KeyHasher hash_func, KeyEqual equal_func) const
{
- size_type bucket_n1, bucket_n2, cnt;
std::pair<siterator, siterator> ret =
- this->priv_equal_range(key, hash_func, equal_func, bucket_n1, bucket_n2, cnt);
+ this->priv_equal_range(key, hash_func, equal_func);
return std::pair<const_iterator, const_iterator>
( const_iterator(ret.first, &this->get_bucket_value_traits())
, const_iterator(ret.second, &this->get_bucket_value_traits()));
}
+ #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
+
//! <b>Requires</b>: value must be an lvalue and shall be in a unordered_set of
//! appropriate type. Otherwise the behavior is undefined.
//!
@@ -2254,11 +2606,7 @@ class hashtable_impl
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: If the internal hash function throws.
- iterator iterator_to(reference value)
- {
- return iterator(bucket_type::s_iterator_to
- (this->priv_value_to_node(value)), &this->get_bucket_value_traits());
- }
+ iterator iterator_to(reference value);
//! <b>Requires</b>: value must be an lvalue and shall be in a unordered_set of
//! appropriate type. Otherwise the behavior is undefined.
@@ -2269,13 +2617,7 @@ class hashtable_impl
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: If the internal hash function throws.
- const_iterator iterator_to(const_reference value) const
- {
- node_reference r = *pointer_traits<node_ptr>::const_cast_from
- (pointer_traits<const_node_ptr>::pointer_to(this->priv_value_to_node(value)));
- siterator sit = bucket_type::s_iterator_to(r);
- return const_iterator(sit, &this->get_bucket_value_traits());
- }
+ const_iterator iterator_to(const_reference value) const;
//! <b>Requires</b>: value must be an lvalue and shall be in a unordered_set of
//! appropriate type. Otherwise the behavior is undefined.
@@ -2289,12 +2631,7 @@ class hashtable_impl
//!
//! <b>Note</b>: This static function is available only if the <i>value traits</i>
//! is stateless.
- static local_iterator s_local_iterator_to(reference value)
- {
- BOOST_STATIC_ASSERT((!stateful_value_traits));
- siterator sit = bucket_type::s_iterator_to(*value_traits::to_node_ptr(value));
- return local_iterator(sit, const_value_traits_ptr());
- }
+ static local_iterator s_local_iterator_to(reference value);
//! <b>Requires</b>: value must be an lvalue and shall be in a unordered_set of
//! appropriate type. Otherwise the behavior is undefined.
@@ -2308,14 +2645,7 @@ class hashtable_impl
//!
//! <b>Note</b>: This static function is available only if the <i>value traits</i>
//! is stateless.
- static const_local_iterator s_local_iterator_to(const_reference value)
- {
- BOOST_STATIC_ASSERT((!stateful_value_traits));
- node_reference r = *pointer_traits<node_ptr>::const_cast_from
- (value_traits::to_node_ptr(value));
- siterator sit = bucket_type::s_iterator_to(r);
- return const_local_iterator(sit, const_value_traits_ptr());
- }
+ static const_local_iterator s_local_iterator_to(const_reference value);
//! <b>Requires</b>: value must be an lvalue and shall be in a unordered_set of
//! appropriate type. Otherwise the behavior is undefined.
@@ -2326,11 +2656,7 @@ class hashtable_impl
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- local_iterator local_iterator_to(reference value)
- {
- siterator sit = bucket_type::s_iterator_to(this->priv_value_to_node(value));
- return local_iterator(sit, this->priv_value_traits_ptr());
- }
+ local_iterator local_iterator_to(reference value);
//! <b>Requires</b>: value must be an lvalue and shall be in a unordered_set of
//! appropriate type. Otherwise the behavior is undefined.
@@ -2341,13 +2667,7 @@ class hashtable_impl
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- const_local_iterator local_iterator_to(const_reference value) const
- {
- node_reference r = *pointer_traits<node_ptr>::const_cast_from
- (pointer_traits<const_node_ptr>::pointer_to(this->priv_value_to_node(value)));
- siterator sit = bucket_type::s_iterator_to(r);
- return const_local_iterator(sit, this->priv_value_traits_ptr());
- }
+ const_local_iterator local_iterator_to(const_reference value) const;
//! <b>Effects</b>: Returns the number of buckets passed in the constructor
//! or the last rehash function.
@@ -2355,8 +2675,7 @@ class hashtable_impl
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- size_type bucket_count() const
- { return this->priv_bucket_count(); }
+ size_type bucket_count() const;
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2365,8 +2684,8 @@ class hashtable_impl
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- size_type bucket_size(size_type n) const
- { return this->priv_bucket_pointer()[n].size(); }
+ size_type bucket_size(size_type n) const;
+ #endif //#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
//! <b>Effects</b>: Returns the index of the bucket in which elements
//! with keys equivalent to k would be found, if any such element existed.
@@ -2392,17 +2711,17 @@ class hashtable_impl
//!
//! <b>Note</b>: the return value is in the range [0, this->bucket_count()).
template<class KeyType, class KeyHasher>
- size_type bucket(const KeyType& k, const KeyHasher &hash_func) const
+ size_type bucket(const KeyType& k, KeyHasher hash_func) const
{ return this->priv_hash_to_bucket(hash_func(k)); }
+ #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
//! <b>Effects</b>: Returns the bucket array pointer passed in the constructor
//! or the last rehash function.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Throws</b>: Nothing.
- bucket_ptr bucket_pointer() const
- { return this->priv_bucket_pointer(); }
+ bucket_ptr bucket_pointer() const;
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2415,8 +2734,7 @@ class hashtable_impl
//!
//! <b>Note</b>: [this->begin(n), this->end(n)) is a valid range
//! containing all of the elements in the nth bucket.
- local_iterator begin(size_type n)
- { return local_iterator(this->priv_bucket_pointer()[n].begin(), this->priv_value_traits_ptr()); }
+ local_iterator begin(size_type n);
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2429,8 +2747,7 @@ class hashtable_impl
//!
//! <b>Note</b>: [this->begin(n), this->end(n)) is a valid range
//! containing all of the elements in the nth bucket.
- const_local_iterator begin(size_type n) const
- { return this->cbegin(n); }
+ const_local_iterator begin(size_type n) const;
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2443,11 +2760,7 @@ class hashtable_impl
//!
//! <b>Note</b>: [this->begin(n), this->end(n)) is a valid range
//! containing all of the elements in the nth bucket.
- const_local_iterator cbegin(size_type n) const
- {
- bucket_reference br = pointer_traits<bucket_ptr>::const_cast_from(this->priv_bucket_pointer())[n];
- return const_local_iterator(br.begin(), this->priv_value_traits_ptr());
- }
+ const_local_iterator cbegin(size_type n) const;
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2460,8 +2773,7 @@ class hashtable_impl
//!
//! <b>Note</b>: [this->begin(n), this->end(n)) is a valid range
//! containing all of the elements in the nth bucket.
- local_iterator end(size_type n)
- { return local_iterator(this->priv_bucket_pointer()[n].end(), this->priv_value_traits_ptr()); }
+ local_iterator end(size_type n);
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2474,8 +2786,7 @@ class hashtable_impl
//!
//! <b>Note</b>: [this->begin(n), this->end(n)) is a valid range
//! containing all of the elements in the nth bucket.
- const_local_iterator end(size_type n) const
- { return this->cend(n); }
+ const_local_iterator end(size_type n) const;
//! <b>Requires</b>: n is in the range [0, this->bucket_count()).
//!
@@ -2488,11 +2799,8 @@ class hashtable_impl
//!
//! <b>Note</b>: [this->begin(n), this->end(n)) is a valid range
//! containing all of the elements in the nth bucket.
- const_local_iterator cend(size_type n) const
- {
- bucket_reference br = pointer_traits<bucket_ptr>::const_cast_from(this->priv_bucket_pointer())[n];
- return const_local_iterator ( br.end(), this->priv_value_traits_ptr());
- }
+ const_local_iterator cend(size_type n) const;
+ #endif //#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
//! <b>Requires</b>: new_bucket_traits can hold a pointer to a new bucket array
//! or the same as the old bucket array with a different length. new_size is the length of the
@@ -2520,36 +2828,33 @@ class hashtable_impl
//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))));
+ (!power_2_buckets || (0 == (new_bucket_count & (new_bucket_count-1u))));
size_type n = 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.
- const bool fast_shrink = (!incremental) && (old_bucket_count > new_bucket_count) &&
- (power_2_buckets ||(old_bucket_count % new_bucket_count) == 0);
+ const bool fast_shrink = (!incremental) && (old_bucket_count >= new_bucket_count) &&
+ (power_2_buckets || (old_bucket_count % new_bucket_count) == 0);
//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;
- new_first_bucket_num = this->priv_get_cache_bucket_num();
}
//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<node_algorithms> NodeDisposer;
+ typedef detail::exception_array_disposer<bucket_type, NodeDisposer, size_type> ArrayDisposer;
NodeDisposer node_disp;
- bucket_type & newbuck = new_buckets[0];
- bucket_type & oldbuck = old_buckets[0];
- detail::exception_array_disposer<bucket_type, NodeDisposer, size_type>
- rollback1(newbuck, node_disp, new_bucket_count);
- detail::exception_array_disposer<bucket_type, NodeDisposer, size_type>
- rollback2(oldbuck, node_disp, old_bucket_count);
+ ArrayDisposer rollback1(new_buckets[0], node_disp, new_bucket_count);
+ ArrayDisposer rollback2(old_buckets[0], node_disp, old_bucket_count);
//Put size in a safe value for rollback exception
- size_type size_backup = this->priv_size_traits().get_size();
+ 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();
@@ -2558,20 +2863,17 @@ class hashtable_impl
//Iterate through nodes
for(; n < old_bucket_count; ++n){
bucket_type &old_bucket = old_buckets[n];
-
if(!fast_shrink){
- siterator before_i(old_bucket.before_begin());
- siterator end_sit(old_bucket.end());
- siterator i(old_bucket.begin());
- for(;i != end_sit; ++i){
+ 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());
const std::size_t hash_value = this->priv_stored_or_compute_hash(v, store_hash_t());
- const size_type new_n = detail::hash_to_bucket_split<power_2_buckets, incremental>(hash_value, new_bucket_count, new_bucket_count);
+ const size_type new_n = detail::hash_to_bucket_split<power_2_buckets, incremental>
+ (hash_value, new_bucket_count, new_bucket_count);
if(cache_begin && new_n < new_first_bucket_num)
new_first_bucket_num = new_n;
- siterator last = bucket_type::s_iterator_to
- (*group_functions_t::get_last_in_group
- (detail::dcast_bucket_ptr<node>(i.pointed_node()), optimize_multikey_t()));
+ siterator const last = (priv_last_in_group)(i);
if(same_buffer && new_n == n){
before_i = last;
}
@@ -2579,7 +2881,6 @@ class hashtable_impl
bucket_type &new_b = new_buckets[new_n];
new_b.splice_after(new_b.before_begin(), old_bucket, before_i, last);
}
- i = before_i;
}
}
else{
@@ -2587,12 +2888,10 @@ class hashtable_impl
if(cache_begin && new_n < new_first_bucket_num)
new_first_bucket_num = new_n;
bucket_type &new_b = new_buckets[new_n];
- if(!old_bucket.empty()){
- new_b.splice_after( new_b.before_begin()
- , old_bucket
- , old_bucket.before_begin()
- , hashtable_impl::priv_get_last(old_bucket));
- }
+ 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()));
}
}
@@ -2621,56 +2920,47 @@ class hashtable_impl
const size_type split_idx = this->priv_split_traits().get_size();
const size_type bucket_cnt = this->priv_bucket_count();
const bucket_ptr buck_ptr = this->priv_bucket_pointer();
+ bool ret = false;
if(grow){
//Test if the split variable can be changed
- if(split_idx >= bucket_cnt)
- return false;
-
- const size_type bucket_to_rehash = split_idx - bucket_cnt/2;
- bucket_type &old_bucket = buck_ptr[bucket_to_rehash];
- siterator before_i(old_bucket.before_begin());
- const siterator end_sit(old_bucket.end());
- siterator i(old_bucket.begin());
- this->priv_split_traits().increment();
-
- //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<bucket_type, split_traits> rollback
- ( buck_ptr[split_idx], old_bucket, this->priv_split_traits());
- for(;i != end_sit; ++i){
- const value_type &v = this->priv_value_from_slist_node(i.pointed_node());
- const std::size_t hash_value = this->priv_stored_or_compute_hash(v, store_hash_t());
- const size_type new_n = this->priv_hash_to_bucket(hash_value);
- siterator last = bucket_type::s_iterator_to
- (*group_functions_t::get_last_in_group
- (detail::dcast_bucket_ptr<node>(i.pointed_node()), optimize_multikey_t()));
- if(new_n == bucket_to_rehash){
- before_i = last;
- }
- else{
- bucket_type &new_b = buck_ptr[new_n];
- new_b.splice_after(new_b.before_begin(), old_bucket, before_i, last);
+ if((ret = split_idx < bucket_cnt)){
+ const size_type bucket_to_rehash = split_idx - bucket_cnt/2;
+ bucket_type &old_bucket = buck_ptr[bucket_to_rehash];
+ this->priv_split_traits().increment();
+
+ //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<bucket_type, split_traits> rollback
+ ( buck_ptr[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());
+ const std::size_t hash_value = this->priv_stored_or_compute_hash(v, store_hash_t());
+ const size_type new_n = this->priv_hash_to_bucket(hash_value);
+ siterator const last = (priv_last_in_group)(i);
+ if(new_n == bucket_to_rehash){
+ before_i = last;
+ }
+ else{
+ bucket_type &new_b = buck_ptr[new_n];
+ new_b.splice_after(new_b.before_begin(), old_bucket, before_i, last);
+ }
}
- i = before_i;
+ rollback.release();
+ this->priv_erasure_update_cache();
}
- rollback.release();
- this->priv_erasure_update_cache();
- return true;
}
- else{
- //Test if the split variable can be changed
- if(split_idx <= bucket_cnt/2)
- return false;
+ else if((ret = split_idx > bucket_cnt/2)){ //!grow
const size_type target_bucket_num = split_idx - 1 - bucket_cnt/2;
bucket_type &target_bucket = buck_ptr[target_bucket_num];
bucket_type &source_bucket = buck_ptr[split_idx-1];
target_bucket.splice_after(target_bucket.cbefore_begin(), source_bucket);
this->priv_split_traits().decrement();
this->priv_insertion_update_cache(target_bucket_num);
- return true;
}
+ return ret;
}
//! <b>Effects</b>: If new_bucket_traits.bucket_count() is not
@@ -2690,24 +2980,22 @@ class hashtable_impl
{
//This function is only available for containers with incremental hashing
BOOST_STATIC_ASSERT(( incremental && power_2_buckets ));
- size_type new_bucket_traits_size = new_bucket_traits.bucket_count();
- size_type cur_bucket_traits = this->priv_bucket_count();
- if(new_bucket_traits_size/2 != cur_bucket_traits && new_bucket_traits_size != cur_bucket_traits/2){
- return false;
- }
-
+ size_type const new_bucket_traits_size = new_bucket_traits.bucket_count();
+ size_type const cur_bucket_traits = 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){
- //Test if the split variable can be changed
if(!(split_idx >= cur_bucket_traits))
return false;
}
- else{
- //Test if the split variable can be changed
- if(!(split_idx <= cur_bucket_traits/2))
+ else if(new_bucket_traits_size == cur_bucket_traits/2){
+ if(!(split_idx <= new_bucket_traits_size))
return false;
}
+ else{
+ return false;
+ }
const size_type ini_n = this->priv_get_cache_bucket_num();
const bucket_ptr old_buckets = this->priv_bucket_pointer();
@@ -2725,19 +3013,16 @@ class hashtable_impl
return true;
}
- //! <b>Requires</b>:
+ #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
+
+ //! <b>Requires</b>: incremental<> option must be set
//!
- //! <b>Effects</b>:
+ //! <b>Effects</b>: returns the current split count
//!
- //! <b>Complexity</b>:
+ //! <b>Complexity</b>: Constant
//!
- //! <b>Throws</b>:
- size_type split_count() const
- {
- //This function is only available if incremental hashing is activated
- BOOST_STATIC_ASSERT(( incremental && power_2_buckets ));
- return this->priv_split_traits().get_size();
- }
+ //! <b>Throws</b>: Nothing
+ size_type split_count() const;
//! <b>Effects</b>: Returns the nearest new bucket count optimized for
//! the container that is bigger or equal than n. This suggestion can be
@@ -2748,14 +3033,7 @@ class hashtable_impl
//! <b>Complexity</b>: Amortized constant time.
//!
//! <b>Throws</b>: Nothing.
- static size_type suggested_upper_bucket_count(size_type n)
- {
- 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);
- bound -= (bound == primes_end);
- return size_type(*bound);
- }
+ static size_type suggested_upper_bucket_count(size_type n);
//! <b>Effects</b>: Returns the nearest new bucket count optimized for
//! the container that is smaller or equal than n. This suggestion can be
@@ -2766,403 +3044,306 @@ class hashtable_impl
//! <b>Complexity</b>: Amortized constant time.
//!
//! <b>Throws</b>: Nothing.
- static size_type suggested_lower_bucket_count(size_type n)
- {
- 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;
- size_type const* bound = std::upper_bound(primes, primes_end, n);
- bound -= (bound != primes);
- return size_type(*bound);
- }
- /// @cond
- void check() const {}
- private:
- size_traits &priv_size_traits()
- { return static_cast<size_traits&>(static_cast<data_type&>(*this)); }
-
- const size_traits &priv_size_traits() const
- { return static_cast<const size_traits&>(static_cast<const data_type&>(*this)); }
-
- bucket_ptr priv_bucket_pointer() const
- { return this->data_type::internal.internal.internal.internal.priv_bucket_pointer(); }
-
- SizeType priv_bucket_count() const
- { return this->data_type::internal.internal.internal.internal.priv_bucket_count(); }
-
- const bucket_plus_vtraits<ValueTraits, BucketTraits> &get_bucket_value_traits() const
- { return this->data_type::internal.internal.internal.internal.get_bucket_value_traits(); }
-
- bucket_plus_vtraits<ValueTraits, BucketTraits> &get_bucket_value_traits()
- { return this->data_type::internal.internal.internal.internal.get_bucket_value_traits(); }
-
- bucket_traits &priv_bucket_traits()
- { return this->data_type::internal.internal.internal.internal.priv_bucket_traits(); }
+ static size_type suggested_lower_bucket_count(size_type n);
+ #endif //#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
- const bucket_traits &priv_bucket_traits() const
- { return this->data_type::internal.internal.internal.internal.priv_bucket_traits(); }
-
- value_traits &priv_value_traits()
- { return this->data_type::internal.internal.internal.internal.priv_value_traits(); }
-
- const value_traits &priv_value_traits() const
- { return this->data_type::internal.internal.internal.internal.priv_value_traits(); }
-
- const_value_traits_ptr priv_value_traits_ptr() const
- { return this->data_type::internal.internal.internal.internal.priv_value_traits_ptr(); }
-
- siterator priv_invalid_local_it() const
- { return this->data_type::internal.internal.internal.internal.priv_invalid_local_it(); }
-
- split_traits &priv_split_traits()
- { return this->data_type::internal.priv_split_traits(); }
-
- const split_traits &priv_split_traits() const
- { return this->data_type::internal.priv_split_traits(); }
- bucket_ptr priv_get_cache()
- { return this->data_type::internal.internal.priv_get_cache(); }
-
- void priv_initialize_cache()
- { return this->data_type::internal.internal.priv_initialize_cache(); }
-
- siterator priv_begin() const
- { return this->data_type::internal.internal.priv_begin(); }
-
- const value_equal &priv_equal() const
- { return this->data_type::internal.internal.priv_equal(); }
-
- value_equal &priv_equal()
- { return this->data_type::internal.internal.priv_equal(); }
-
- const hasher &priv_hasher() const
- { return this->data_type::internal.internal.internal.priv_hasher(); }
-
- hasher &priv_hasher()
- { return this->data_type::internal.internal.internal.priv_hasher(); }
-
- void priv_swap_cache(hashtable_impl &h)
- { this->data_type::internal.internal.priv_swap_cache(h.data_type::internal.internal); }
-
- node &priv_value_to_node(value_type &v)
- { return this->data_type::internal.internal.internal.internal.priv_value_to_node(v); }
-
- const node &priv_value_to_node(const value_type &v) const
- { return this->data_type::internal.internal.internal.internal.priv_value_to_node(v); }
-
- SizeType priv_get_cache_bucket_num()
- { return this->data_type::internal.internal.priv_get_cache_bucket_num(); }
-
- void priv_insertion_update_cache(SizeType n)
- { return this->data_type::internal.internal.priv_insertion_update_cache(n); }
-
- template<bool Boolean>
- std::size_t priv_stored_or_compute_hash(const value_type &v, detail::bool_<Boolean> b) const
- { return this->data_type::internal.internal.internal.priv_stored_or_compute_hash(v, b); }
-
- value_type &priv_value_from_slist_node(slist_node_ptr n)
- { return this->data_type::internal.internal.internal.internal.priv_value_from_slist_node(n); }
+ friend bool operator==(const hashtable_impl &x, const hashtable_impl &y)
+ {
+ //Taken from N3068
+ if(constant_time_size && x.size() != y.size()){
+ return false;
+ }
+ for (const_iterator ix = x.cbegin(), ex = x.cend(); ix != ex; ++ix){
+ std::pair<const_iterator, const_iterator> eqx(x.equal_range(key_of_value()(*ix))),
+ eqy(y.equal_range(key_of_value()(*ix)));
+ if (boost::intrusive::iterator_distance(eqx.first, eqx.second) !=
+ boost::intrusive::iterator_distance(eqy.first, eqy.second) ||
+ !(priv_algo_is_permutation)(eqx.first, eqx.second, eqy.first) ){
+ return false;
+ }
+ ix = eqx.second;
+ }
+ return true;
+ }
- const value_type &priv_value_from_slist_node(slist_node_ptr n) const
- { return this->data_type::internal.internal.internal.internal.priv_value_from_slist_node(n); }
+ friend bool operator!=(const hashtable_impl &x, const hashtable_impl &y)
+ { return !(x == y); }
- void priv_erasure_update_cache_range(SizeType first_bucket_num, SizeType last_bucket_num)
- { return this->data_type::internal.internal.priv_erasure_update_cache_range(first_bucket_num, last_bucket_num); }
+ friend bool operator<(const hashtable_impl &x, const hashtable_impl &y)
+ { return ::boost::intrusive::algo_lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); }
- void priv_erasure_update_cache()
- { return this->data_type::internal.internal.priv_erasure_update_cache(); }
+ friend bool operator>(const hashtable_impl &x, const hashtable_impl &y)
+ { return y < x; }
- static std::size_t priv_stored_hash(slist_node_ptr n, detail::true_ true_value)
- { return bucket_plus_vtraits<ValueTraits, BucketTraits>::priv_stored_hash(n, true_value); }
+ friend bool operator<=(const hashtable_impl &x, const hashtable_impl &y)
+ { return !(y < x); }
- static std::size_t priv_stored_hash(slist_node_ptr n, detail::false_ false_value)
- { return bucket_plus_vtraits<ValueTraits, BucketTraits>::priv_stored_hash(n, false_value); }
+ friend bool operator>=(const hashtable_impl &x, const hashtable_impl &y)
+ { return !(x < y); }
- std::size_t priv_hash_to_bucket(std::size_t hash_value) const
- {
- return detail::hash_to_bucket_split<power_2_buckets, incremental>
- (hash_value, this->priv_bucket_traits().bucket_count(), this->priv_split_traits().get_size());
- }
+ /// @cond
+ void check() const {}
+ private:
- template<class Disposer>
- void priv_erase_range_impl
- (size_type bucket_num, siterator before_first_it, siterator end_sit, Disposer disposer, size_type &num_erased)
+ template <class MaybeConstHashtableImpl, class Cloner, class Disposer>
+ void priv_clone_from(MaybeConstHashtableImpl &src, Cloner cloner, Disposer disposer)
{
- const bucket_ptr buckets = this->priv_bucket_pointer();
- bucket_type &b = buckets[bucket_num];
-
- if(before_first_it == b.before_begin() && end_sit == b.end()){
- this->priv_erase_range_impl(bucket_num, 1, disposer, num_erased);
- }
- else{
- num_erased = 0;
- siterator to_erase(before_first_it);
- ++to_erase;
- slist_node_ptr end_ptr = end_sit.pointed_node();
- while(to_erase != end_sit){
- group_functions_t::erase_from_group(end_ptr, detail::dcast_bucket_ptr<node>(to_erase.pointed_node()), optimize_multikey_t());
- to_erase = b.erase_after_and_dispose(before_first_it, make_node_disposer(disposer));
- ++num_erased;
+ this->clear_and_dispose(disposer);
+ if(!constant_time_size || !src.empty()){
+ const size_type src_bucket_count = src.bucket_count();
+ const size_type dst_bucket_count = this->bucket_count();
+ //Check power of two bucket array if the option is activated
+ BOOST_INTRUSIVE_INVARIANT_ASSERT
+ (!power_2_buckets || (0 == (src_bucket_count & (src_bucket_count-1))));
+ BOOST_INTRUSIVE_INVARIANT_ASSERT
+ (!power_2_buckets || (0 == (dst_bucket_count & (dst_bucket_count-1))));
+ //If src bucket count is bigger or equal, structural copy is possible
+ const bool structural_copy = (!incremental) && (src_bucket_count >= dst_bucket_count) &&
+ (power_2_buckets || (src_bucket_count % dst_bucket_count) == 0);
+ if(structural_copy){
+ this->priv_structural_clone_from(src, cloner, disposer);
+ }
+ else{
+ //Unlike previous cloning algorithm, this can throw
+ //if cloner, hasher or comparison functor throw
+ typedef typename detail::if_c< detail::is_const<MaybeConstHashtableImpl>::value
+ , typename MaybeConstHashtableImpl::const_iterator
+ , typename MaybeConstHashtableImpl::iterator
+ >::type clone_iterator;
+ clone_iterator b(src.begin()), e(src.end());
+ detail::exception_disposer<hashtable_impl, Disposer> rollback(*this, disposer);
+ for(; b != e; ++b){
+ //No need to check for duplicates and insert it in the first position
+ //as this is an unordered container. So use minimal insertion code
+ std::size_t const hash_to_store = this->priv_stored_or_compute_hash(*b, store_hash_t());;
+ size_type const bucket_number = this->priv_hash_to_bucket(hash_to_store);
+ typedef typename detail::if_c
+ <detail::is_const<MaybeConstHashtableImpl>::value, const_reference, reference>::type reference_type;
+ reference_type r = *b;
+ this->priv_clone_front_in_bucket<reference_type>(bucket_number, r, hash_to_store, cloner);
+ }
+ rollback.release();
}
- this->priv_size_traits().set_size(this->priv_size_traits().get_size()-num_erased);
}
}
- template<class Disposer>
- void priv_erase_range_impl
- (size_type first_bucket_num, size_type num_buckets, Disposer disposer, size_type &num_erased)
- {
- //Now fully clear the intermediate buckets
- const bucket_ptr buckets = this->priv_bucket_pointer();
- num_erased = 0;
- for(size_type i = first_bucket_num; i < (num_buckets + first_bucket_num); ++i){
- bucket_type &b = buckets[i];
- siterator b_begin(b.before_begin());
- siterator nxt(b_begin);
- ++nxt;
- siterator end_sit(b.end());
- while(nxt != end_sit){
- group_functions_t::init_group(detail::dcast_bucket_ptr<node>(nxt.pointed_node()), optimize_multikey_t());
- nxt = b.erase_after_and_dispose
- (b_begin, make_node_disposer(disposer));
- this->priv_size_traits().decrement();
- ++num_erased;
+ template<class ValueReference, class Cloner>
+ void priv_clone_front_in_bucket( size_type const bucket_number
+ , typename detail::identity<ValueReference>::type src_ref
+ , std::size_t const hash_to_store, Cloner cloner)
+ {
+ //No need to check for duplicates and insert it in the first position
+ //as this is an unordered container. So use minimal insertion code
+ //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_pointer()[bucket_number];
+ siterator const prev(cur_bucket.before_begin());
+ //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() &&
+ this->priv_equal()( key_of_value()(src_ref)
+ , key_of_value()(this->priv_value_from_slist_node((++siterator(prev)).pointed_node())));
+ this->priv_insert_equal_after_find(*cloner(src_ref), bucket_number, hash_to_store, prev, next_is_in_group);
+ }
+
+ template <class MaybeConstHashtableImpl, class Cloner, class Disposer>
+ void priv_structural_clone_from(MaybeConstHashtableImpl &src, Cloner cloner, Disposer disposer)
+ {
+ //First clone the first ones
+ const size_type src_bucket_count = src.bucket_count();
+ const size_type dst_bucket_count = this->bucket_count();
+ const bucket_ptr src_buckets = src.priv_bucket_pointer();
+ const bucket_ptr dst_buckets = this->priv_bucket_pointer();
+ size_type constructed = 0;
+ typedef node_cast_adaptor< detail::node_disposer<Disposer, value_traits, CircularSListAlgorithms>
+ , slist_node_ptr, node_ptr > NodeDisposer;
+ NodeDisposer node_disp(disposer, &this->priv_value_traits());
+
+ detail::exception_array_disposer<bucket_type, NodeDisposer, size_type>
+ rollback(dst_buckets[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 std::size_t new_n = detail::hash_to_bucket(constructed, dst_bucket_count, detail::bool_<power_2_buckets>());
+ bucket_type &src_b = src_buckets[constructed];
+ for( siterator b(src_b.begin()), e(src_b.end()); b != e; ++b){
+ slist_node_ptr const n(b.pointed_node());
+ typedef typename detail::if_c
+ <detail::is_const<MaybeConstHashtableImpl>::value, const_reference, reference>::type reference_type;
+ reference_type r = this->priv_value_from_slist_node(n);
+ this->priv_clone_front_in_bucket<reference_type>
+ (new_n, r, this->priv_stored_hash(n, store_hash_t()), cloner);
}
}
+ this->priv_hasher() = src.priv_hasher();
+ this->priv_equal() = src.priv_equal();
+ rollback.release();
+ this->priv_size_traits().set_size(src.priv_size_traits().get_size());
+ this->priv_split_traits().set_size(dst_bucket_count);
+ this->priv_insertion_update_cache(0u);
+ this->priv_erasure_update_cache();
}
- template<class Disposer>
- void priv_erase_range( siterator before_first_it, size_type first_bucket
- , siterator last_it, size_type last_bucket
- , Disposer disposer)
+ std::size_t priv_hash_to_bucket(std::size_t hash_value) const
{
- size_type num_erased;
- if (first_bucket == last_bucket){
- this->priv_erase_range_impl(first_bucket, before_first_it, last_it, disposer, num_erased);
- }
- else {
- bucket_type *b = (&this->priv_bucket_pointer()[0]);
- this->priv_erase_range_impl(first_bucket, before_first_it, b[first_bucket].end(), disposer, num_erased);
- if(size_type n = (last_bucket - first_bucket - 1))
- this->priv_erase_range_impl(first_bucket + 1, n, disposer, num_erased);
- this->priv_erase_range_impl(last_bucket, b[last_bucket].before_begin(), last_it, disposer, num_erased);
- }
+ return detail::hash_to_bucket_split<power_2_buckets, incremental>
+ (hash_value, this->priv_bucket_traits().bucket_count(), this->priv_split_traits().get_size());
}
- std::size_t priv_get_bucket_num(siterator it)
- { return this->priv_get_bucket_num_hash_dispatch(it, store_hash_t()); }
-
- std::size_t priv_get_bucket_num_hash_dispatch(siterator it, detail::true_) //store_hash
+ 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)
{
- return this->priv_hash_to_bucket
- (this->priv_stored_hash(it.pointed_node(), store_hash_t()));
+ //Now store hash if needed
+ node_ptr n = pointer_traits<node_ptr>::pointer_to(this->priv_value_to_node(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));
+ //Shortcut to optimize_multikey cases
+ group_functions_t::insert_in_group
+ ( next_is_in_group ? detail::dcast_bucket_ptr<node>((++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());
}
- std::size_t priv_get_bucket_num_hash_dispatch(siterator it, detail::false_) //NO store_hash
- { return this->data_type::internal.internal.internal.internal.priv_get_bucket_num_no_hash_store(it, optimize_multikey_t()); }
-
- static siterator priv_get_previous(bucket_type &b, siterator i)
- { return bucket_plus_vtraits_t::priv_get_previous(b, i, optimize_multikey_t()); }
-
- static siterator priv_get_last(bucket_type &b)
- { return bucket_plus_vtraits_t::priv_get_last(b, optimize_multikey_t()); }
-
- template<class Disposer>
- void priv_erase(const_iterator i, Disposer disposer, detail::true_)
- {
- slist_node_ptr elem(i.slist_it().pointed_node());
- slist_node_ptr f_bucket_end, l_bucket_end;
- if(store_hash){
- f_bucket_end = l_bucket_end =
- (this->priv_bucket_pointer()
- [this->priv_hash_to_bucket
- (this->priv_stored_hash(elem, store_hash_t()))
- ]).before_begin().pointed_node();
- }
- else{
- f_bucket_end = this->priv_bucket_pointer()->cend().pointed_node();
- l_bucket_end = f_bucket_end + this->priv_bucket_count() - 1;
- }
- node_ptr nxt_in_group;
- siterator prev = bucket_type::s_iterator_to
- (*group_functions_t::get_previous_and_next_in_group
- ( elem, nxt_in_group, f_bucket_end, l_bucket_end)
- );
- bucket_type::s_erase_after_and_dispose(prev, make_node_disposer(disposer));
- if(nxt_in_group)
- group_algorithms::unlink_after(nxt_in_group);
- if(safemode_or_autounlink)
- group_algorithms::init(detail::dcast_bucket_ptr<node>(elem));
+ template<class KeyType, class KeyHasher, class KeyEqual>
+ siterator priv_find //In case it is not found previt is bucket.before_begin()
+ ( 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 <class Disposer>
- void priv_erase(const_iterator i, Disposer disposer, detail::false_)
+ template<class KeyType, class KeyEqual>
+ bool priv_is_value_equal_to_key(const value_type &v, const std::size_t h, const KeyType &key, KeyEqual equal_func) const
{
- siterator to_erase(i.slist_it());
- bucket_type &b = this->priv_bucket_pointer()[this->priv_get_bucket_num(to_erase)];
- siterator prev(this->priv_get_previous(b, to_erase));
- b.erase_after_and_dispose(prev, make_node_disposer(disposer));
+ (void)h;
+ return (!compare_hash || this->priv_stored_or_compute_hash(v, store_hash_t()) == h) && equal_func(key, key_of_value()(v));
}
- template<class KeyType, class KeyHasher, class KeyValueEqual>
- siterator priv_find
- ( const KeyType &key, KeyHasher hash_func
- , KeyValueEqual equal_func, size_type &bucket_number, std::size_t &h, siterator &previt) const
+ //return previous iterator to the next equal range group in case
+ static siterator priv_last_in_group(const siterator &it_first_in_group)
{
- h = hash_func(key);
- return this->priv_find_with_hash(key, equal_func, bucket_number, h, previt);
+ return bucket_type::s_iterator_to
+ (*group_functions_t::get_last_in_group
+ (detail::dcast_bucket_ptr<node>(it_first_in_group.pointed_node()), optimize_multikey_t()));
}
- template<class KeyType, class KeyValueEqual>
- siterator priv_find_with_hash
- ( const KeyType &key, KeyValueEqual equal_func, size_type &bucket_number, const std::size_t h, siterator &previt) const
+ template<class KeyType, class KeyEqual>
+ 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
{
bucket_number = this->priv_hash_to_bucket(h);
bucket_type &b = this->priv_bucket_pointer()[bucket_number];
previt = b.before_begin();
- if(constant_time_size && this->empty()){
- return this->priv_invalid_local_it();
- }
-
siterator it = previt;
- ++it;
-
- while(it != b.end()){
- const value_type &v = this->priv_value_from_slist_node(it.pointed_node());
- if(compare_hash){
- std::size_t vh = this->priv_stored_or_compute_hash(v, store_hash_t());
- if(h == vh && equal_func(key, v)){
- return it;
- }
- }
- else if(equal_func(key, v)){
+ siterator const endit = b.end();
+
+ while(++it != endit){
+ if(this->priv_is_value_equal_to_key(this->priv_value_from_slist_node(it.pointed_node()), h, key, equal_func)){
return it;
}
- if(optimize_multikey){
- previt = bucket_type::s_iterator_to
- (*group_functions_t::get_last_in_group
- (detail::dcast_bucket_ptr<node>(it.pointed_node()), optimize_multikey_t()));
- it = previt;
- }
- else{
- previt = it;
- }
- ++it;
+ previt = it = (priv_last_in_group)(it);
}
previt = b.before_begin();
return this->priv_invalid_local_it();
}
- iterator priv_insert_equal_with_hash(reference value, std::size_t hash_value)
- {
- size_type bucket_num;
- siterator prev;
- siterator it = this->priv_find_with_hash
- (value, this->priv_equal(), bucket_num, hash_value, prev);
- return this->priv_insert_equal_find(value, bucket_num, hash_value, it);
- }
-
- iterator priv_insert_equal_find(reference value, size_type bucket_num, std::size_t hash_value, siterator it)
- {
- bucket_type &b = this->priv_bucket_pointer()[bucket_num];
- bool found_equal = it != this->priv_invalid_local_it();
- if(!found_equal){
- it = b.before_begin();
- }
- //Now store hash if needed
- node_ptr n = pointer_traits<node_ptr>::pointer_to(this->priv_value_to_node(value));
- node_functions_t::store_hash(n, hash_value, store_hash_t());
- //Checks for some modes
- if(safemode_or_autounlink)
- BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(node_algorithms::unique(n));
- //Shortcut for optimize_multikey cases
- if(optimize_multikey){
- node_ptr first_in_group = found_equal ?
- detail::dcast_bucket_ptr<node>(it.pointed_node()) : node_ptr();
- group_functions_t::insert_in_group(first_in_group, 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(b.insert_after(it, *n), &this->get_bucket_value_traits());
- }
-
- template<class KeyType, class KeyHasher, class KeyValueEqual>
- std::pair<siterator, siterator> priv_equal_range
+ template<class KeyType, class KeyHasher, class KeyEqual>
+ std::pair<siterator, siterator> priv_local_equal_range
( const KeyType &key
, KeyHasher hash_func
- , KeyValueEqual equal_func
- , size_type &bucket_number_first
- , size_type &bucket_number_second
+ , KeyEqual equal_func
+ , size_type &found_bucket
, size_type &cnt) const
{
- std::size_t h;
cnt = 0;
- siterator prev;
//Let's see if the element is present
+
+ siterator prev;
+ size_type n_bucket;
+ std::size_t h;
std::pair<siterator, siterator> to_return
- ( this->priv_find(key, hash_func, equal_func, bucket_number_first, h, prev)
+ ( this->priv_find(key, hash_func, equal_func, n_bucket, h, prev)
, this->priv_invalid_local_it());
- if(to_return.first == to_return.second){
- bucket_number_second = bucket_number_first;
- return to_return;
- }
- {
+
+ if(to_return.first != to_return.second){
+ 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_pointer()[bucket_number_first];
+ bucket_type &b = this->priv_bucket_pointer()[n_bucket];
siterator it = to_return.first;
+ ++cnt; //At least one is found
if(optimize_multikey){
- to_return.second = bucket_type::s_iterator_to
- (*node_traits::get_next(group_functions_t::get_last_in_group
- (detail::dcast_bucket_ptr<node>(it.pointed_node()), optimize_multikey_t())));
-
- cnt = 0;
- for(; it != to_return.second; ++it){ ++cnt; }
- if(to_return.second != b.end()){
- bucket_number_second = bucket_number_first;
- return to_return;
- }
+ to_return.second = ++(priv_last_in_group)(it);
+ cnt += boost::intrusive::iterator_distance(++it, to_return.second);
}
else{
- ++cnt;
- ++it;
- while(it != b.end()){
- const value_type &v = this->priv_value_from_slist_node(it.pointed_node());
- if(compare_hash){
- std::size_t hv = this->priv_stored_or_compute_hash(v, store_hash_t());
- if(hv != h || !equal_func(key, v)){
- to_return.second = it;
- bucket_number_second = bucket_number_first;
- return to_return;
- }
- }
- else if(!equal_func(key, v)){
- to_return.second = it;
- bucket_number_second = bucket_number_first;
- return to_return;
- }
- ++it;
+ 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)){
++cnt;
}
+ to_return.second = it;
}
}
+ return to_return;
+ }
- //If we reached the end, find the first, non-empty bucket
- for(bucket_number_second = bucket_number_first+1
- ; bucket_number_second != this->priv_bucket_count()
- ; ++bucket_number_second){
- bucket_type &b = this->priv_bucket_pointer()[bucket_number_second];
- if(!b.empty()){
- to_return.second = b.begin();
- return to_return;
+ template<class KeyType, class KeyHasher, class KeyEqual>
+ std::pair<siterator, siterator> priv_equal_range
+ ( const KeyType &key
+ , KeyHasher hash_func
+ , KeyEqual equal_func) const
+ {
+ size_type n_bucket;
+ size_type cnt;
+
+ //Let's see if the element is present
+ std::pair<siterator, siterator> to_return
+ (this->priv_local_equal_range(key, hash_func, equal_func, n_bucket, cnt));
+ //If not, find the next element as ".second" if ".second" local iterator
+ //is not pointing to an element.
+ bucket_ptr const bp = this->priv_bucket_pointer();
+ if(to_return.first != to_return.second &&
+ to_return.second == bp[n_bucket].end()){
+ to_return.second = this->priv_invalid_local_it();
+ ++n_bucket;
+ for( const size_type max_bucket = this->priv_bucket_count()
+ ; n_bucket != max_bucket
+ ; ++n_bucket){
+ bucket_type &b = bp[n_bucket];
+ if(!b.empty()){
+ to_return.second = b.begin();
+ break;
+ }
}
}
-
- //Otherwise, return the end node
- to_return.second = this->priv_invalid_local_it();
return to_return;
}
+
+ std::size_t priv_get_bucket_num(siterator it)
+ { return this->priv_get_bucket_num_hash_dispatch(it, store_hash_t()); }
+
+ std::size_t priv_get_bucket_num_hash_dispatch(siterator it, detail::true_) //store_hash
+ {
+ return this->priv_hash_to_bucket
+ (this->priv_stored_hash(it.pointed_node(), store_hash_t()));
+ }
+
+ std::size_t priv_get_bucket_num_hash_dispatch(siterator it, detail::false_) //NO store_hash
+ { return this->priv_get_bucket_num_no_hash_store(it, optimize_multikey_t()); }
+
+ static siterator priv_get_previous(bucket_type &b, siterator i)
+ { return bucket_plus_vtraits_t::priv_get_previous(b, i, optimize_multikey_t()); }
+
/// @endcond
};
@@ -3232,16 +3413,17 @@ struct make_hashtable
typedef hashtable_impl
< value_traits
+ , typename packed_options::key_of_value
, typename packed_options::hash
, typename packed_options::equal
- , typename packed_options::size_type
, bucket_traits
+ , typename packed_options::size_type
, (std::size_t(false)*hash_bool_flags::unique_keys_pos)
- | (std::size_t(packed_options::constant_time_size)*hash_bool_flags::constant_time_size_pos)
- | (std::size_t(packed_options::power_2_buckets)*hash_bool_flags::power_2_buckets_pos)
- | (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::constant_time_size)*hash_bool_flags::constant_time_size_pos)
+ |(std::size_t(packed_options::power_2_buckets)*hash_bool_flags::power_2_buckets_pos)
+ |(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)
> implementation_defined;
/// @endcond
@@ -3299,6 +3481,14 @@ class hashtable
hashtable& operator=(BOOST_RV_REF(hashtable) x)
{ return static_cast<hashtable&>(this->Base::operator=(BOOST_MOVE_BASE(Base, x))); }
+
+ template <class Cloner, class Disposer>
+ void clone_from(const hashtable &src, Cloner cloner, Disposer disposer)
+ { Base::clone_from(src, cloner, disposer); }
+
+ template <class Cloner, class Disposer>
+ void clone_from(BOOST_RV_REF(hashtable) src, Cloner cloner, Disposer disposer)
+ { Base::clone_from(BOOST_MOVE_BASE(Base, src), cloner, disposer); }
};
#endif