diff --git a/doc/intrusive.qbk b/doc/intrusive.qbk index 3a84c3f..bf8a604 100644 --- a/doc/intrusive.qbk +++ b/doc/intrusive.qbk @@ -528,7 +528,25 @@ chapters: small for user classes (usually the size of two pointers). Many operations have constant time complexity. -* [*set/multiset]: A `std::set/std::multiset` like intrusive associative containers. +* [*set/multiset/rbtree]: A `std::set/std::multiset` like intrusive associative containers + based on red-black trees. + The size overhead is moderate for user classes (usually the size of three pointers). + Many operations have logarithmic time complexity. + +* [*avl_set/avl_multiset/avltree]: A `std::set/std::multiset` like intrusive associative + containers based on AVL trees. + The size overhead is moderate for user classes (usually the size of three pointers). + Many operations have logarithmic time complexity. + +* [*splay_set/splay_multiset/splaytree]: A `std::set/std::multiset` like intrusive associative + containers based on splay trees. Splay trees have no constant operations, but they + have some interesting caching properties. + The size overhead is moderate for user classes (usually the size of three pointers). + Many operations have logarithmic time complexity. + +* [*sg_set/sg_multiset/sgtree]: A `std::set/std::multiset` like intrusive associative + containers based on scapegoat trees. Scapegoat can be configured with the desired + balance factor to achieve the desised rebalancing frequency/search time compromise. The size overhead is moderate for user classes (usually the size of three pointers). Many operations have logarithmic time complexity. @@ -539,7 +557,7 @@ chapters: The size overhead is moderate for user classes (an average of two pointers per element). Many operations have an amortized constant time complexity. -Each of these intrusive containers can be configured with constant or linear time +Most of these intrusive containers can be configured with constant or linear time size: * [*Linear time size]: The intrusive container doesn't hold a size member that it's @@ -753,7 +771,7 @@ linear time complexity, even some that usually are constant time, like only provides forward iterators. For most cases, a doubly linked list is preferrable because it offers more -constant-time functions with a slightly bigger overhead. +constant-time functions with a slightly bigger size overhead. However, for some applications like constructing more elaborated containers, singly linked lists are essential because of their low size overhead. @@ -784,7 +802,8 @@ Like the rest of [*Boost.Intrusive] containers, it [classref boost::intrusive::slist slist]-compatible. [classref boost::intrusive::slist_base_hook slist_base_hook] and -[classref boost::intrusive::slist_member_hook slist_member_hook] receive the same options explained in +[classref boost::intrusive::slist_member_hook slist_member_hook] +receive the same options explained in the section [link intrusive.usage How to use Boost.Intrusive]: * [*`tag`] (for base hooks only): This argument serves as a tag, @@ -807,7 +826,7 @@ the section [link intrusive.usage How to use Boost.Intrusive]: template class slist; -[classref boost::intrusive::slist slist] receives the same options explained in +[classref boost::intrusive::slist slist] receives the options explained in the section [link intrusive.usage How to use Boost.Intrusive]: * [*`base_hook`] / [*`member_hook`] / @@ -819,7 +838,23 @@ the section [link intrusive.usage How to use Boost.Intrusive]: Default: `constant_time_size` * [*`size_type`]: To specify the type that will be used to store the size - of the container. Default: `size_type` + of the container. Default: `size_type`. + +[classref boost::intrusive::slist slist] can receive additional options: + +* [*`linear`]: the singly linked list is implemented as a + null-terminated list instead of a circular list. This allows `O(1)` swap, + but losses some operations like `container_from_end_iterator`. +* [*`cache_last`]: the singly linked also stores a pointer to the + last element of the singly linked list. This allows `O(1)` swap, + `splice_after(iterator, slist &)` and makes the list offer new functions + like `push_back(reference)` and `back()`. Logically, the size an empty list is + increased in `sizeof(void_pointer)` and the the cached last node pointer must + be updated in every operation, and that might incur in a slight performance impact. + +`auto_unlink` hooks are not usable if `linear` and/or `cache_last` options are +used. If `auto_unlink` hooks are used and those options are specified, a static +assertion will be raised. [endsect] @@ -1151,6 +1186,11 @@ Apart from them, these hooks offer additional options: rehashing is frequent or hashing the value is a slow operation. Default: `store_hash`. +* [*`optimize_multikey`]: This option reserves additional space in + the hook that will be used to group equal elements in unordered multisets, + improving significantly the performance when many equal values are inserted + in these containers. Default: `optimize_multikey`. + [endsect] [section:unordered_set_unordered_multiset_containers unordered_set and unordered_multiset containers] @@ -1210,7 +1250,7 @@ receive the same options explained in the section * [*`size_type`]: To specify the type that will be used to store the size of the container. Default: `size_type` -And they also can receive two additional options: +And they also can receive additional options: * [*`equal`]: Equality function for the objects to be inserted in containers. Default: `equal< std::equal_to >` @@ -1228,7 +1268,14 @@ And they also can receive two additional options: modulo operations and for some applications modulo operations can impose a considerable overhead. In debug mode an assertion will be raised if the user provides a bucket length that is not power of two. - Default: `constant_time_size`. + Default: `power_2_buckets`. + +* [*`cache_begin`]: Due to its internal structure, finding the first + element of an unordered container (`begin()` operation) is + amortized constant-time. It's possible to speed up `begin()` and other operations + related to it (like `clear()`) if the container caches internally the position + of the first element. This imposes the overhead of one pointer to the size + of the container. Default: `cache_begin`. [endsect] @@ -1301,13 +1348,13 @@ more frequently accessed than others, splay trees perform faster searches than e balanced binary trees (such as red-black trees). The caching effect offered by splay trees comes with a cost: the tree must be -rebalanced when a element is searched. This disallows const versions of search +rebalanced when an element is searched. This disallows const versions of search functions like `find()`, `lower_bound()`, `upper_bound()`, `equal_range()`, `count()`... Because of this, splay-tree based associative containers are not drop-in replacements of [classref boost::intrusive::set set]/ -[classref boost::intrusive::splay_set splay_set]. +[classref boost::intrusive::multiset multiset]. Apart from this, if element searches are randomized, the tree will be rebalanced without taking advantage of the cache effect, so splay trees can offer worse @@ -1565,22 +1612,22 @@ time, scapegoat trees have no additional per-node overhead compared to a regular search tree. A binary search tree is said to be weight balanced if half the nodes are on the left -of the root, and half on the right. An α-height-balanced tree is defined with defined +of the root, and half on the right. An a-height-balanced tree is defined with defined with the following equation: -[*['height(tree) <= log1/α(tree.size())]] +[*['height(tree) <= log1/a(tree.size())]] -* [*['α == 1]]: A tree forming a linked list is considered balanced. -* [*['α == 0.5]]: Only a perfectly balanced binary is considered balanced. +* [*['a == 1]]: A tree forming a linked list is considered balanced. +* [*['a == 0.5]]: Only a perfectly balanced binary is considered balanced. -Scapegoat trees are loosely ['α-height-balanced] so: +Scapegoat trees are loosely ['a-height-balanced] so: -[*['height(tree) <= log1/α(tree.size()) + 1]] +[*['height(tree) <= log1/a(tree.size()) + 1]] -Scapegoat trees support any α between 0.5 and 1. If α is higher, the tree is rebalanced +Scapegoat trees support any a between 0.5 and 1. If a is higher, the tree is rebalanced less often, obtaining quicker insertions but slower searches. Lower -α values improve search times. Scapegoat-trees implemented in [*Boost.Intrusive] offer the possibility of -[*changing α at run-time] taking advantage of the flexibility of scapegoat trees. +a values improve search times. Scapegoat-trees implemented in [*Boost.Intrusive] offer the possibility of +[*changing a at run-time] taking advantage of the flexibility of scapegoat trees. For more information on scapegoat trees see [@http://en.wikipedia.org/wiki/Scapegoat_tree Wikipedia entry]. Scapegoat trees also have downsides: @@ -1591,7 +1638,7 @@ Scapegoat trees also have downsides: tree is also considerably increased. * The operations needed to determine if the tree is unbalanced require floating-point - operations like ['log1/α]. If the system has no floating point operations (like some + operations like ['log1/a]. If the system has no floating point operations (like some embedded systems), scapegoat tree operations might become slow. [*Boost.Intrusive] offers 3 containers based on scapegoat trees: @@ -1689,9 +1736,9 @@ And they also can receive additional options: * [*`floating_point`]: When this option is deactivated, the scapegoat tree loses the ability to change - the balance factor α at run-time, but the size of an empty container is reduced + the balance factor a at run-time, but the size of an empty container is reduced and no floating point operations are performed, normally increasing container - performance. The fixed α factor that is used when this option is activated + performance. The fixed a factor that is used when this option is activated is ['1/sqrt(2) ~ 0,70711]. Default: `floating_point` [endsect] @@ -2523,6 +2570,63 @@ For a complete list of functions see [endsect] +[/ +/ +/[section:sgtree_algorithms Intrusive sg tree algorithms] +/ +/ +/[classref boost::intrusive::sgtree_algorithms sgtree_algorithms] have the same +/interface as [classref boost::intrusive::rbtree_algorithms rbtree_algorithms]. +/ +/[c++] +/ +/ template +/ struct sgtree_algorithms; +/ +/[classref boost::intrusive::sgtree_algorithms sgtree_algorithms] +/is configured with a NodeTraits class, which encapsulates +/the information about the node to be manipulated. NodeTraits must support the +/following interface: +/ +/[*Typedefs]: +/ +/* `node`: The type of the node that forms the circular sgtree +/ +/* `node_ptr`: The type of a pointer to a node (usually node*) +/ +/* `const_node_ptr`: The type of a pointer to a const node (usually const node*) +/ +/[*Static functions]: +/ +/* `static node_ptr get_parent(const_node_ptr n);`: +/ Returns a pointer to the parent node stored in "n". +/ +/* `static void set_parent(node_ptr n, node_ptr p);`: +/ Sets the pointer to the parent node stored in "n" to "p". +/ +/* `static node_ptr get_left(const_node_ptr n);`: +/ Returns a pointer to the left node stored in "n". +/ +/* `static void set_left(node_ptr n, node_ptr l);`: +/ Sets the pointer to the left node stored in "n" to "l". +/ +/* `static node_ptr get_right(const_node_ptr n);`: +/ Returns a pointer to the right node stored in "n". +/ +/* `static void set_right(node_ptr n, node_ptr r);`: +/ Sets the pointer to the right node stored in "n" to "r". +/ +/Once we have a node traits configuration we can use [*Boost.Intrusive] algorithms +/with our nodes: +/ +/[import ../example/doc_sgtree_algorithms.cpp] +/[doc_sgtree_algorithms_code] +/ +/For a complete list of functions see +/[classref boost::intrusive::sgtree_algorithms sgtree_algorithms reference]. +/ +/[endsect] +/] [endsect] [section:value_traits Containers with custom ValueTraits] @@ -3104,7 +3208,7 @@ lists need to perform the same operations. These are the results: [table Reverse times for Visual C++ 7.1 / Windows XP - [[Container] [Time in us/iteration (small object / big object)] [Normalized time (small object / big object) (small object / big object)]] + [[Container] [Time in us/iteration (small object / big object)] [Normalized time (small object / big object)]] [[`normal_link` intrusive list] [2656 / 10625] [1 / 1.83]] [[`safe_link` intrusive list] [2812 / 10937] [1.05 / 1.89]] [[`auto_unlink` intrusive list] [2710 / 10781] [1.02 / 1.86]] @@ -3310,18 +3414,18 @@ all the objects to be inserted in intrusive containers in containers like `std:: [endsect] -[section:disabling_exceptions Disabling exceptions support] +[section:release_notes Release Notes] -[*Boost.Intrusive] might be useful in environments where exceptions are not available -or recommendable (like embedded or real-time systems). [*Boost.Intrusive] uses the -global Boost mechanism to disable exception handling, so that if the compiler -configuration disables exceptions, `BOOST_NO_EXCEPTIONS` is defined and exception -handling is disabled. +[section:release_notes_boost_1_36_00 Boost 1.36 Release] -This mechanism is a global mechanism to disable exceptions. If for any reason, -the user wants to disable exception handling [*only] in [*Boost.Intrusive], -`BOOST_INTRUSIVE_DISABLE_EXCEPTION_HANDLING` can be defined to disable -exception handling in the library. +* Added `linear<>` and `cache_last<>` options to singly linked lists. +* Added `optimize_multikey<>` option to unordered container hooks. +* Optimized unordered containers when `store_hash` option is used in the hook. +* Implementation changed to be exception agnostic so that it can be used + in environments without exceptions. +* Added `container_from_iterator` function to tree-based containers. + +[endsect] [endsect] @@ -3331,13 +3435,13 @@ exception handling in the library. * Visual 7.1/WinXP * Visual 8.0/WinXP +* Visual 9.0/WinXP * GCC 4.1.1/MinGW * GCC 3.4.4/Cygwin * Intel 9.1/WinXP * GCC 4.1.2/Linux * GCC 3.4.3/Solaris 11 * GCC 4.0/Mac Os 10.4.1 -* SunCC 5.8/Solaris 11 [endsect] diff --git a/include/boost/intrusive/avl_set.hpp b/include/boost/intrusive/avl_set.hpp index 8ff22a4..8429f13 100644 --- a/include/boost/intrusive/avl_set.hpp +++ b/include/boost/intrusive/avl_set.hpp @@ -240,7 +240,7 @@ class avl_set_impl //! Precondition: end_iterator must be a valid end const_iterator //! of avl_set. //! - //! Effects: Returns a const reference to the avl_set associated to the end iterator + //! Effects: Returns a const reference to the set associated to the end iterator //! //! Throws: Nothing. //! @@ -252,6 +252,34 @@ class avl_set_impl , &avl_set_impl::tree_); } + //! Precondition: it must be a valid iterator of set. + //! + //! Effects: Returns a reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static avl_set_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &avl_set_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of set. + //! + //! Effects: Returns a const reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const avl_set_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &avl_set_impl::tree_); + } + //! Effects: Returns the key_compare object used by the avl_set. //! //! Complexity: Constant. @@ -1085,6 +1113,12 @@ class avl_set static const avl_set &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static avl_set &container_from_iterator(iterator end_iterator) + { return static_cast(Base::container_from_iterator(end_iterator)); } + + static const avl_set &container_from_iterator(const_iterator end_iterator) + { return static_cast(Base::container_from_iterator(end_iterator)); } }; #endif @@ -1317,6 +1351,34 @@ class avl_multiset_impl , &avl_multiset_impl::tree_); } + //! Precondition: it must be a valid iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static avl_multiset_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &avl_multiset_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const avl_multiset_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &avl_multiset_impl::tree_); + } + //! Effects: Returns the key_compare object used by the avl_multiset. //! //! Complexity: Constant. @@ -2057,6 +2119,12 @@ class avl_multiset static const avl_multiset &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static avl_multiset &container_from_iterator(iterator end_iterator) + { return static_cast(Base::container_from_iterator(end_iterator)); } + + static const avl_multiset &container_from_iterator(const_iterator end_iterator) + { return static_cast(Base::container_from_iterator(end_iterator)); } }; #endif diff --git a/include/boost/intrusive/avltree.hpp b/include/boost/intrusive/avltree.hpp index 86a2957..afd6bd4 100644 --- a/include/boost/intrusive/avltree.hpp +++ b/include/boost/intrusive/avltree.hpp @@ -387,6 +387,28 @@ class avltree_impl static const avltree_impl &container_from_end_iterator(const_iterator end_iterator) { return priv_container_from_end_iterator(end_iterator); } + //! Precondition: it must be a valid iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static avltree_impl &container_from_iterator(iterator it) + { return priv_container_from_iterator(it); } + + //! Precondition: it must be a valid end const_iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const avltree_impl &container_from_iterator(const_iterator it) + { return priv_container_from_iterator(it); } + //! Effects: Returns the value_compare object used by the tree. //! //! Complexity: Constant. @@ -1233,6 +1255,9 @@ class avltree_impl avltree_impl *avl = detail::parent_from_member(d, &avltree_impl::data_); return *avl; } + + static avltree_impl &priv_container_from_iterator(const const_iterator &it) + { return priv_container_from_end_iterator(it.end_iterator_from_it()); } }; #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED @@ -1426,6 +1451,12 @@ class avltree static const avltree &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static avltree &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const avltree &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif diff --git a/include/boost/intrusive/avltree_algorithms.hpp b/include/boost/intrusive/avltree_algorithms.hpp index 94d046f..145e0fb 100644 --- a/include/boost/intrusive/avltree_algorithms.hpp +++ b/include/boost/intrusive/avltree_algorithms.hpp @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -641,6 +640,16 @@ class avltree_algorithms rebalance_after_insertion(header, new_value); } + //! Requires: "n" must be a node inserted in a tree. + //! + //! Effects: Returns a pointer to the header node of the tree. + //! + //! Complexity: Logarithmic. + //! + //! Throws: Nothing. + static node_ptr get_header(node_ptr n) + { return tree_algorithms::get_header(n); } + /// @cond private: diff --git a/include/boost/intrusive/circular_list_algorithms.hpp b/include/boost/intrusive/circular_list_algorithms.hpp index 97438bf..537b151 100644 --- a/include/boost/intrusive/circular_list_algorithms.hpp +++ b/include/boost/intrusive/circular_list_algorithms.hpp @@ -62,8 +62,8 @@ class circular_list_algorithms //! Throws: Nothing. static void init(node_ptr this_node) { - NodeTraits::set_next(this_node, 0); - NodeTraits::set_previous(this_node, 0); + NodeTraits::set_next(this_node, node_ptr(0)); + NodeTraits::set_previous(this_node, node_ptr(0)); } //! Effects: Returns true is "this_node" is in a non-used state diff --git a/include/boost/intrusive/circular_slist_algorithms.hpp b/include/boost/intrusive/circular_slist_algorithms.hpp index 73e4590..2a257e4 100644 --- a/include/boost/intrusive/circular_slist_algorithms.hpp +++ b/include/boost/intrusive/circular_slist_algorithms.hpp @@ -305,12 +305,12 @@ class circular_slist_algorithms static node_ptr move_backwards(node_ptr p, std::size_t n) { //Null shift, nothing to do - if(!n) return 0; + if(!n) return node_ptr(0); node_ptr first = NodeTraits::get_next(p); //count() == 1 or 2, nothing to do if(NodeTraits::get_next(first) == p) - return 0; + return node_ptr(0); bool end_found = false; node_ptr new_last(0); @@ -326,7 +326,7 @@ class circular_slist_algorithms //Shortcut the shift with the modulo of the size of the list n %= i; if(!n) - return 0; + return node_ptr(0); i = 0; //Unlink p and continue the new first node search first = NodeTraits::get_next(p); @@ -357,11 +357,11 @@ class circular_slist_algorithms static node_ptr move_forward(node_ptr p, std::size_t n) { //Null shift, nothing to do - if(!n) return 0; + if(!n) return node_ptr(0); node_ptr first = node_traits::get_next(p); //count() == 1 or 2, nothing to do - if(node_traits::get_next(first) == p) return 0; + if(node_traits::get_next(first) == p) return node_ptr(0); //Iterate until p is found to know where the current last node is. //If the shift count is less than the size of the list, we can also obtain @@ -380,7 +380,7 @@ class circular_slist_algorithms //Shortcut the shift with the modulo of the size of the list std::size_t new_before_last_pos = (distance - (n % distance))% distance; //If the shift is a multiple of the size there is nothing to do - if(!new_before_last_pos) return 0; + if(!new_before_last_pos) return node_ptr(0); for( new_last = p ; new_before_last_pos-- diff --git a/include/boost/intrusive/detail/avltree_node.hpp b/include/boost/intrusive/detail/avltree_node.hpp index f60f7db..92458a4 100644 --- a/include/boost/intrusive/detail/avltree_node.hpp +++ b/include/boost/intrusive/detail/avltree_node.hpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include namespace boost { @@ -111,7 +111,7 @@ struct compact_avltree_node_traits_impl ::type const_node_ptr; typedef typename node::balance balance; - typedef pointer_plus_2_bits ptr_bit; + typedef pointer_plus_bits ptr_bit; static node_ptr get_parent(const_node_ptr n) { return ptr_bit::get_pointer(n->parent_); } @@ -148,7 +148,7 @@ struct compact_avltree_node_traits_impl }; //Dispatches the implementation based on the boolean -template +template struct avltree_node_traits_dispatch : public default_avltree_node_traits_impl {}; @@ -164,10 +164,10 @@ struct avltree_node_traits : public avltree_node_traits_dispatch < VoidPointer , OptimizeSize && - has_pointer_plus_2_bits + max_pointer_plus_bits < VoidPointer , detail::alignment_of >::value - >::value + >::value >= 2u > {}; diff --git a/include/boost/intrusive/detail/common_slist_algorithms.hpp b/include/boost/intrusive/detail/common_slist_algorithms.hpp index 4f4a8f1..cc37a74 100644 --- a/include/boost/intrusive/detail/common_slist_algorithms.hpp +++ b/include/boost/intrusive/detail/common_slist_algorithms.hpp @@ -47,7 +47,7 @@ class common_slist_algorithms { NodeTraits::set_next(this_node, this_node); } static void init(node_ptr this_node) - { NodeTraits::set_next(this_node, 0); } + { NodeTraits::set_next(this_node, node_ptr(0)); } static bool unique(const_node_ptr this_node) { diff --git a/include/boost/intrusive/detail/ebo_functor_holder.hpp b/include/boost/intrusive/detail/ebo_functor_holder.hpp index 69c50cd..696944f 100644 --- a/include/boost/intrusive/detail/ebo_functor_holder.hpp +++ b/include/boost/intrusive/detail/ebo_functor_holder.hpp @@ -23,8 +23,15 @@ template class ebo_functor_holder_impl { public: - ebo_functor_holder_impl(){} - ebo_functor_holder_impl(const T& t):t(t){} + ebo_functor_holder_impl() + {} + ebo_functor_holder_impl(const T& t) + : t(t) + {} + template + ebo_functor_holder_impl(const Arg1& arg1, const Arg2& arg2) + : t(arg1, arg2) + {} T& get(){return t;} const T& get()const{return t;} @@ -38,8 +45,15 @@ class ebo_functor_holder_impl : public T { public: - ebo_functor_holder_impl(){} - ebo_functor_holder_impl(const T& t):T(t){} + ebo_functor_holder_impl() + {} + ebo_functor_holder_impl(const T& t) + : T(t) + {} + template + ebo_functor_holder_impl(const Arg1& arg1, const Arg2& arg2) + : T(arg1, arg2) + {} T& get(){return *this;} const T& get()const{return *this;} @@ -54,7 +68,14 @@ class ebo_functor_holder public: ebo_functor_holder(){} - ebo_functor_holder(const T& t):super(t){} + ebo_functor_holder(const T& t) + : super(t) + {} + + template + ebo_functor_holder(const Arg1& arg1, const Arg2& arg2) + : super(arg1, arg2) + {} ebo_functor_holder& operator=(const ebo_functor_holder& x) { diff --git a/include/boost/intrusive/detail/hashtable_node.hpp b/include/boost/intrusive/detail/hashtable_node.hpp index 0a8e85d..66bec59 100644 --- a/include/boost/intrusive/detail/hashtable_node.hpp +++ b/include/boost/intrusive/detail/hashtable_node.hpp @@ -50,6 +50,7 @@ const std::size_t prime_list_holder::prime_list_size template struct bucket_impl : public Slist { + typedef Slist slist_type; bucket_impl() {} @@ -69,28 +70,6 @@ struct bucket_impl : public Slist //Slist::clear(); return *this; } - - static typename Slist::difference_type get_bucket_num - ( typename Slist::const_iterator it - , const bucket_impl &first_bucket - , const bucket_impl &last_bucket) - { - typename Slist::const_iterator - first(first_bucket.cend()), last(last_bucket.cend()); - - //The end node is embedded in the singly linked list: - //iterate until we reach it. - while(!(first.pointed_node() <= it.pointed_node() && - it.pointed_node() <= last.pointed_node())){ - ++it; - } - //Now get the bucket_impl from the iterator - const bucket_impl &b = static_cast - (Slist::container_from_end_iterator(it)); - - //Now just calculate the index b has in the bucket array - return &b - &first_bucket; - } }; template @@ -133,6 +112,9 @@ class hashtable_iterator < typename Container::pointer, const Container>::type const_cont_ptr; typedef typename Container::size_type size_type; + static typename Container::node_ptr downcast_bucket(typename bucket_type::node_ptr p) + { return typename Container::node_ptr(&static_cast(*p)); } + public: typedef typename detail::add_const_if_c ::type value_type; @@ -172,7 +154,7 @@ class hashtable_iterator { return *this->operator ->(); } value_type* operator->() const - { return detail::get_pointer(this->get_real_value_traits()->to_value_ptr(slist_it_.pointed_node())); } + { return detail::get_pointer(this->get_real_value_traits()->to_value_ptr(downcast_bucket(slist_it_.pointed_node()))); } const Container *get_container() const { return detail::get_pointer(cont_); } @@ -186,17 +168,19 @@ class hashtable_iterator const Container *cont = detail::get_pointer(cont_); bucket_type* buckets = detail::get_pointer(cont->bucket_pointer()); size_type buckets_len = cont->bucket_count(); - const_siterator first(buckets[0].cend()); - const_siterator last (buckets[buckets_len].cend()); ++slist_it_; - if(first.pointed_node() <= slist_it_.pointed_node() && - slist_it_.pointed_node()<= last.pointed_node() ){ - size_type n_bucket = (size_type) - bucket_type::get_bucket_num(slist_it_, buckets[0], buckets[buckets_len]); + if(buckets[0].cend().pointed_node() <= slist_it_.pointed_node() && + slist_it_.pointed_node()<= buckets[buckets_len].cend().pointed_node() ){ + //Now get the bucket_impl from the iterator + const bucket_type &b = static_cast + (bucket_type::slist_type::container_from_end_iterator(slist_it_)); + + //Now just calculate the index b has in the bucket array + size_type n_bucket = static_cast(&b - &buckets[0]); do{ if (++n_bucket == buckets_len){ - slist_it_ = buckets->end(); + slist_it_ = (&buckets[0] + buckets_len)->end(); break; } slist_it_ = buckets[n_bucket].begin(); diff --git a/include/boost/intrusive/detail/list_node.hpp b/include/boost/intrusive/detail/list_node.hpp index ec48a6d..a4d4505 100644 --- a/include/boost/intrusive/detail/list_node.hpp +++ b/include/boost/intrusive/detail/list_node.hpp @@ -32,7 +32,8 @@ struct list_node { typedef typename boost::pointer_to_other ::type node_ptr; - node_ptr prev_, next_; + node_ptr next_; + node_ptr prev_; }; template @@ -85,7 +86,7 @@ class list_iterator typedef value_type * pointer; list_iterator() - : members_ (0, 0) + : members_ (node_ptr(0), 0) {} explicit list_iterator(node_ptr node, const Container *cont_ptr) diff --git a/include/boost/intrusive/detail/mpl.hpp b/include/boost/intrusive/detail/mpl.hpp index 5dcd75c..ed68350 100644 --- a/include/boost/intrusive/detail/mpl.hpp +++ b/include/boost/intrusive/detail/mpl.hpp @@ -13,6 +13,8 @@ #ifndef BOOST_INTRUSIVE_DETAIL_MPL_HPP #define BOOST_INTRUSIVE_DETAIL_MPL_HPP +#include + namespace boost { namespace intrusive { namespace detail { @@ -290,6 +292,24 @@ class is_empty_class static const bool value = sizeof(empty_helper_t1) == sizeof(empty_helper_t2); }; +template +struct ls_zeros +{ + static const std::size_t value = (S & std::size_t(1)) ? 0 : (1 + ls_zeros<(S>>1u)>::value); +}; + +template<> +struct ls_zeros<0> +{ + static const std::size_t value = 0; +}; + +template<> +struct ls_zeros<1> +{ + static const std::size_t value = 0; +}; + } //namespace detail } //namespace intrusive } //namespace boost diff --git a/include/boost/intrusive/detail/no_exceptions_support.hpp b/include/boost/intrusive/detail/no_exceptions_support.hpp deleted file mode 100644 index 4344684..0000000 --- a/include/boost/intrusive/detail/no_exceptions_support.hpp +++ /dev/null @@ -1,28 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// (C) Copyright Ion Gaztanaga 2007 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// See http://www.boost.org/libs/intrusive for documentation. -// -///////////////////////////////////////////////////////////////////////////// - -#ifndef BOOST_INTRUSIVE_NO_EXCEPTION_SUPPORT_HPP - -#if !(defined BOOST_INTRUSIVE_DISABLE_EXCEPTION_HANDLING) -# include -# define BOOST_INTRUSIVE_TRY BOOST_TRY -# define BOOST_INTRUSIVE_CATCH(x) BOOST_CATCH(x) -# define BOOST_INTRUSIVE_RETHROW BOOST_RETHROW -# define BOOST_INTRUSIVE_CATCH_END BOOST_CATCH_END -#else -# define BOOST_INTRUSIVE_TRY { if (true) -# define BOOST_INTRUSIVE_CATCH(x) else if (false) -# define BOOST_INTRUSIVE_RETHROW -# define BOOST_INTRUSIVE_CATCH_END } -#endif - -#endif //#ifndef BOOST_INTRUSIVE_NO_EXCEPTION_SUPPORT_HPP diff --git a/include/boost/intrusive/detail/rbtree_node.hpp b/include/boost/intrusive/detail/rbtree_node.hpp index 647c6e5..5345209 100644 --- a/include/boost/intrusive/detail/rbtree_node.hpp +++ b/include/boost/intrusive/detail/rbtree_node.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include namespace boost { @@ -110,7 +110,7 @@ struct compact_rbtree_node_traits_impl typedef typename boost::pointer_to_other ::type const_node_ptr; - typedef pointer_plus_bit ptr_bit; + typedef pointer_plus_bits ptr_bit; typedef typename node::color color; @@ -133,10 +133,10 @@ struct compact_rbtree_node_traits_impl { n->right_ = r; } static color get_color(const_node_ptr n) - { return (color)ptr_bit::get_bit(n->parent_); } + { return (color)ptr_bit::get_bits(n->parent_); } static void set_color(node_ptr n, color c) - { ptr_bit::set_bit(n->parent_, c != 0); } + { ptr_bit::set_bits(n->parent_, c != 0); } static color black() { return node::black_t; } @@ -146,7 +146,7 @@ struct compact_rbtree_node_traits_impl }; //Dispatches the implementation based on the boolean -template +template struct rbtree_node_traits_dispatch : public default_rbtree_node_traits_impl {}; @@ -161,11 +161,11 @@ template struct rbtree_node_traits : public rbtree_node_traits_dispatch < VoidPointer - , OptimizeSize && - has_pointer_plus_bit + , OptimizeSize && + (max_pointer_plus_bits < VoidPointer , detail::alignment_of >::value - >::value + >::value >= 1) > {}; diff --git a/include/boost/intrusive/detail/slist_node.hpp b/include/boost/intrusive/detail/slist_node.hpp index a5d8883..fb516ec 100644 --- a/include/boost/intrusive/detail/slist_node.hpp +++ b/include/boost/intrusive/detail/slist_node.hpp @@ -78,7 +78,7 @@ class slist_iterator typedef value_type * pointer; slist_iterator() - : members_ (0, 0) + : members_ (node_ptr(0), 0) {} explicit slist_iterator(node_ptr node, const Container *cont_ptr) diff --git a/include/boost/intrusive/detail/tree_algorithms.hpp b/include/boost/intrusive/detail/tree_algorithms.hpp index 7cf320c..3cf9786 100644 --- a/include/boost/intrusive/detail/tree_algorithms.hpp +++ b/include/boost/intrusive/detail/tree_algorithms.hpp @@ -17,7 +17,6 @@ #include #include #include -#include #include namespace boost { @@ -97,6 +96,7 @@ class tree_algorithms { /// @cond private: + typedef typename NodeTraits::node node; /// @endcond @@ -123,6 +123,26 @@ class tree_algorithms /// @cond private: + template + struct dispose_subtree_disposer + { + dispose_subtree_disposer(Disposer &disp, node_ptr subtree) + : disposer_(&disp), subtree_(subtree) + {} + + void release() + { disposer_ = 0; } + + ~dispose_subtree_disposer() + { + if(disposer_){ + dispose_subtree(subtree_, *disposer_); + } + } + Disposer *disposer_; + node_ptr subtree_; + }; + static node_ptr uncast(const_node_ptr ptr) { return node_ptr(const_cast(::boost::intrusive::detail::get_pointer(ptr))); @@ -505,9 +525,21 @@ class tree_algorithms //! Nodes: If node is inserted in a tree, this function corrupts the tree. static void init(node_ptr node) { - NodeTraits::set_parent(node, 0); - NodeTraits::set_left(node, 0); - NodeTraits::set_right(node, 0); + NodeTraits::set_parent(node, node_ptr(0)); + NodeTraits::set_left(node, node_ptr(0)); + NodeTraits::set_right(node, node_ptr(0)); + }; + + //! Effects: Returns true if node is in the same state as if called init(node) + //! + //! Complexity: Constant. + //! + //! Throws: Nothing. + static bool inited(const_node_ptr node) + { + return !NodeTraits::get_parent(node) && + !NodeTraits::get_left(node) && + !NodeTraits::get_right(node) ; }; //! Requires: node must not be part of any tree. @@ -522,7 +554,7 @@ class tree_algorithms //! Nodes: If node is inserted in a tree, this function corrupts the tree. static void init_header(node_ptr header) { - NodeTraits::set_parent(header, 0); + NodeTraits::set_parent(header, node_ptr(0)); NodeTraits::set_left(header, header); NodeTraits::set_right(header, header); } @@ -565,7 +597,7 @@ class tree_algorithms { node_ptr leftmost = NodeTraits::get_left(header); if (leftmost == header) - return 0; + return node_ptr(0); node_ptr leftmost_parent(NodeTraits::get_parent(leftmost)); node_ptr leftmost_right (NodeTraits::get_right(leftmost)); bool is_root = leftmost_parent == header; @@ -580,12 +612,12 @@ class tree_algorithms NodeTraits::set_left(NodeTraits::get_parent(header), leftmost_right); } else if (is_root){ - NodeTraits::set_parent(header, 0); + NodeTraits::set_parent(header, node_ptr(0)); NodeTraits::set_left(header, header); NodeTraits::set_right(header, header); } else{ - NodeTraits::set_left(leftmost_parent, 0); + NodeTraits::set_left(leftmost_parent, node_ptr(0)); NodeTraits::set_left(header, leftmost_parent); } return leftmost; @@ -1143,58 +1175,54 @@ class tree_algorithms node_ptr rightmost = target_sub_root; //First set the subroot - NodeTraits::set_left(target_sub_root, 0); - NodeTraits::set_right(target_sub_root, 0); + NodeTraits::set_left(target_sub_root, node_ptr(0)); + NodeTraits::set_right(target_sub_root, node_ptr(0)); NodeTraits::set_parent(target_sub_root, target_parent); - try { - while(true) { - //First clone left nodes - if( NodeTraits::get_left(current) && - !NodeTraits::get_left(insertion_point)) { - current = NodeTraits::get_left(current); - node_ptr temp = insertion_point; - //Clone and mark as leaf - insertion_point = cloner(current); - NodeTraits::set_left (insertion_point, 0); - NodeTraits::set_right (insertion_point, 0); - //Insert left - NodeTraits::set_parent(insertion_point, temp); - NodeTraits::set_left (temp, insertion_point); - //Update leftmost - if(rightmost == target_sub_root) - leftmost = insertion_point; - } - //Then clone right nodes - else if( NodeTraits::get_right(current) && - !NodeTraits::get_right(insertion_point)){ - current = NodeTraits::get_right(current); - node_ptr temp = insertion_point; - //Clone and mark as leaf - insertion_point = cloner(current); - NodeTraits::set_left (insertion_point, 0); - NodeTraits::set_right (insertion_point, 0); - //Insert right - NodeTraits::set_parent(insertion_point, temp); - NodeTraits::set_right (temp, insertion_point); - //Update rightmost - rightmost = insertion_point; - } - //If not, go up - else if(current == source_root){ - break; - } - else{ - //Branch completed, go up searching more nodes to clone - current = NodeTraits::get_parent(current); - insertion_point = NodeTraits::get_parent(insertion_point); - } + dispose_subtree_disposer rollback(disposer, target_sub_root); + while(true) { + //First clone left nodes + if( NodeTraits::get_left(current) && + !NodeTraits::get_left(insertion_point)) { + current = NodeTraits::get_left(current); + node_ptr temp = insertion_point; + //Clone and mark as leaf + insertion_point = cloner(current); + NodeTraits::set_left (insertion_point, node_ptr(0)); + NodeTraits::set_right (insertion_point, node_ptr(0)); + //Insert left + NodeTraits::set_parent(insertion_point, temp); + NodeTraits::set_left (temp, insertion_point); + //Update leftmost + if(rightmost == target_sub_root) + leftmost = insertion_point; + } + //Then clone right nodes + else if( NodeTraits::get_right(current) && + !NodeTraits::get_right(insertion_point)){ + current = NodeTraits::get_right(current); + node_ptr temp = insertion_point; + //Clone and mark as leaf + insertion_point = cloner(current); + NodeTraits::set_left (insertion_point, node_ptr(0)); + NodeTraits::set_right (insertion_point, node_ptr(0)); + //Insert right + NodeTraits::set_parent(insertion_point, temp); + NodeTraits::set_right (temp, insertion_point); + //Update rightmost + rightmost = insertion_point; + } + //If not, go up + else if(current == source_root){ + break; + } + else{ + //Branch completed, go up searching more nodes to clone + current = NodeTraits::get_parent(current); + insertion_point = NodeTraits::get_parent(insertion_point); } } - catch(...) { - dispose_subtree(target_sub_root, disposer); - throw; - } + rollback.release(); leftmost_out = leftmost; rightmost_out = rightmost; } @@ -1321,8 +1349,8 @@ class tree_algorithms NodeTraits::set_right(header, z); } NodeTraits::set_parent(z, par); - NodeTraits::set_right(z, 0); - NodeTraits::set_left(z, 0); + NodeTraits::set_right(z, node_ptr(0)); + NodeTraits::set_left(z, node_ptr(0)); } static void erase(node_ptr header, node_ptr z) @@ -1384,7 +1412,7 @@ class tree_algorithms { std::size_t len; len = 0; - if(!old_root) return 0; + if(!old_root) return node_ptr(0); //To avoid irregularities in the algorithm (old_root can be a //left or right child or even the root of the tree) just put the @@ -1392,8 +1420,8 @@ class tree_algorithms //information to restore the original relationship after //the algorithm is applied. node_ptr super_root = NodeTraits::get_parent(old_root); - assert(super_root); - + BOOST_INTRUSIVE_INVARIANT_ASSERT(super_root); + //Get info node_ptr super_root_right_backup = NodeTraits::get_right(super_root); bool super_root_is_header = is_header(super_root); @@ -1464,7 +1492,7 @@ class tree_algorithms //information to restore the original relationship after //the algorithm is applied. node_ptr super_root = NodeTraits::get_parent(old_root); - assert(super_root); + BOOST_INTRUSIVE_INVARIANT_ASSERT(super_root); //Get info node_ptr super_root_right_backup = NodeTraits::get_right(super_root); @@ -1511,6 +1539,28 @@ class tree_algorithms return new_root; } + //! Requires: "n" must be a node inserted in a tree. + //! + //! Effects: Returns a pointer to the header node of the tree. + //! + //! Complexity: Logarithmic. + //! + //! Throws: Nothing. + static node_ptr get_root(node_ptr node) + { + BOOST_INTRUSIVE_INVARIANT_ASSERT((!inited(node))); + node_ptr x = NodeTraits::get_parent(node); + if(x){ + while(!is_header(x)){ + x = NodeTraits::get_parent(x); + } + return x; + } + else{ + return node; + } + } + private: static void erase_impl(node_ptr header, node_ptr z, data_for_rebalance &info) { @@ -1569,7 +1619,6 @@ class tree_algorithms info.x_parent = x_parent; info.y = y; } - }; } //namespace detail { diff --git a/include/boost/intrusive/detail/tree_node.hpp b/include/boost/intrusive/detail/tree_node.hpp index ea53b1c..a375cba 100644 --- a/include/boost/intrusive/detail/tree_node.hpp +++ b/include/boost/intrusive/detail/tree_node.hpp @@ -168,6 +168,11 @@ class tree_iterator return 0; } + tree_iterator end_iterator_from_it() const + { + return tree_iterator(node_algorithms::get_header(this->pointed_node()), this->get_container()); + } + private: struct members : public detail::select_constptr diff --git a/include/boost/intrusive/detail/utilities.hpp b/include/boost/intrusive/detail/utilities.hpp index e8b3bb3..f190c92 100644 --- a/include/boost/intrusive/detail/utilities.hpp +++ b/include/boost/intrusive/detail/utilities.hpp @@ -235,16 +235,9 @@ struct node_cloner node_cloner(F f, const Container *cont) : base_t(f), cont_(cont) {} - + node_ptr operator()(node_ptr p) - { - node_ptr n = cont_->get_real_value_traits().to_node_ptr - (*base_t::get()(*cont_->get_real_value_traits().to_value_ptr(p))); - //Cloned node must be in default mode if the linking mode requires it - if(safemode_or_autounlink) - BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(node_algorithms::unique(n)); - return n; - } + { return this->operator()(*p); } node_ptr operator()(const node &to_clone) { @@ -396,16 +389,16 @@ template struct link_dispatch {}; -template -void destructor_impl(Container &cont, detail::link_dispatch) -{ (void)cont; BOOST_INTRUSIVE_SAFE_HOOK_DESTRUCTOR_ASSERT(!cont.is_linked()); } +template +void destructor_impl(Hook &hook, detail::link_dispatch) +{ (void)hook; BOOST_INTRUSIVE_SAFE_HOOK_DESTRUCTOR_ASSERT(!hook.is_linked()); } -template -void destructor_impl(Container &cont, detail::link_dispatch) -{ cont.unlink(); } +template +void destructor_impl(Hook &hook, detail::link_dispatch) +{ hook.unlink(); } -template -void destructor_impl(Container &, detail::link_dispatch) +template +void destructor_impl(Hook &, detail::link_dispatch) {} template @@ -548,6 +541,62 @@ inline std::size_t sqrt2_pow_2xplus1 (std::size_t x) return (value >> (pow - x)) + 1; } +template +class exception_disposer +{ + Container *cont_; + Disposer &disp_; + + exception_disposer(const exception_disposer&); + exception_disposer &operator=(const exception_disposer&); + + public: + exception_disposer(Container &cont, Disposer &disp) + : cont_(&cont), disp_(disp) + {} + + void release() + { cont_ = 0; } + + ~exception_disposer() + { + if(cont_){ + cont_->clear_and_dispose(disp_); + } + } +}; + +template +class exception_array_disposer +{ + Container *cont_; + Disposer &disp_; + typename Container::size_type &constructed_; + + exception_array_disposer(const exception_array_disposer&); + exception_array_disposer &operator=(const exception_array_disposer&); + + public: + typedef typename Container::size_type size_type; + exception_array_disposer + (Container &cont, Disposer &disp, size_type &constructed) + : cont_(&cont), disp_(disp), constructed_(constructed) + {} + + void release() + { cont_ = 0; } + + ~exception_array_disposer() + { + size_type n = constructed_; + if(cont_){ + while(n--){ + cont_[n].clear_and_dispose(disp_); + } + } + } +}; + } //namespace detail } //namespace intrusive } //namespace boost diff --git a/include/boost/intrusive/hashtable.hpp b/include/boost/intrusive/hashtable.hpp index 6947427..b621894 100644 --- a/include/boost/intrusive/hashtable.hpp +++ b/include/boost/intrusive/hashtable.hpp @@ -14,15 +14,15 @@ #include //std C++ -#include -#include -#include -#include +#include //std::equal_to +#include //std::pair +#include //std::swap, std::lower_bound, std::upper_bound +#include //std::size_t +#include //std::iterator_traits //boost #include #include #include -#include //General intrusive utilities #include #include @@ -34,12 +34,128 @@ #include #include #include +#include namespace boost { namespace intrusive { /// @cond +namespace detail { + +template +struct hash_reduced_slist_node_traits +{ + template static detail::one test(...); + template static detail::two test(typename U::reduced_slist_node_traits* = 0); + static const bool value = sizeof(test(0)) == sizeof(detail::two); +}; + +template +struct apply_reduced_slist_node_traits +{ + typedef typename NodeTraits::reduced_slist_node_traits type; +}; + +template +struct reduced_slist_node_traits +{ + typedef typename detail::eval_if_c + < hash_reduced_slist_node_traits::value + , apply_reduced_slist_node_traits + , detail::identity + >::type type; +}; + +template +struct get_slist_impl +{ + typedef trivial_value_traits trivial_traits; + + //Reducing symbol length + struct type : make_slist + < typename NodeTraits::node + , boost::intrusive::value_traits + , boost::intrusive::constant_time_size + , boost::intrusive::size_type + >::type + {}; +}; + +template +struct unordered_bucket_impl +{ + /// @cond + typedef typename detail::eval_if_c + < detail::external_value_traits_is_true + ::value + , detail::eval_value_traits + + , detail::identity + + >::type real_value_traits; + + typedef typename detail::get_node_traits + ::type node_traits; + typedef typename get_slist_impl + ::type + >::type slist_impl; + typedef detail::bucket_impl implementation_defined; + typedef implementation_defined type; +}; + +template +struct unordered_bucket_ptr_impl +{ + /// @cond + + typedef typename detail::eval_if_c + < detail::external_value_traits_is_true + ::value + , detail::eval_value_traits + + , detail::identity + + >::type real_value_traits; + typedef typename detail::get_node_traits + ::type::node_ptr node_ptr; + typedef typename unordered_bucket_impl + ::type bucket_type; + typedef typename boost::pointer_to_other + ::type implementation_defined; + /// @endcond + typedef implementation_defined type; +}; + +} //namespace detail { + +/// @endcond + +template +struct unordered_bucket +{ + /// @cond + typedef typename ValueTraitsOrHookOption:: + template pack::value_traits supposed_value_traits; + /// @endcond + typedef typename detail::unordered_bucket_impl + ::type type; +}; + +template +struct unordered_bucket_ptr +{ + /// @cond + typedef typename ValueTraitsOrHookOption:: + template pack::value_traits supposed_value_traits; + /// @endcond + typedef typename detail::unordered_bucket_ptr_impl + ::type type; +}; + +/// @cond + namespace detail{ template @@ -59,6 +175,23 @@ struct store_hash_is_true static const bool value = store_hash_bool::value > sizeof(one)*2; }; +template +struct optimize_multikey_bool +{ + template + struct two_or_three {one _[2 + Add];}; + template static one test(...); + template static two_or_three + test (detail::bool_* = 0); + static const std::size_t value = sizeof(test(0)); +}; + +template +struct optimize_multikey_is_true +{ + static const bool value = optimize_multikey_bool::value > sizeof(one)*2; +}; + template struct bucket_plus_size : public detail::size_holder @@ -92,7 +225,7 @@ struct bucket_hash_t : public detail::ebo_functor_holder bucket_plus_size bucket_plus_size_; }; -template +template struct bucket_hash_equal_t : public detail::ebo_functor_holder { typedef typename Config::equal equal; @@ -105,19 +238,41 @@ struct bucket_hash_equal_t : public detail::ebo_functor_holder bucket_hash; }; +template //cache_begin == true version +struct bucket_hash_equal_t + : public detail::ebo_functor_holder +{ + typedef typename Config::equal equal; + typedef typename Config::hash hasher; + typedef typename Config::bucket_traits bucket_traits; + typedef typename unordered_bucket_ptr_impl + ::type bucket_ptr; + + bucket_hash_equal_t(const bucket_traits &b_traits, const hasher & h, const equal &e) + : detail::ebo_functor_holder(e), bucket_hash(b_traits, h) + {} + bucket_hash_t bucket_hash; + bucket_ptr cached_begin_; +}; + template -struct data_t : public Config::value_traits +struct hashtable_data_t : public Config::value_traits { typedef typename Config::value_traits value_traits; typedef typename Config::equal equal; typedef typename Config::hash hasher; typedef typename Config::bucket_traits bucket_traits; - data_t( const bucket_traits &b_traits, const hasher & h - , const equal &e, const value_traits &val_traits) + hashtable_data_t( const bucket_traits &b_traits, const hasher & h + , const equal &e, const value_traits &val_traits) : Config::value_traits(val_traits), bucket_hash_equal_(b_traits, h, e) {} - bucket_hash_equal_t bucket_hash_equal_; + bucket_hash_equal_t bucket_hash_equal_; +}; + +struct insert_commit_data_impl +{ + std::size_t hash; }; } //namespace detail { @@ -137,12 +292,14 @@ struct get_default_uset_hook }; template < class ValueTraits + , bool UniqueKeys , class Hash , class Equal , class SizeType , bool ConstantTimeSize , class BucketTraits , bool Power2Buckets + , bool CacheBegin > struct usetopt { @@ -153,6 +310,8 @@ struct usetopt typedef BucketTraits bucket_traits; static const bool constant_time_size = ConstantTimeSize; static const bool power_2_buckets = Power2Buckets; + static const bool unique_keys = UniqueKeys; + static const bool cache_begin = CacheBegin; }; struct default_bucket_traits; @@ -174,75 +333,12 @@ struct uset_defaults , hash > , bucket_traits , power_2_buckets + , cache_begin >::type {}; -template -struct get_slist_impl -{ - typedef trivial_value_traits trivial_traits; - - //Reducing symbol length - struct type : make_slist - < typename NodeTraits::node - , boost::intrusive::value_traits - , boost::intrusive::constant_time_size - , boost::intrusive::size_type - >::type - {}; -}; - /// @endcond -template -struct unordered_bucket -{ - /// @cond - typedef typename ValueTraitsOrHookOption:: - template pack::value_traits supposed_value_traits; - - typedef typename detail::eval_if_c - < detail::external_value_traits_is_true - ::value - , detail::eval_value_traits - - , detail::identity - - >::type real_value_traits; - - typedef typename detail::get_node_traits - ::type node_traits; - typedef typename get_slist_impl - ::type slist_impl; - typedef detail::bucket_impl implementation_defined; - /// @endcond - typedef implementation_defined type; -}; - -template -struct unordered_bucket_ptr -{ - /// @cond - typedef typename ValueTraitsOrHookOption:: - template pack::value_traits supposed_value_traits; - typedef typename detail::eval_if_c - < detail::external_value_traits_is_true - ::value - , detail::eval_value_traits - - , detail::identity - - >::type real_value_traits; - typedef typename detail::get_node_traits - ::type::node_ptr node_ptr; - typedef typename unordered_bucket - ::type bucket_type; - typedef typename boost::pointer_to_other - ::type implementation_defined; - /// @endcond - typedef implementation_defined type; -}; - //! The class template hashtable is an intrusive hash table container, that //! is used to construct intrusive unordered_set and unordered_multiset containers. The //! no-throw guarantee holds only, if the Equal object and Hasher don't throw. @@ -261,7 +357,8 @@ struct unordered_bucket_ptr //! //! The container supports the following options: //! \c base_hook<>/member_hook<>/value_traits<>, -//! \c constant_time_size<>, \c size_type<>, \c hash<> and \c equal<> . +//! \c constant_time_size<>, \c size_type<>, \c hash<> and \c equal<> +//! \c bucket_traits<>, power_2_buckets<> and cache_begin<>. //! //! hashtable only provides forward iterators but it provides 4 iterator types: //! iterator and const_iterator to navigate through the whole container and @@ -284,13 +381,12 @@ template template #endif class hashtable_impl - : private detail::data_t + : private detail::hashtable_data_t { public: typedef typename Config::value_traits value_traits; /// @cond - static const bool external_value_traits = detail::external_value_traits_is_true::value; typedef typename detail::eval_if_c @@ -306,8 +402,10 @@ class hashtable_impl , detail::eval_bucket_traits , detail::identity >::type real_bucket_traits; - typedef typename get_slist_impl - ::type slist_impl; + typedef typename detail::get_slist_impl + ::type + >::type slist_impl; /// @endcond typedef typename real_value_traits::pointer pointer; @@ -327,7 +425,7 @@ class hashtable_impl typedef typename slist_impl::const_iterator const_siterator; typedef detail::hashtable_iterator iterator; typedef detail::hashtable_iterator const_iterator; - typedef typename real_value_traits::node_traits node_traits; + typedef typename real_value_traits::node_traits node_traits; typedef typename node_traits::node node; typedef typename boost::pointer_to_other ::type node_ptr; @@ -338,19 +436,67 @@ class hashtable_impl static const bool constant_time_size = Config::constant_time_size; static const bool stateful_value_traits = detail::store_cont_ptr_on_it::value; static const bool store_hash = detail::store_hash_is_true::value; + static const bool unique_keys = Config::unique_keys; + static const bool optimize_multikey + = detail::optimize_multikey_is_true::value && !unique_keys; + static const bool power_2_buckets = Config::power_2_buckets; + static const bool cache_begin = Config::cache_begin; /// @cond private: - typedef detail::bool_ store_hash_t; - typedef detail::size_holder size_traits; - typedef detail::data_t base_type; - typedef detail::transform_iterator - < typename slist_impl::iterator - , detail::node_to_value > local_iterator_impl; - typedef detail::transform_iterator - < typename slist_impl::iterator - , detail::node_to_value > const_local_iterator_impl; + typedef typename slist_impl::node_ptr slist_node_ptr; + typedef typename boost::pointer_to_other + ::type void_pointer; + //We'll define group traits, but these won't be instantiated if + //optimize_multikey is not true + typedef unordered_group_node_traits group_traits; + typedef circular_slist_algorithms group_algorithms; + typedef detail::bool_ store_hash_t; + typedef detail::bool_ optimize_multikey_t; + typedef detail::bool_ cache_begin_t; + typedef detail::bool_ power_2_buckets_t; + typedef detail::size_holder size_traits; + typedef detail::hashtable_data_t base_type; + + template + struct downcast_node_to_value + : public detail::node_to_value + { + typedef detail::node_to_value base_t; + typedef typename base_t::result_type result_type; + typedef typename detail::add_const_if_c + ::type &first_argument_type; + typedef typename detail::add_const_if_c + ::type &intermediate_argument_type; + + downcast_node_to_value(const hashtable_impl *cont) + : base_t(cont) + {} + + result_type operator()(first_argument_type arg) const + { return this->base_t::operator()(static_cast(arg)); } + }; + + template + struct node_cast_adaptor + : private detail::ebo_functor_holder + { + typedef detail::ebo_functor_holder base_t; + + template + node_cast_adaptor(const ConvertibleToF &c2f, const hashtable_impl *cont) + : base_t(base_t(c2f, cont)) + {} + + typename base_t::node_ptr operator()(const typename slist_impl::node &to_clone) + { return base_t::operator()(static_cast(to_clone)); } + + void operator()(typename slist_impl::node_ptr to_clone) + { base_t::operator()(node_ptr(&static_cast(*to_clone))); } + }; + + private: //noncopyable hashtable_impl (const hashtable_impl&); hashtable_impl operator =(const hashtable_impl&); @@ -362,103 +508,23 @@ class hashtable_impl //Constant-time size is incompatible with auto-unlink hooks! BOOST_STATIC_ASSERT(!(constant_time_size && ((int)real_value_traits::link_mode == (int)auto_unlink))); - static const bool power_2_buckets = Config::power_2_buckets; + template + node_cast_adaptor > + make_node_disposer(const Disposer &disposer) const + { return node_cast_adaptor >(disposer, this); } - std::size_t from_hash_to_bucket(std::size_t hash_value) const - { return from_hash_to_bucket(hash_value, detail::bool_()); } - - std::size_t from_hash_to_bucket(std::size_t hash_value, detail::bool_) const - { return hash_value % this->get_real_bucket_traits().bucket_count(); } - - std::size_t from_hash_to_bucket(std::size_t hash_value, detail::bool_) const - { return hash_value & (this->get_real_bucket_traits().bucket_count() - 1); } - - const key_equal &priv_equal() const - { return static_cast(this->bucket_hash_equal_.get()); } - - key_equal &priv_equal() - { return static_cast(this->bucket_hash_equal_.get()); } - - const real_bucket_traits &get_real_bucket_traits(detail::bool_) const - { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_; } - - const real_bucket_traits &get_real_bucket_traits(detail::bool_) const - { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_.get_bucket_traits(*this); } - - real_bucket_traits &get_real_bucket_traits(detail::bool_) - { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_; } - - real_bucket_traits &get_real_bucket_traits(detail::bool_) - { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_.get_bucket_traits(*this); } - - const real_bucket_traits &get_real_bucket_traits() const - { return this->get_real_bucket_traits(detail::bool_()); } - - real_bucket_traits &get_real_bucket_traits() - { return this->get_real_bucket_traits(detail::bool_()); } - - const hasher &priv_hasher() const - { return static_cast(this->bucket_hash_equal_.bucket_hash.get()); } - - hasher &priv_hasher() - { return static_cast(this->bucket_hash_equal_.bucket_hash.get()); } - - bucket_ptr priv_buckets() const - { return this->get_real_bucket_traits().bucket_begin(); } - - size_type priv_buckets_len() const - { return this->get_real_bucket_traits().bucket_count(); } - - static node_ptr uncast(const_node_ptr ptr) - { - return node_ptr(const_cast(detail::get_pointer(ptr))); - } - - node &from_value_to_node(value_type &v) - { return *this->get_real_value_traits().to_node_ptr(v); } - - const node &from_value_to_node(const value_type &v) const - { return *this->get_real_value_traits().to_node_ptr(v); } - - size_traits &priv_size_traits() - { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_; } - - const size_traits &priv_size_traits() const - { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_; } - - struct insert_commit_data_impl - { - size_type hash; - }; /// @endcond public: + typedef detail::insert_commit_data_impl insert_commit_data; - class local_iterator - : public local_iterator_impl - { - public: - local_iterator() - {} + typedef detail::transform_iterator + < typename slist_impl::iterator + , downcast_node_to_value > local_iterator; - local_iterator(siterator sit, const hashtable_impl *cont) - : local_iterator_impl(sit, cont) - {} - }; - - class const_local_iterator - : public const_local_iterator_impl - { - public: - const_local_iterator() - {} - - const_local_iterator(siterator sit, const hashtable_impl *cont) - : const_local_iterator_impl(sit, cont) - {} - }; - - typedef insert_commit_data_impl insert_commit_data; + typedef detail::transform_iterator + < typename slist_impl::iterator + , downcast_node_to_value > const_local_iterator; /// @cond @@ -503,7 +569,7 @@ class hashtable_impl , const value_traits &v_traits = value_traits()) : base_type(b_traits, hash_func, equal_func, v_traits) { - priv_clear_buckets(); + priv_initialize_buckets(); this->priv_size_traits().set_size(size_type(0)); BOOST_INTRUSIVE_INVARIANT_ASSERT(this->priv_buckets_len() != 0); //Check power of two bucket array if the option is activated @@ -562,7 +628,7 @@ class hashtable_impl //! //! Throws: Nothing. iterator end() - { return iterator(invalid_local_it(this->get_real_bucket_traits()), 0); } + { return iterator(priv_invalid_local_it(), 0); } //! Effects: Returns a const_iterator pointing to the end of the unordered_set. //! @@ -578,7 +644,7 @@ class hashtable_impl //! //! Throws: Nothing. const_iterator cend() const - { return const_iterator(invalid_local_it(this->get_real_bucket_traits()), 0); } + { return const_iterator(priv_invalid_local_it(), 0); } //! Effects: Returns the hasher object used by the unordered_set. //! @@ -598,8 +664,8 @@ class hashtable_impl //! Effects: Returns true is the container is empty. //! - //! Complexity: if constant_time_size is false, average constant time - //! (worst case, with empty() == true): O(this->bucket_count()). + //! Complexity: if constant-time size and cache_last options are disabled, + //! average constant time (worst case, with empty() == true: O(this->bucket_count()). //! Otherwise constant. //! //! Throws: Nothing. @@ -608,6 +674,9 @@ class hashtable_impl if(constant_time_size){ return !this->size(); } + else if(cache_begin){ + return this->begin() == this->end(); + } else{ size_type buckets_len = this->priv_buckets_len(); const bucket_type *b = detail::get_pointer(this->priv_buckets()); @@ -658,7 +727,8 @@ class hashtable_impl swap(this->priv_equal(), other.priv_equal()); swap(this->priv_hasher(), other.priv_hasher()); //These can't throw - swap(this->get_real_bucket_traits(), other.get_real_bucket_traits()); + swap(this->priv_real_bucket_traits(), other.priv_real_bucket_traits()); + priv_swap_cache(cache_begin_t(), 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()); @@ -698,75 +768,71 @@ class hashtable_impl const bucket_ptr src_buckets = src.priv_buckets(); const bucket_ptr dst_buckets = this->priv_buckets(); size_type constructed; - BOOST_INTRUSIVE_TRY{ - for( constructed = 0 - ; constructed < dst_bucket_count + typedef node_cast_adaptor > NodeDisposer; + typedef node_cast_adaptor > NodeCloner; + NodeDisposer node_disp(disposer, this); + + detail::exception_array_disposer + 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), 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){ - dst_buckets[constructed].clone_from - ( src_buckets[constructed] - , detail::node_cloner(cloner, this) - , detail::node_disposer(disposer, this) - ); - } - 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 = (power_2_buckets) - ? dst_buckets[constructed & (dst_bucket_count-1)] - : dst_buckets[constructed % 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(*detail::node_cloner - (cloner, this)(b.pointed_node())); - } + bucket_type &dst_b = + dst_buckets[priv_hash_to_bucket(constructed, 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)(*b.pointed_node()))); } } } - BOOST_INTRUSIVE_CATCH(...){ - while(constructed--){ - dst_buckets[constructed].clear_and_dispose - (detail::node_disposer(disposer, this)); - } - BOOST_INTRUSIVE_RETHROW; - } - BOOST_INTRUSIVE_CATCH_END + rollback.release(); this->priv_size_traits().set_size(src.priv_size_traits().get_size()); + priv_insertion_update_cache(0u); + priv_erasure_update_cache(); } else{ //Unlike previous cloning algorithm, this can throw //if cloner, the hasher or comparison functor throw const_iterator b(src.begin()), e(src.end()); - BOOST_INTRUSIVE_TRY{ - for(; b != e; ++b){ - this->insert_equal(*cloner(*b)); - } + detail::exception_disposer + rollback(*this, disposer); + for(; b != e; ++b){ + this->insert_equal(*cloner(*b)); } - BOOST_INTRUSIVE_CATCH(...){ - this->clear_and_dispose(disposer); - BOOST_INTRUSIVE_RETHROW; - } - BOOST_INTRUSIVE_CATCH_END + rollback.release(); } } } iterator insert_equal(reference value) { - size_type bucket_num, hash_value; + 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); + (value, this->priv_hasher(), this->priv_equal(), bucket_num, hash_value, prev); bucket_type &b = this->priv_buckets()[bucket_num]; - if(it == invalid_local_it(this->get_real_bucket_traits())){ - it = b.before_begin(); - } - node_ptr n = node_ptr(&from_value_to_node(value)); + bool found_equal = it != priv_invalid_local_it(); + node_ptr n = node_ptr(&priv_value_to_node(value)); this->priv_store_hash(n, hash_value, store_hash_t()); if(safemode_or_autounlink) BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(node_algorithms::unique(n)); + if(!found_equal){ + it = b.before_begin(); + } + this->priv_insert_in_group(found_equal ? dcast_bucket_ptr(it.pointed_node()) : node_ptr(0), n, optimize_multikey_t()); + priv_insertion_update_cache(bucket_num); this->priv_size_traits().increment(); return iterator(b.insert_after(it, *n), this); } @@ -867,9 +933,10 @@ class hashtable_impl , 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); - bool success = prev_pos == invalid_local_it(this->get_real_bucket_traits()); + this->priv_find(key, hash_func, equal_func, bucket_num, commit_data.hash, prev); + bool success = prev_pos == priv_invalid_local_it(); if(success){ prev_pos = this->priv_buckets()[bucket_num].before_begin(); } @@ -897,14 +964,16 @@ class hashtable_impl //! After a successful rehashing insert_commit_data remains valid. iterator insert_unique_commit(reference value, const insert_commit_data &commit_data) { - size_type bucket_num = from_hash_to_bucket(commit_data.hash); + size_type bucket_num = priv_hash_to_bucket(commit_data.hash); bucket_type &b = this->priv_buckets()[bucket_num]; this->priv_size_traits().increment(); - node_ptr n = node_ptr(&from_value_to_node(value)); + node_ptr n = node_ptr(&priv_value_to_node(value)); this->priv_store_hash(n, commit_data.hash, store_hash_t()); if(safemode_or_autounlink) BOOST_INTRUSIVE_SAFE_HOOK_DEFAULT_ASSERT(node_algorithms::unique(n)); - return iterator( b.insert_after(b.before_begin(), *n), this); + priv_insertion_update_cache(bucket_num); + this->priv_insert_in_group(node_ptr(0), n, optimize_multikey_t()); + return iterator(b.insert_after(b.before_begin(), *n), this); } //! Effects: Erases the element pointed to by i. @@ -983,12 +1052,9 @@ class hashtable_impl template void erase_and_dispose(const_iterator i, Disposer disposer) { - siterator to_erase(i.slist_it()); - bucket_ptr f(priv_buckets()), l(f + priv_buckets_len()); - bucket_type &b = this->priv_buckets()[bucket_type::get_bucket_num(to_erase, *f, *l)]; - b.erase_after_and_dispose - (b.previous(to_erase), detail::node_disposer(disposer, this)); + priv_erase(i, disposer, optimize_multikey_t()); this->priv_size_traits().decrement(); + priv_erasure_update_cache(); } //! Requires: Disposer::operator()(pointer) shouldn't throw. @@ -1009,66 +1075,26 @@ class hashtable_impl if(b == e) return; //Get the bucket number and local iterator for both iterators - bucket_ptr f(priv_buckets()), l(f + priv_buckets_len()); - size_type first_bucket_num = bucket_type::get_bucket_num(b.slist_it(), *f, *l); + siterator first_local_it(b.slist_it()); + size_type first_bucket_num = this->priv_get_bucket_num(first_local_it); siterator before_first_local_it - = priv_buckets()[first_bucket_num].previous(b.slist_it()); + = priv_get_previous(priv_buckets()[first_bucket_num], first_local_it); size_type last_bucket_num; siterator last_local_it; //For the end iterator, we will assign the end iterator //of the last bucket - if(e == end()){ + if(e == this->end()){ last_bucket_num = this->bucket_count() - 1; last_local_it = priv_buckets()[last_bucket_num].end(); } else{ last_local_it = e.slist_it(); - last_bucket_num = bucket_type::get_bucket_num(last_local_it, *f, *l); - } - - const bucket_ptr buckets = priv_buckets(); - //First erase the nodes of the first bucket - { - bucket_type &first_b = buckets[first_bucket_num]; - siterator nxt(before_first_local_it); ++nxt; - siterator end = first_b.end(); - while(nxt != end){ - nxt = first_b.erase_after_and_dispose - ( before_first_local_it - , detail::node_disposer(disposer, this)); - this->priv_size_traits().decrement(); - } - } - - //Now fully clear the intermediate buckets - for(size_type i = first_bucket_num+1; i < last_bucket_num; ++i){ - bucket_type &b = buckets[i]; - if(b.empty()) - continue; - siterator b_begin(b.before_begin()); - siterator nxt(b_begin); ++nxt; - siterator end = b.end(); - while(nxt != end){ - nxt = b.erase_after_and_dispose - (b_begin, detail::node_disposer(disposer, this)); - this->priv_size_traits().decrement(); - } - } - - //Now erase nodes from the last bucket - { - bucket_type &last_b = buckets[last_bucket_num]; - siterator b_begin(last_b.before_begin()); - siterator nxt(b_begin); ++nxt; - while(nxt != last_local_it){ - nxt = last_b.erase_after_and_dispose - (b_begin, detail::node_disposer - (disposer, this)); - this->priv_size_traits().decrement(); - } + last_bucket_num = this->priv_get_bucket_num(last_local_it); } + priv_erase_range(before_first_local_it, first_bucket_num, last_local_it, last_bucket_num, disposer); + priv_erasure_update_cache(first_bucket_num, last_bucket_num); } //! Requires: Disposer::operator()(pointer) shouldn't throw. @@ -1109,40 +1135,35 @@ class hashtable_impl size_type erase_and_dispose(const KeyType& key, KeyHasher hash_func ,KeyValueEqual equal_func, Disposer disposer) { + size_type bucket_num; + std::size_t hash; + siterator prev; + siterator it = + this->priv_find(key, hash_func, equal_func, bucket_num, hash, prev); + bool success = it != priv_invalid_local_it(); size_type count(0); - - if(constant_time_size && this->empty()){ + if(!success){ return 0; } - - bucket_type &b = this->priv_buckets()[from_hash_to_bucket(hash_func(key))]; - siterator it = b.begin(); - siterator prev = b.before_begin(); - - bool found = false; - //Find equal value - while(it != b.end()){ - const value_type &v = - *this->get_real_value_traits().to_value_ptr(it.pointed_node()); - if(equal_func(key, v)){ - found = true; - break; + else if(optimize_multikey){ + siterator last = bucket_type::s_iterator_to + (*node_traits::get_next(priv_get_last_in_group + (dcast_bucket_ptr(it.pointed_node())))); + this->priv_erase_range_impl(bucket_num, prev, last, disposer, count); + } + else{ + //If found erase all equal values + bucket_type &b = this->priv_buckets()[bucket_num]; + for(siterator end = b.end(); it != end; ++count, ++it){ + slist_node_ptr n(it.pointed_node()); + if(!equal_func(key, priv_value_from_slist_node(n))){ + break; + } + this->priv_size_traits().decrement(); } - ++prev; - ++it; - } - - if(!found) - return 0; - - //If found erase all equal values - for(siterator end = b.end(); it != end && - equal_func(key, *this->get_real_value_traits().to_value_ptr(it.pointed_node())) - ; ++count){ - it = b.erase_after_and_dispose - (prev, detail::node_disposer(disposer, this)); - this->priv_size_traits().decrement(); + b.erase_after_and_dispose(prev, it, make_node_disposer(disposer)); } + priv_erasure_update_cache(); return count; } @@ -1179,11 +1200,11 @@ class hashtable_impl size_type num_buckets = this->bucket_count(); bucket_ptr b = this->priv_buckets(); for(; num_buckets--; ++b){ - b->clear_and_dispose - (detail::node_disposer(disposer, this)); + b->clear_and_dispose(make_node_disposer(disposer)); } this->priv_size_traits().set_size(size_type(0)); } + priv_initialize_cache(); } //! Effects: Returns the number of contained elements with the given value @@ -1246,8 +1267,10 @@ class hashtable_impl template iterator find(const KeyType &key, KeyHasher hash_func, KeyValueEqual equal_func) { - size_type bucket_n, hash; - siterator local_it = this->priv_find(key, hash_func, equal_func, bucket_n, hash); + 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); } @@ -1283,8 +1306,10 @@ class hashtable_impl const_iterator find (const KeyType &key, KeyHasher hash_func, KeyValueEqual equal_func) const { - size_type bucket_n, hash_value; - siterator sit = this->priv_find(key, hash_func, equal_func, bucket_n, hash_value); + 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); } @@ -1382,7 +1407,7 @@ class hashtable_impl //! Throws: If the internal hash function throws. iterator iterator_to(reference value) { - return iterator(bucket_type::s_iterator_to(from_value_to_node(value)), this); + return iterator(bucket_type::s_iterator_to(priv_value_to_node(value)), this); } //! Requires: value must be an lvalue and shall be in a unordered_set of @@ -1396,12 +1421,9 @@ class hashtable_impl //! Throws: If the internal hash function throws. const_iterator iterator_to(const_reference value) const { - return const_iterator(bucket_type::s_iterator_to(from_value_to_node(const_cast(value))), this); + return const_iterator(bucket_type::s_iterator_to(priv_value_to_node(const_cast(value))), this); } - - - //! Requires: value must be an lvalue and shall be in a unordered_set of //! appropriate type. Otherwise the behavior is undefined. //! @@ -1417,7 +1439,7 @@ class hashtable_impl static local_iterator s_local_iterator_to(reference value) { BOOST_STATIC_ASSERT((!stateful_value_traits)); - siterator sit = bucket_type::s_iterator_to(((hashtable_impl*)0)->from_value_to_node(value)); + siterator sit = bucket_type::s_iterator_to(((hashtable_impl*)0)->priv_value_to_node(value)); return local_iterator(sit, (hashtable_impl*)0); } @@ -1436,7 +1458,7 @@ class hashtable_impl static const_local_iterator s_local_iterator_to(const_reference value) { BOOST_STATIC_ASSERT((!stateful_value_traits)); - siterator sit = bucket_type::s_iterator_to(((hashtable_impl*)0)->from_value_to_node(const_cast(value))); + siterator sit = bucket_type::s_iterator_to(((hashtable_impl*)0)->priv_value_to_node(const_cast(value))); return const_local_iterator(sit, (hashtable_impl*)0); } @@ -1451,7 +1473,7 @@ class hashtable_impl //! Throws: Nothing. local_iterator local_iterator_to(reference value) { - siterator sit = bucket_type::s_iterator_to(this->from_value_to_node(value)); + siterator sit = bucket_type::s_iterator_to(this->priv_value_to_node(value)); return local_iterator(sit, this); } @@ -1467,7 +1489,7 @@ class hashtable_impl const_local_iterator local_iterator_to(const_reference value) const { siterator sit = bucket_type::s_iterator_to - (const_cast(this->from_value_to_node(value))); + (const_cast(this->priv_value_to_node(value))); return const_local_iterator(sit, this); } @@ -1515,7 +1537,7 @@ class hashtable_impl //! Note: the return value is in the range [0, this->bucket_count()). template size_type bucket(const KeyType& k, const KeyHasher &hash_func) const - { return from_hash_to_bucket(hash_func(k)); } + { return priv_hash_to_bucket(hash_func(k)); } //! Effects: Returns the bucket array pointer passed in the constructor //! or the last rehash function. @@ -1630,75 +1652,81 @@ class hashtable_impl size_type new_buckets_len = new_bucket_traits.bucket_count(); bucket_ptr old_buckets = this->priv_buckets(); size_type old_buckets_len = this->priv_buckets_len(); + //Check power of two bucket array if the option is activated BOOST_INTRUSIVE_INVARIANT_ASSERT (!power_2_buckets || (0 == (new_buckets_len & (new_buckets_len-1u)))); - BOOST_INTRUSIVE_TRY{ - size_type n = 0; - 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 = (old_buckets_len > new_buckets_len) && - (power_2_buckets ||(old_buckets_len % new_buckets_len) == 0); - //If we are shrinking the same bucket array and it's - //is a fast shrink, just rehash the last nodes - if(same_buffer && fast_shrink){ - n = new_buckets_len; - } + size_type n = this->priv_get_cache() - this->priv_buckets(); + 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 = (old_buckets_len > new_buckets_len) && + (power_2_buckets ||(old_buckets_len % new_buckets_len) == 0); + //If we are shrinking the same bucket array and it's + //is a fast shrink, just rehash the last nodes + if(same_buffer && fast_shrink){ + n = new_buckets_len; + } - //Iterate through nodes - for(; n < old_buckets_len; ++n){ - bucket_type &old_bucket = old_buckets[n]; + //Anti-exception stuff: they destroy the elements if something goes wrong + typedef detail::init_disposer NodeDisposer; + NodeDisposer node_disp; + detail::exception_array_disposer + rollback1(new_buckets[0], node_disp, new_buckets_len); + detail::exception_array_disposer + rollback2(old_buckets[0], node_disp, old_buckets_len); - if(!fast_shrink){ - siterator before_i(old_bucket.before_begin()); - siterator end(old_bucket.end()); - siterator i(old_bucket.begin()); - for(;i != end; ++i){ - const value_type &v = *this->get_real_value_traits().to_value_ptr(i.pointed_node()); - const std::size_t hash_value = this->priv_hash_when_rehashing(v, store_hash_t()); - const size_type new_n = (power_2_buckets) - ? (hash_value & (new_buckets_len-1)) : (hash_value % new_buckets_len); - //If this is a buffer expansion don't move if it's not necessary - if(same_buffer && new_n == n){ - ++before_i; - } - else{ - bucket_type &new_b = new_buckets[new_n]; - new_b.splice_after(new_b.before_begin(), old_bucket, before_i); - i = before_i; - } + //Put size in a safe value for rollback exception + size_type size_backup = this->priv_size_traits().get_size(); + this->priv_size_traits().set_size(0); + //Put cache to safe position + priv_initialize_cache(); + priv_insertion_update_cache(size_type(0u)); + + //Iterate through nodes + for(; n < old_buckets_len; ++n){ + bucket_type &old_bucket = old_buckets[n]; + + if(!fast_shrink){ + siterator before_i(old_bucket.before_begin()); + siterator end(old_bucket.end()); + siterator i(old_bucket.begin()); + for(;i != end; ++i){ + const value_type &v = priv_value_from_slist_node(i.pointed_node()); + const std::size_t hash_value = this->priv_stored_hash(v, store_hash_t()); + const size_type new_n = priv_hash_to_bucket(hash_value, new_buckets_len); + siterator last = bucket_type::s_iterator_to + (*priv_get_last_in_group(dcast_bucket_ptr(i.pointed_node()))); + if(same_buffer && new_n == n){ + before_i = last; } - } - else{ - const size_type new_n = (power_2_buckets) - ? (n & (new_buckets_len-1)) - : (n % new_buckets_len); - bucket_type &new_b = new_buckets[new_n]; - new_b.splice_after(new_b.before_begin(), old_bucket); + else{ + 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{ + const size_type new_n = priv_hash_to_bucket(n, new_buckets_len); + 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() + , priv_get_last(old_bucket)); + } + } + } - this->get_real_bucket_traits()= new_bucket_traits; - } - BOOST_INTRUSIVE_CATCH(...){ - for(size_type n = 0; n < new_buckets_len; ++n){ - if(safemode_or_autounlink){ - new_buckets[n].clear_and_dispose - (detail::init_disposer()); - old_buckets[n].clear_and_dispose - (detail::init_disposer()); - } - else{ - new_buckets[n].clear(); - old_buckets[n].clear(); - } - } - this->priv_size_traits().set_size(size_type(0)); - BOOST_INTRUSIVE_RETHROW; - } - BOOST_INTRUSIVE_CATCH_END + this->priv_size_traits().set_size(size_backup); + this->priv_real_bucket_traits() = new_bucket_traits; + priv_initialize_cache(); + priv_insertion_update_cache(size_type(0u)); + priv_erasure_update_cache(); + rollback1.release(); + rollback2.release(); } //! Effects: Returns the nearest new bucket count optimized for @@ -1714,8 +1742,7 @@ class hashtable_impl { const std::size_t *primes = &detail::prime_list_holder<0>::prime_list[0]; const std::size_t *primes_end = primes + detail::prime_list_holder<0>::prime_list_size; - size_type const* bound = - std::lower_bound(primes, primes_end, n); + size_type const* bound = std::lower_bound(primes, primes_end, n); if(bound == primes_end) bound--; return size_type(*bound); @@ -1734,8 +1761,7 @@ class hashtable_impl { const std::size_t *primes = &detail::prime_list_holder<0>::prime_list[0]; const std::size_t *primes_end = primes + detail::prime_list_holder<0>::prime_list_size; - size_type const* bound = - std::upper_bound(primes, primes_end, n); + size_type const* bound = std::upper_bound(primes, primes_end, n); if(bound != primes_end) bound--; return size_type(*bound); @@ -1743,72 +1769,666 @@ class hashtable_impl /// @cond private: + std::size_t priv_hash_to_bucket(std::size_t hash_value) const + { return priv_hash_to_bucket(hash_value, power_2_buckets_t()); } - std::size_t priv_hash_when_rehashing(const value_type &v, detail::true_) - { return node_traits::get_hash(this->get_real_value_traits().to_node_ptr(v)); } + std::size_t priv_hash_to_bucket(std::size_t hash_value, detail::bool_) const + { return hash_value % this->priv_real_bucket_traits().bucket_count(); } - std::size_t priv_hash_when_rehashing(const value_type &v, detail::false_) - { return priv_hasher()(v); } + std::size_t priv_hash_to_bucket(std::size_t hash_value, detail::bool_) const + { return hash_value & (this->priv_real_bucket_traits().bucket_count() - 1); } - void priv_store_hash(node_ptr p, std::size_t h, detail::true_) - { return node_traits::set_hash(p, h); } + std::size_t priv_hash_to_bucket(std::size_t hash_value, std::size_t bucket_len) const + { return priv_hash_to_bucket(hash_value, bucket_len, power_2_buckets_t()); } - void priv_store_hash(node_ptr, std::size_t, detail::false_) - {} - - static siterator invalid_local_it(const real_bucket_traits &b) - { return b.bucket_begin()->end(); } + std::size_t priv_hash_to_bucket(std::size_t hash_value, std::size_t bucket_len, detail::bool_) const + { return hash_value % bucket_len; } - siterator priv_begin(size_type &bucket_num) const + std::size_t priv_hash_to_bucket(std::size_t hash_value, std::size_t bucket_len, detail::bool_) const + { return hash_value & (bucket_len - 1); } + + const key_equal &priv_equal() const + { return static_cast(this->bucket_hash_equal_.get()); } + + key_equal &priv_equal() + { return static_cast(this->bucket_hash_equal_.get()); } + + value_type &priv_value_from_slist_node(slist_node_ptr n) + { return *this->get_real_value_traits().to_value_ptr(dcast_bucket_ptr(n)); } + + const value_type &priv_value_from_slist_node(slist_node_ptr n) const + { return *this->get_real_value_traits().to_value_ptr(dcast_bucket_ptr(n)); } + + const real_bucket_traits &priv_real_bucket_traits(detail::bool_) const + { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_; } + + const real_bucket_traits &priv_real_bucket_traits(detail::bool_) const + { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_.get_bucket_traits(*this); } + + real_bucket_traits &priv_real_bucket_traits(detail::bool_) + { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_; } + + real_bucket_traits &priv_real_bucket_traits(detail::bool_) + { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_.bucket_traits_.get_bucket_traits(*this); } + + const real_bucket_traits &priv_real_bucket_traits() const + { return this->priv_real_bucket_traits(detail::bool_()); } + + real_bucket_traits &priv_real_bucket_traits() + { return this->priv_real_bucket_traits(detail::bool_()); } + + const hasher &priv_hasher() const + { return static_cast(this->bucket_hash_equal_.bucket_hash.get()); } + + hasher &priv_hasher() + { return static_cast(this->bucket_hash_equal_.bucket_hash.get()); } + + bucket_ptr priv_buckets() const + { return this->priv_real_bucket_traits().bucket_begin(); } + + size_type priv_buckets_len() const + { return this->priv_real_bucket_traits().bucket_count(); } + + static node_ptr uncast(const_node_ptr ptr) + { return node_ptr(const_cast(detail::get_pointer(ptr))); } + + node &priv_value_to_node(value_type &v) + { return *this->get_real_value_traits().to_node_ptr(v); } + + const node &priv_value_to_node(const value_type &v) const + { return *this->get_real_value_traits().to_node_ptr(v); } + + size_traits &priv_size_traits() + { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_; } + + const size_traits &priv_size_traits() const + { return this->bucket_hash_equal_.bucket_hash.bucket_plus_size_; } + + template + void priv_erase_range_impl + (size_type bucket_num, siterator before_first_it, siterator end, Disposer disposer, size_type &num_erased) { - size_type buckets_len = this->priv_buckets_len(); - for (bucket_num = 0; bucket_num < buckets_len; ++bucket_num){ - bucket_type &b = this->priv_buckets()[bucket_num]; - if(!b.empty()) - return b.begin(); + const bucket_ptr buckets = priv_buckets(); + bucket_type &b = buckets[bucket_num]; + + if(before_first_it == b.before_begin() && end == b.end()){ + 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.pointed_node(); + while(to_erase != end){ + priv_erase_from_group(end_ptr, dcast_bucket_ptr(to_erase.pointed_node()), optimize_multikey_t()); + to_erase = b.erase_after_and_dispose(before_first_it, make_node_disposer(disposer)); + ++num_erased; + } + this->priv_size_traits().set_size(this->priv_size_traits().get_size()-num_erased); } - return invalid_local_it(this->get_real_bucket_traits()); } - void priv_clear_buckets() - { priv_clear_buckets(this->priv_buckets(), this->priv_buckets_len()); } + template + 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 = priv_buckets(); + 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(b.end()); + while(nxt != end){ + priv_init_group(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; + } + } + } - static void priv_clear_buckets(bucket_ptr buckets_ptr, size_type buckets_len) + template + void priv_erase_range( siterator before_first_it, size_type first_bucket + , siterator last_it, size_type last_bucket + , Disposer disposer) + { + size_type num_erased; + if (first_bucket == last_bucket){ + priv_erase_range_impl(first_bucket, before_first_it, last_it, disposer, num_erased); + } + else { + bucket_type *b = (&this->priv_buckets()[0]); + 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)) + priv_erase_range_impl(first_bucket + 1, n, disposer, num_erased); + priv_erase_range_impl(last_bucket, b[last_bucket].before_begin(), last_it, disposer, num_erased); + } + } + + static node_ptr dcast_bucket_ptr(typename slist_impl::node_ptr p) + { return node_ptr(&static_cast(*p)); } + + std::size_t priv_stored_hash(const value_type &v, detail::true_) + { return node_traits::get_hash(this->get_real_value_traits().to_node_ptr(v)); } + + std::size_t priv_stored_hash(const value_type &v, detail::false_) + { return priv_hasher()(v); } + + std::size_t priv_stored_hash(slist_node_ptr n, detail::true_) + { return node_traits::get_hash(dcast_bucket_ptr(n)); } + + std::size_t priv_stored_hash(slist_node_ptr, detail::false_) + { + //This code should never be reached! + BOOST_INTRUSIVE_INVARIANT_ASSERT(0); + return 0; + } + + static void priv_store_hash(node_ptr p, std::size_t h, detail::true_) + { return node_traits::set_hash(p, h); } + + static void priv_store_hash(node_ptr, std::size_t, detail::false_) + {} + + static void priv_clear_group_nodes(bucket_type &b, detail::true_) + { + siterator it(b.begin()), itend(b.end()); + while(it != itend){ + node_ptr to_erase(dcast_bucket_ptr(it.pointed_node())); + ++it; + group_algorithms::init(to_erase); + } + } + + static void priv_clear_group_nodes(bucket_type &, detail::false_) + {} + + std::size_t priv_get_bucket_num(siterator it) + { return priv_get_bucket_num_hash_dispatch(it, store_hash_t()); } + + std::size_t priv_get_bucket_num_hash_dispatch(siterator it, detail::true_) + { + 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_) + { return priv_get_bucket_num_no_hash_store(it, optimize_multikey_t()); } + + std::size_t priv_get_bucket_num_no_hash_store( siterator it, detail::true_) + { + bucket_ptr f(priv_buckets()), l(f + priv_buckets_len() - 1); + slist_node_ptr bb = priv_get_bucket_before_begin + ( f->end().pointed_node() + , l->end().pointed_node() + , dcast_bucket_ptr(it.pointed_node())); + //Now get the bucket_impl from the iterator + const bucket_type &b = static_cast + (bucket_type::slist_type::container_from_end_iterator(bucket_type::s_iterator_to(*bb))); + //Now just calculate the index b has in the bucket array + return static_cast(&b - &*f); + } + + std::size_t priv_get_bucket_num_no_hash_store( siterator it, detail::false_) + { + bucket_ptr f(priv_buckets()), l(f + priv_buckets_len() - 1); + slist_node_ptr first_ptr(f->cend().pointed_node()) + , last_ptr(l->cend().pointed_node()); + + //The end node is embedded in the singly linked list: + //iterate until we reach it. + while(!(first_ptr <= it.pointed_node() && it.pointed_node() <= last_ptr)){ + ++it; + } + //Now get the bucket_impl from the iterator + const bucket_type &b = static_cast + (bucket_type::container_from_end_iterator(it)); + + //Now just calculate the index b has in the bucket array + return static_cast(&b - &*f); + } + + void priv_erase_from_group(slist_node_ptr end_ptr, 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 first_in_group = node_traits::get_next(prev_in_group_ptr) != to_erase_ptr; + + if(first_in_group && last_in_group){ + group_algorithms::init(to_erase_ptr); + } + else if(first_in_group){ + group_algorithms::unlink_after(nxt_ptr); + } + else if(last_in_group){ + node_ptr first_in_group = //possible_first_in_group ? possible_first_in_group : + priv_get_first_in_group_of_last_in_group(to_erase_ptr); + group_algorithms::unlink_after(first_in_group); + //possible_first_in_group = 0; + } + else{ + group_algorithms::unlink_after(nxt_ptr); + } + } + + void priv_erase_from_group(slist_node_ptr, node_ptr, detail::false_) + {} + + void priv_init_group(slist_node_ptr n, detail::true_) + { group_algorithms::init(dcast_bucket_ptr(n)); } + + void priv_init_group(slist_node_ptr, detail::false_) + {} + + void priv_insert_in_group(node_ptr 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); + } + } + + static slist_node_ptr priv_get_bucket_before_begin + (slist_node_ptr bucket_beg, slist_node_ptr bucket_end, node_ptr p) + { + //First find the last node of p's group. + //This requires checking the first node of the next group or + //the bucket node. + node_ptr prev_node = p; + node_ptr nxt(node_traits::get_next(p)); + while(!(bucket_beg <= nxt && nxt <= bucket_end) && + (group_traits::get_next(nxt) == prev_node)){ + prev_node = nxt; + nxt = node_traits::get_next(nxt); + } + + //If we've reached the bucket node just return it. + if(bucket_beg <= nxt && nxt <= bucket_end){ + return nxt; + } + + //Otherwise, iterate using group links until the bucket node + node_ptr first_node_of_group = nxt; + node_ptr last_node_group = group_traits::get_next(first_node_of_group); + slist_node_ptr possible_end = node_traits::get_next(last_node_group); + + while(!(bucket_beg <= possible_end && possible_end <= bucket_end)){ + first_node_of_group = dcast_bucket_ptr(possible_end); + last_node_group = group_traits::get_next(first_node_of_group); + possible_end = node_traits::get_next(last_node_group); + } + return possible_end; + } + + static node_ptr priv_get_prev_to_first_in_group(slist_node_ptr bucket_node, node_ptr first_in_group) + { + //Just iterate using group links and obtain the node + //before "first_in_group)" + node_ptr prev_node = dcast_bucket_ptr(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); + } + return prev_node; + } + + static node_ptr priv_get_first_in_group_of_last_in_group(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; + } + + void priv_insert_in_group(node_ptr, node_ptr, detail::false_) + {} + + static node_ptr priv_get_last_in_group(node_ptr first_in_group) + { return priv_get_last_in_group(first_in_group, optimize_multikey_t()); } + + static node_ptr priv_get_last_in_group(node_ptr first_in_group, detail::true_) + { return group_traits::get_next(first_in_group); } + + static node_ptr priv_get_last_in_group(node_ptr n, detail::false_) + { return n; } + + siterator priv_get_previous + (bucket_type &b, siterator i) + { return priv_get_previous(b, i, optimize_multikey_t()); } + + siterator priv_get_previous + (bucket_type &b, siterator i, detail::true_) + { + node_ptr elem(dcast_bucket_ptr(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; + + typename bucket_type::node &n = first_in_group + ? *priv_get_prev_to_first_in_group(b.end().pointed_node(), elem) + : *group_traits::get_next(elem) + ; + return bucket_type::s_iterator_to(n); + } + + siterator priv_get_previous + (bucket_type &b, siterator i, detail::false_) + { return b.previous(i); } + + static siterator priv_get_last(bucket_type &b) + { return priv_get_last(b, optimize_multikey_t()); } + + static siterator priv_get_last(bucket_type &b, detail::true_) + { + //First find the last node of p's group. + //This requires checking the first node of the next group or + //the bucket node. + slist_node_ptr end_ptr(b.end().pointed_node()); + node_ptr possible_end(node_traits::get_next( dcast_bucket_ptr(end_ptr))); + node_ptr last_node_group(possible_end); + + while(end_ptr != possible_end){ + last_node_group = group_traits::get_next(dcast_bucket_ptr(possible_end)); + possible_end = node_traits::get_next(last_node_group); + } + return bucket_type::s_iterator_to(*last_node_group); + } + + static siterator priv_get_last(bucket_type &b, detail::false_) + { return b.previous(b.end()); } + + siterator priv_get_previous_and_next_in_group + (siterator i, node_ptr &nxt_in_group) + { + siterator prev; + node_ptr elem(dcast_bucket_ptr(i.pointed_node())); + bucket_ptr f(priv_buckets()), l(f + priv_buckets_len() - 1); + + slist_node_ptr first_end_ptr(f->cend().pointed_node()); + slist_node_ptr last_end_ptr (l->cend().pointed_node()); + + node_ptr nxt(node_traits::get_next(elem)); + node_ptr prev_in_group(group_traits::get_next(elem)); + bool last_in_group = (first_end_ptr <= nxt && nxt <= last_end_ptr) || + (group_traits::get_next(nxt) != 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 = 0; + } + else{ + start_pos = prev_in_group; + nxt_in_group = node_traits::get_next(elem); + } + slist_node_ptr bucket_node; + if(store_hash){ + bucket_node = this->priv_buckets() + [this->priv_hash_to_bucket + (this->priv_stored_hash(elem, store_hash_t())) + ].before_begin().pointed_node(); + } + else{ + bucket_node = priv_get_bucket_before_begin + (first_end_ptr, last_end_ptr, start_pos); + } + prev = bucket_type::s_iterator_to + (*priv_get_prev_to_first_in_group(bucket_node, elem)); + } + else{ + if(last_in_group){ + nxt_in_group = priv_get_first_in_group_of_last_in_group(elem); + } + else{ + nxt_in_group = node_traits::get_next(elem); + } + prev = bucket_type::s_iterator_to(*group_traits::get_next(elem)); + } + return prev; + } + + template + void priv_erase(const_iterator i, Disposer disposer, detail::true_) + { + siterator elem(i.slist_it()); + node_ptr nxt_in_group; + siterator prev = priv_get_previous_and_next_in_group(elem, nxt_in_group); + 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(dcast_bucket_ptr(elem.pointed_node())); + } + + template + void priv_erase(const_iterator i, Disposer disposer, detail::false_) + { + siterator to_erase(i.slist_it()); + bucket_type &b = this->priv_buckets()[this->priv_get_bucket_num(to_erase)]; + siterator prev(priv_get_previous(b, to_erase)); + b.erase_after_and_dispose(prev, make_node_disposer(disposer)); + } + + bucket_ptr priv_invalid_bucket() const + { + const real_bucket_traits &rbt = this->priv_real_bucket_traits(); + return rbt.bucket_begin() + rbt.bucket_count(); + } + + siterator priv_invalid_local_it() const + { return priv_invalid_bucket()->end(); } + + siterator priv_begin(size_type &bucket_num) const + { return priv_begin(bucket_num, cache_begin_t()); } + + siterator priv_begin(size_type &bucket_num, detail::bool_) const + { + size_type n = 0; + size_type buckets_len = this->priv_buckets_len(); + for (n = 0; n < buckets_len; ++n){ + bucket_type &b = this->priv_buckets()[n]; + if(!b.empty()){ + bucket_num = n; + return b.begin(); + } + } + bucket_num = n; + return priv_invalid_local_it(); + } + + siterator priv_begin(size_type &bucket_num, detail::bool_) const + { + bucket_num = this->bucket_hash_equal_.cached_begin_ - this->priv_buckets(); + if(this->bucket_hash_equal_.cached_begin_ == priv_invalid_bucket()){ + return priv_invalid_local_it(); + } + else{ + return this->bucket_hash_equal_.cached_begin_->begin(); + } + } + + void priv_initialize_cache() + { priv_initialize_cache(cache_begin_t()); } + + void priv_initialize_cache(detail::bool_) + { this->bucket_hash_equal_.cached_begin_ = priv_invalid_bucket(); } + + void priv_initialize_cache(detail::bool_) + {} + + void priv_insertion_update_cache(size_type insertion_bucket) + { priv_insertion_update_cache(insertion_bucket, cache_begin_t()); } + + void priv_insertion_update_cache(size_type insertion_bucket, detail::bool_) + { + bucket_ptr p = priv_buckets() + insertion_bucket; + if(p < this->bucket_hash_equal_.cached_begin_){ + this->bucket_hash_equal_.cached_begin_ = p; + } + } + + void priv_insertion_update_cache(size_type, detail::bool_) + {} + + void priv_erasure_update_cache(size_type first_bucket, size_type last_bucket) + { priv_erasure_update_cache(first_bucket, last_bucket, cache_begin_t()); } + + void priv_erasure_update_cache(size_type first_bucket_num, size_type last_bucket_num, detail::bool_) + { + //If the last bucket is the end, the cache must be updated + //to the last position if all + if(priv_get_cache_bucket_num() == first_bucket_num && + priv_buckets()[first_bucket_num].empty() ){ + priv_set_cache(priv_buckets() + last_bucket_num); + priv_erasure_update_cache(); + } + } + + void priv_erasure_update_cache(size_type, size_type, detail::bool_) + {} + + void priv_erasure_update_cache() + { priv_erasure_update_cache(cache_begin_t()); } + + void priv_erasure_update_cache(detail::bool_) + { + if(constant_time_size && !size()){ + priv_initialize_cache(); + } + else{ + size_type current_n = this->bucket_hash_equal_.cached_begin_ - priv_buckets(); + for( const size_type num_buckets = this->priv_buckets_len() + ; current_n < num_buckets + ; ++current_n, ++this->bucket_hash_equal_.cached_begin_){ + if(!this->bucket_hash_equal_.cached_begin_->empty()){ + return; + } + } + priv_initialize_cache(); + } + } + + void priv_erasure_update_cache(detail::bool_) + {} + + void priv_swap_cache(detail::bool_, hashtable_impl &other) + { + std::swap( this->bucket_hash_equal_.cached_begin_ + , other.bucket_hash_equal_.cached_begin_); + } + + void priv_swap_cache(detail::bool_, hashtable_impl &) + {} + + bucket_ptr priv_get_cache() + { return priv_get_cache(cache_begin_t()); } + + bucket_ptr priv_get_cache(detail::bool_) + { return this->bucket_hash_equal_.cached_begin_; } + + bucket_ptr priv_get_cache(detail::bool_) + { return this->priv_buckets(); } + + void priv_set_cache(bucket_ptr p) + { priv_set_cache(p, cache_begin_t()); } + + void priv_set_cache(bucket_ptr p, detail::bool_) + { this->bucket_hash_equal_.cached_begin_ = p; } + + void priv_set_cache(bucket_ptr, detail::bool_) + {} + + size_type priv_get_cache_bucket_num() + { return priv_get_cache_bucket_num(cache_begin_t()); } + + size_type priv_get_cache_bucket_num(detail::bool_) + { return this->bucket_hash_equal_.cached_begin_ - this->priv_buckets(); } + + size_type priv_get_cache_bucket_num(detail::bool_) + { return 0u; } + + void priv_clear_buckets() + { + this->priv_clear_buckets + ( priv_get_cache() + , this->priv_buckets_len() - (priv_get_cache() - priv_buckets())); + } + + void priv_initialize_buckets() + { + this->priv_clear_buckets + ( priv_buckets(), this->priv_buckets_len()); + } + + void priv_clear_buckets(bucket_ptr buckets_ptr, size_type buckets_len) { for(; buckets_len--; ++buckets_ptr){ if(safemode_or_autounlink){ + priv_clear_group_nodes(*buckets_ptr, optimize_multikey_t()); buckets_ptr->clear_and_dispose(detail::init_disposer()); } else{ buckets_ptr->clear(); } } + priv_initialize_cache(); } template siterator priv_find ( const KeyType &key, KeyHasher hash_func - , KeyValueEqual equal_func, size_type &bucket_number, size_type &h) const + , KeyValueEqual equal_func, size_type &bucket_number, std::size_t &h, siterator &previt) const { - bucket_number = from_hash_to_bucket((h = hash_func(key))); + bucket_number = priv_hash_to_bucket((h = hash_func(key))); if(constant_time_size && this->empty()){ - return invalid_local_it(this->get_real_bucket_traits()); + return priv_invalid_local_it(); } bucket_type &b = this->priv_buckets()[bucket_number]; - siterator it = b.begin(); + previt = b.before_begin(); + siterator it = previt; + ++it; while(it != b.end()){ - const value_type &v = - *this->get_real_value_traits().to_value_ptr(it.pointed_node()); + const value_type &v = priv_value_from_slist_node(it.pointed_node()); if(equal_func(key, v)){ return it; } + if(optimize_multikey){ + previt = bucket_type::s_iterator_to + (*priv_get_last_in_group(dcast_bucket_ptr(it.pointed_node()))); + it = previt; + } + else{ + previt = it; + } ++it; } - return invalid_local_it(this->get_real_bucket_traits()); + return priv_invalid_local_it(); } template @@ -1820,33 +2440,44 @@ class hashtable_impl , size_type &bucket_number_second , size_type &count) const { - size_type h; + std::size_t h; count = 0; + siterator prev; //Let's see if the element is present std::pair to_return - ( priv_find(key, hash_func, equal_func, bucket_number_first, h) - , invalid_local_it(this->get_real_bucket_traits())); + ( priv_find(key, hash_func, equal_func, bucket_number_first, h, prev) + , priv_invalid_local_it()); if(to_return.first == to_return.second){ bucket_number_second = bucket_number_first; return to_return; } - ++count; //If it's present, find the first that it's not equal in //the same bucket bucket_type &b = this->priv_buckets()[bucket_number_first]; siterator it = to_return.first; - ++it; - - while(it != b.end()){ - const value_type &v = - *this->get_real_value_traits().to_value_ptr(it.pointed_node()); - if(!equal_func(key, v)){ - to_return.second = it; + if(optimize_multikey){ + to_return.second = bucket_type::s_iterator_to + (*node_traits::get_next(priv_get_last_in_group + (dcast_bucket_ptr(it.pointed_node())))); + count = std::distance(it, to_return.second); + if(to_return.second != b.end()){ bucket_number_second = bucket_number_first; return to_return; } - ++it; + } + else{ ++count; + ++it; + while(it != b.end()){ + const value_type &v = priv_value_from_slist_node(it.pointed_node()); + if(!equal_func(key, v)){ + to_return.second = it; + bucket_number_second = bucket_number_first; + return to_return; + } + ++it; + ++count; + } } //If we reached the end, find the first, non-empty bucket @@ -1861,22 +2492,24 @@ class hashtable_impl } //Otherwise, return the end node - to_return.second = invalid_local_it(this->get_real_bucket_traits()); + to_return.second = priv_invalid_local_it(); return to_return; } /// @endcond }; /// @cond -template +template < class T + , bool UniqueKeys + , class O1 = none, class O2 = none + , class O3 = none, class O4 = none + , class O5 = none, class O6 = none + , class O7 = none, class O8 = none + > struct make_hashtable_opt { typedef typename pack_options - < uset_defaults, O1, O2, O3, O4, O5, O6, O7>::type packed_options; + < uset_defaults, O1, O2, O3, O4, O5, O6, O7, O8>::type packed_options; //Real value traits must be calculated from options typedef typename detail::get_value_traits @@ -1891,9 +2524,16 @@ struct make_hashtable_opt >::type real_value_traits; typedef typename packed_options::bucket_traits specified_bucket_traits; /// @endcond - //Real bucket traits must be calculated from options and calculated valute_traits - typedef typename get_slist_impl - ::type slist_impl; + + //Real bucket traits must be calculated from options and calculated value_traits + typedef typename detail::get_slist_impl + ::type + >::type slist_impl; + + typedef typename detail::reduced_slist_node_traits + ::type node_traits; + typedef typename detail::if_c< detail::is_same < specified_bucket_traits @@ -1905,12 +2545,14 @@ struct make_hashtable_opt typedef usetopt < value_traits + , UniqueKeys , typename packed_options::hash , typename packed_options::equal , typename packed_options::size_type , packed_options::constant_time_size , real_bucket_traits , packed_options::power_2_buckets + , packed_options::cache_begin > type; }; /// @endcond @@ -1923,7 +2565,7 @@ template template #endif struct make_hashtable @@ -1931,7 +2573,7 @@ struct make_hashtable /// @cond typedef hashtable_impl < typename make_hashtable_opt - ::type + ::type > implementation_defined; /// @endcond @@ -1939,12 +2581,12 @@ struct make_hashtable }; #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED -template +template class hashtable - : public make_hashtable::type + : public make_hashtable::type { typedef typename make_hashtable - ::type Base; + ::type Base; public: typedef typename Base::value_traits value_traits; diff --git a/include/boost/intrusive/intrusive_fwd.hpp b/include/boost/intrusive/intrusive_fwd.hpp index 9228bcb..c260187 100644 --- a/include/boost/intrusive/intrusive_fwd.hpp +++ b/include/boost/intrusive/intrusive_fwd.hpp @@ -283,8 +283,7 @@ template > class bs_set_member_hook; -//hash/unordered -//rbtree/set/multiset +//hashtable/unordered_set/unordered_multiset template < class T , class O1 = none @@ -294,6 +293,7 @@ template , class O5 = none , class O6 = none , class O7 = none + , class O8 = none > class hashtable; @@ -306,6 +306,7 @@ template , class O5 = none , class O6 = none , class O7 = none + , class O8 = none > class unordered_set; @@ -318,6 +319,7 @@ template , class O5 = none , class O6 = none , class O7 = none + , class O8 = none > class unordered_multiset; diff --git a/include/boost/intrusive/linear_slist_algorithms.hpp b/include/boost/intrusive/linear_slist_algorithms.hpp index 681c13b..9f1f599 100644 --- a/include/boost/intrusive/linear_slist_algorithms.hpp +++ b/include/boost/intrusive/linear_slist_algorithms.hpp @@ -136,7 +136,7 @@ class linear_slist_algorithms //! //! Throws: Nothing. static void init_header(node_ptr this_node) - { NodeTraits::set_next(this_node, 0); } + { NodeTraits::set_next(this_node, node_ptr(0)); } //! Requires: this_node and prev_init_node must be in the same linear list. //! @@ -195,7 +195,7 @@ class linear_slist_algorithms //! Complexity: This function is linear to the contained elements. static node_ptr reverse(node_ptr p) { - if(!p) return 0; + if(!p) return node_ptr(0); node_ptr i = NodeTraits::get_next(p); node_ptr first(p); while(i){ @@ -218,7 +218,7 @@ class linear_slist_algorithms //! Complexity: Linear to the number of elements plus the number moved positions. static std::pair move_first_n_backwards(node_ptr p, std::size_t n) { - std::pair ret(0, 0); + std::pair ret(node_ptr(0), node_ptr(0)); //Null shift, or count() == 0 or 1, nothing to do if(!n || !p || !NodeTraits::get_next(p)){ return ret; @@ -252,12 +252,12 @@ class linear_slist_algorithms //If the p has not been found in the previous loop, find it //starting in the new first node and unlink it if(!end_found){ - old_last = base_t::get_previous_node(first, 0); + old_last = base_t::get_previous_node(first, node_ptr(0)); } //Now link p after the new last node NodeTraits::set_next(old_last, p); - NodeTraits::set_next(new_last, 0); + NodeTraits::set_next(new_last, node_ptr(0)); ret.first = first; ret.second = new_last; return ret; @@ -273,7 +273,7 @@ class linear_slist_algorithms //! Complexity: Linear to the number of elements plus the number moved positions. static std::pair move_first_n_forward(node_ptr p, std::size_t n) { - std::pair ret(0, 0); + std::pair ret(node_ptr(0), node_ptr(0)); //Null shift, or count() == 0 or 1, nothing to do if(!n || !p || !NodeTraits::get_next(p)) return ret; @@ -311,7 +311,7 @@ class linear_slist_algorithms node_ptr new_first(node_traits::get_next(new_last)); //Now put the old beginning after the old end NodeTraits::set_next(old_last, p); - NodeTraits::set_next(new_last, 0); + NodeTraits::set_next(new_last, node_ptr(0)); ret.first = new_first; ret.second = new_last; return ret; diff --git a/include/boost/intrusive/list.hpp b/include/boost/intrusive/list.hpp index 5968dd2..ca7f7f6 100644 --- a/include/boost/intrusive/list.hpp +++ b/include/boost/intrusive/list.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -732,17 +732,13 @@ class list_impl void clone_from(const list_impl &src, Cloner cloner, Disposer disposer) { this->clear_and_dispose(disposer); - BOOST_INTRUSIVE_TRY{ - const_iterator b(src.begin()), e(src.end()); - for(; b != e; ++b){ - this->push_back(*cloner(*b)); - } + detail::exception_disposer + rollback(*this, disposer); + const_iterator b(src.begin()), e(src.end()); + for(; b != e; ++b){ + this->push_back(*cloner(*b)); } - BOOST_INTRUSIVE_CATCH(...){ - this->clear_and_dispose(disposer); - BOOST_INTRUSIVE_RETHROW; - } - BOOST_INTRUSIVE_CATCH_END + rollback.release(); } //! Requires: value must be an lvalue and p must be a valid iterator of *this. diff --git a/include/boost/intrusive/options.hpp b/include/boost/intrusive/options.hpp index ab1f3dc..2e55c58 100644 --- a/include/boost/intrusive/options.hpp +++ b/include/boost/intrusive/options.hpp @@ -406,6 +406,24 @@ struct store_hash /// @endcond }; +//!This option setter specifies if the unordered hook +//!should offer room to store another link to another node +//!with the same key. +//!Storing this link will speed up lookups and insertions on +//!unordered_multiset containers with a great number of elements +//!with the same key. +template +struct optimize_multikey +{ +/// @cond + template + struct pack : Base + { + static const bool optimize_multikey = Enabled; + }; +/// @endcond +}; + //!This option setter specifies if the bucket array will be always power of two. //!This allows using masks instead of the default modulo operation to determine //!the bucket number from the hash value, leading to better performance. @@ -423,6 +441,22 @@ struct power_2_buckets /// @endcond }; +//!This option setter specifies if the container will cache a pointer to the first +//!non-empty bucket so that begin() is always constant-time. +//!This is specially helpful when we can have containers with a few elements +//!but with big bucket arrays (that is, hashtables with low load factors). +template +struct cache_begin +{ +/// @cond + template + struct pack : Base + { + static const bool cache_begin = Enabled; + }; +/// @endcond +}; + /// @cond template @@ -500,6 +534,7 @@ struct hook_defaults , optimize_size , store_hash , linear + , optimize_multikey >::type {}; diff --git a/include/boost/intrusive/pointer_plus_2_bits.hpp b/include/boost/intrusive/pointer_plus_2_bits.hpp deleted file mode 100644 index c7d9592..0000000 --- a/include/boost/intrusive/pointer_plus_2_bits.hpp +++ /dev/null @@ -1,82 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// (C) Copyright Ion Gaztanaga 2007 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// See http://www.boost.org/libs/intrusive for documentation. -// -///////////////////////////////////////////////////////////////////////////// - -#ifndef BOOST_INTRUSIVE_POINTER_PLUS_2_BIT_HPP -#define BOOST_INTRUSIVE_POINTER_PLUS_2_BIT_HPP - -namespace boost { -namespace intrusive { - -//!This trait class is used to know if a pointer -//!can embed 2 extra bits of information if -//!it's going to be used to point to objects -//!with an alignment of "Alignment" bytes. -template -struct has_pointer_plus_2_bits -{ - static const bool value = false; -}; - -//!This is an specialization for raw pointers. -//!Raw pointers can embed two extra bits in the lower bits -//!if the alignment is multiple of 4. -template -struct has_pointer_plus_2_bits -{ - static const bool value = (N % 4u == 0); -}; - -//!This is class that is supposed to have static methods -//!to embed 2 extra bits of information in a pointer. -//! -//!This is a declaration and there is no default implementation, -//!because operations to embed bits change with every pointer type. -//! -//!An implementation that detects that a pointer type whose -//!has_pointer_plus_2_bits<>::value is non-zero can make use of these -//!operations to embed bits in the pointer. -template -struct pointer_plus_2_bits -{ - static const bool value = false; -}; - -//!This is the specialization to embed 2 extra bits of information -//!in a raw pointer. Extra bits are stored in the lower bits of the pointer. -template -struct pointer_plus_2_bits -{ - typedef T* pointer; - - static pointer get_pointer(pointer n) - { return pointer(std::size_t(n) & ~std::size_t(3u)); } - - static void set_pointer(pointer &n, pointer p) - { - assert(0 == (std::size_t(p) & std::size_t(3u))); - n = pointer(std::size_t(p) | (std::size_t(n) & std::size_t(3u))); - } - - static std::size_t get_bits(pointer n) - { return (std::size_t(n) & std::size_t(3u)); } - - static void set_bits(pointer &n, std::size_t c) - { - assert(c < 4); - n = pointer(std::size_t(get_pointer(n)) | c); - } -}; - -} //namespace intrusive -} //namespace boost - -#endif //BOOST_INTRUSIVE_POINTER_PLUS_2_BIT_HPP diff --git a/include/boost/intrusive/pointer_plus_bit.hpp b/include/boost/intrusive/pointer_plus_bit.hpp deleted file mode 100644 index 863f5f8..0000000 --- a/include/boost/intrusive/pointer_plus_bit.hpp +++ /dev/null @@ -1,78 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// (C) Copyright Ion Gaztanaga 2007 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// See http://www.boost.org/libs/intrusive for documentation. -// -///////////////////////////////////////////////////////////////////////////// - -#ifndef BOOST_INTRUSIVE_POINTER_PLUS_BIT_HPP -#define BOOST_INTRUSIVE_POINTER_PLUS_BIT_HPP - -namespace boost { -namespace intrusive { - -//!This trait class is used to know if a pointer -//!can embed an extra bit of information if -//!it's going to be used to point to objects -//!with an alignment of "Alignment" bytes. -template -struct has_pointer_plus_bit -{ - static const bool value = false; -}; - -//!This is an specialization for raw pointers. -//!Raw pointers can embed an extra bit in the lower bit -//!if the alignment is multiple of 2. -template -struct has_pointer_plus_bit -{ - static const bool value = (N % 2u == 0); -}; - -//!This is class that is supposed to have static methods -//!to embed an extra bit of information in a pointer. -//!This is a declaration and there is no default implementation, -//!because operations to embed the bit change with every pointer type. -//! -//!An implementation that detects that a pointer type whose -//!has_pointer_plus_bit<>::value is non-zero can make use of these -//!operations to embed the bit in the pointer. -template -struct pointer_plus_bit -{ - static const bool value = false; -}; - -//!This is the specialization to embed an extra bit of information -//!in a raw pointer. The extra bit is stored in the lower bit of the pointer. -template -struct pointer_plus_bit -{ - typedef T* pointer; - - static pointer get_pointer(pointer n) - { return pointer(std::size_t(n) & ~std::size_t(1u)); } - - static void set_pointer(pointer &n, pointer p) - { - assert(0 == (std::size_t(p) & std::size_t(1u))); - n = pointer(std::size_t(p) | (std::size_t(n) & std::size_t(1u))); - } - - static bool get_bit(pointer n) - { return (std::size_t(n) & std::size_t(1u)) != 0; } - - static void set_bit(pointer &n, bool c) - { n = pointer(std::size_t(get_pointer(n)) | std::size_t(c)); } -}; - -} //namespace intrusive -} //namespace boost - -#endif //BOOST_INTRUSIVE_POINTER_PLUS_BIT_HPP diff --git a/include/boost/intrusive/pointer_plus_bits.hpp b/include/boost/intrusive/pointer_plus_bits.hpp new file mode 100644 index 0000000..7c77e90 --- /dev/null +++ b/include/boost/intrusive/pointer_plus_bits.hpp @@ -0,0 +1,81 @@ +///////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2007 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTRUSIVE_POINTER_PLUS_BITS_HPP +#define BOOST_INTRUSIVE_POINTER_PLUS_BITS_HPP + +#include //ls_zeros + +namespace boost { +namespace intrusive { + +//!This trait class is used to know if a pointer +//!can embed extra bits of information if +//!it's going to be used to point to objects +//!with an alignment of "Alignment" bytes. +template +struct max_pointer_plus_bits +{ + static const std::size_t value = 0; +}; + +//!This is an specialization for raw pointers. +//!Raw pointers can embed extra bits in the lower bits +//!if the alignment is multiple of 2pow(NumBits). +template +struct max_pointer_plus_bits +{ + static const std::size_t value = detail::ls_zeros::value; +}; + +//!This is class that is supposed to have static methods +//!to embed extra bits of information in a pointer. +//!This is a declaration and there is no default implementation, +//!because operations to embed the bits change with every pointer type. +//! +//!An implementation that detects that a pointer type whose +//!has_pointer_plus_bits<>::value is non-zero can make use of these +//!operations to embed the bits in the pointer. +template +struct pointer_plus_bits; + +//!This is the specialization to embed extra bits of information +//!in a raw pointer. The extra bits are stored in the lower bit of the pointer. +template +struct pointer_plus_bits +{ + static const std::size_t Mask = ((std::size_t(1u) << NumBits) - 1); + typedef T* pointer; + + static pointer get_pointer(pointer n) + { return pointer(std::size_t(n) & ~Mask); } + + static void set_pointer(pointer &n, pointer p) + { + assert(0 == (std::size_t(p) & Mask)); + n = pointer(std::size_t(p) | (std::size_t(n) & Mask)); + } + + static std::size_t get_bits(pointer n) + { return (std::size_t(n) & Mask); } + + static void set_bits(pointer &n, std::size_t c) + { + assert(c <= Mask); + n = pointer(std::size_t(get_pointer(n)) | c); + } +}; + +} //namespace intrusive +} //namespace boost + +#endif //BOOST_INTRUSIVE_POINTER_PLUS_BITS_HPP diff --git a/include/boost/intrusive/rbtree.hpp b/include/boost/intrusive/rbtree.hpp index 1682a35..4ec26ab 100644 --- a/include/boost/intrusive/rbtree.hpp +++ b/include/boost/intrusive/rbtree.hpp @@ -379,7 +379,7 @@ class rbtree_impl //! Precondition: end_iterator must be a valid end const_iterator //! of rbtree. //! - //! Effects: Returns a const reference to the rbtree associated to the end iterator + //! Effects: Returns a const reference to the rbtree associated to the iterator //! //! Throws: Nothing. //! @@ -387,6 +387,28 @@ class rbtree_impl static const rbtree_impl &container_from_end_iterator(const_iterator end_iterator) { return priv_container_from_end_iterator(end_iterator); } + //! Precondition: it must be a valid iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static rbtree_impl &container_from_iterator(iterator it) + { return priv_container_from_iterator(it); } + + //! Precondition: it must be a valid end const_iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the end iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const rbtree_impl &container_from_iterator(const_iterator it) + { return priv_container_from_iterator(it); } + //! Effects: Returns the value_compare object used by the tree. //! //! Complexity: Constant. @@ -1176,33 +1198,26 @@ class rbtree_impl static void init_node(reference value) { node_algorithms::init(value_traits::to_node_ptr(value)); } -/* - //! Effects: removes x from a tree of the appropriate type. It has no effect, - //! if x is not in such a tree. + //! Effects: removes "value" from the container. //! //! Throws: Nothing. //! - //! Complexity: Constant time. + //! Complexity: Logarithmic time. //! - //! Note: This static function is only usable with the "safe mode" - //! hook and non-constant time size lists. Otherwise, the user must use - //! the non-static "erase(reference )" member. If the user calls - //! this function with a non "safe mode" or constant time size list - //! a compilation error will be issued. - template - static void remove_node(T& value) + //! Note: This static function is only usable with non-constant + //! time size containers that have stateless comparison functors. + //! + //! If the user calls + //! this function with a constant time size container or stateful comparison + //! functor a compilation error will be issued. + static void remove_node(reference value) { - //This function is only usable for safe mode hooks and non-constant - //time lists. - //BOOST_STATIC_ASSERT((!(safemode_or_autounlink && constant_time_size))); BOOST_STATIC_ASSERT((!constant_time_size)); - BOOST_STATIC_ASSERT((boost::is_convertible::value)); node_ptr to_remove(value_traits::to_node_ptr(value)); - node_algorithms::unlink_and_rebalance(to_remove); + node_algorithms::unlink(to_remove); if(safemode_or_autounlink) node_algorithms::init(to_remove); } -*/ /// @cond private: @@ -1233,6 +1248,9 @@ class rbtree_impl rbtree_impl *rb = detail::parent_from_member(d, &rbtree_impl::data_); return *rb; } + + static rbtree_impl &priv_container_from_iterator(const const_iterator &it) + { return priv_container_from_end_iterator(it.end_iterator_from_it()); } }; #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED @@ -1426,6 +1444,12 @@ class rbtree static const rbtree &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static rbtree &container_from_it(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const rbtree &container_from_it(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif diff --git a/include/boost/intrusive/rbtree_algorithms.hpp b/include/boost/intrusive/rbtree_algorithms.hpp index c5fae2a..8699d4c 100644 --- a/include/boost/intrusive/rbtree_algorithms.hpp +++ b/include/boost/intrusive/rbtree_algorithms.hpp @@ -54,7 +54,6 @@ #include #include -#include #include #include @@ -695,6 +694,16 @@ class rbtree_algorithms rebalance_after_insertion(header, new_value); } + //! Requires: "n" must be a node inserted in a tree. + //! + //! Effects: Returns a pointer to the header node of the tree. + //! + //! Complexity: Logarithmic. + //! + //! Throws: Nothing. + static node_ptr get_header(node_ptr n) + { return tree_algorithms::get_header(n); } + /// @cond private: diff --git a/include/boost/intrusive/set.hpp b/include/boost/intrusive/set.hpp index 03c52ab..de0b6c7 100644 --- a/include/boost/intrusive/set.hpp +++ b/include/boost/intrusive/set.hpp @@ -226,7 +226,7 @@ class set_impl //! Precondition: end_iterator must be a valid end iterator //! of set. //! - //! Effects: Returns a const reference to the set associated to the end iterator + //! Effects: Returns a reference to the set associated to the end iterator //! //! Throws: Nothing. //! @@ -253,6 +253,34 @@ class set_impl , &set_impl::tree_); } + //! Precondition: it must be a valid iterator of set. + //! + //! Effects: Returns a reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static set_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &set_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of set. + //! + //! Effects: Returns a const reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const set_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &set_impl::tree_); + } + //! Effects: Returns the key_compare object used by the set. //! //! Complexity: Constant. @@ -1086,6 +1114,12 @@ class set static const set &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static set &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const set &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif @@ -1318,6 +1352,34 @@ class multiset_impl , &multiset_impl::tree_); } + //! Precondition: it must be a valid iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + static multiset_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &multiset_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + static const multiset_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &multiset_impl::tree_); + } + //! Effects: Returns the key_compare object used by the multiset. //! //! Complexity: Constant. @@ -1932,6 +1994,21 @@ class multiset_impl void replace_node(iterator replace_this, reference with_this) { tree_.replace_node(replace_this, with_this); } + //! Effects: removes "value" from the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic time. + //! + //! Note: This static function is only usable with non-constant + //! time size containers that have stateless comparison functors. + //! + //! If the user calls + //! this function with a constant time size container or stateful comparison + //! functor a compilation error will be issued. + static void remove_node(reference value) + { tree_type::remove_node(value); } + /// @cond friend bool operator==(const multiset_impl &x, const multiset_impl &y) { return x.tree_ == y.tree_; } @@ -2058,6 +2135,12 @@ class multiset static const multiset &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static multiset &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const multiset &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif diff --git a/include/boost/intrusive/sg_set.hpp b/include/boost/intrusive/sg_set.hpp index 2ef7799..66d461d 100644 --- a/include/boost/intrusive/sg_set.hpp +++ b/include/boost/intrusive/sg_set.hpp @@ -252,6 +252,34 @@ class sg_set_impl , &sg_set_impl::tree_); } + //! Precondition: it must be a valid iterator of set. + //! + //! Effects: Returns a reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static sg_set_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &sg_set_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of set. + //! + //! Effects: Returns a const reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const sg_set_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &sg_set_impl::tree_); + } + //! Effects: Returns the key_compare object used by the sg_set. //! //! Complexity: Constant. @@ -1124,6 +1152,12 @@ class sg_set static const sg_set &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static sg_set &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const sg_set &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif @@ -1356,6 +1390,34 @@ class sg_multiset_impl , &sg_multiset_impl::tree_); } + //! Precondition: it must be a valid iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + static sg_multiset_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &sg_multiset_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + static const sg_multiset_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &sg_multiset_impl::tree_); + } + //! Effects: Returns the key_compare object used by the sg_multiset. //! //! Complexity: Constant. @@ -2135,6 +2197,12 @@ class sg_multiset static const sg_multiset &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static sg_multiset &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const sg_multiset &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif diff --git a/include/boost/intrusive/sgtree.hpp b/include/boost/intrusive/sgtree.hpp index d62851f..44ba705 100644 --- a/include/boost/intrusive/sgtree.hpp +++ b/include/boost/intrusive/sgtree.hpp @@ -527,6 +527,28 @@ class sgtree_impl static const sgtree_impl &container_from_end_iterator(const_iterator end_iterator) { return priv_container_from_end_iterator(end_iterator); } + //! Precondition: it must be a valid iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static sgtree_impl &container_from_iterator(iterator it) + { return priv_container_from_iterator(it); } + + //! Precondition: it must be a valid end const_iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const sgtree_impl &container_from_iterator(const_iterator it) + { return priv_container_from_iterator(it); } + //! Effects: Returns the value_compare object used by the tree. //! //! Complexity: Constant. @@ -1442,6 +1464,9 @@ class sgtree_impl sgtree_impl *scapegoat = detail::parent_from_member(d, &sgtree_impl::data_); return *scapegoat; } + + static sgtree_impl &priv_container_from_iterator(const const_iterator &it) + { return priv_container_from_end_iterator(it.end_iterator_from_it()); } }; #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED diff --git a/include/boost/intrusive/sgtree_algorithms.hpp b/include/boost/intrusive/sgtree_algorithms.hpp index 4f2a9c7..d37f5b3 100644 --- a/include/boost/intrusive/sgtree_algorithms.hpp +++ b/include/boost/intrusive/sgtree_algorithms.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -640,6 +639,16 @@ class sgtree_algorithms static node_ptr rebalance_subtree(node_ptr old_root) { return tree_algorithms::rebalance_subtree(old_root); } + //! Requires: "n" must be a node inserted in a tree. + //! + //! Effects: Returns a pointer to the header node of the tree. + //! + //! Complexity: Logarithmic. + //! + //! Throws: Nothing. + static node_ptr get_header(node_ptr n) + { return tree_algorithms::get_header(n); } + /// @cond private: diff --git a/include/boost/intrusive/slist.hpp b/include/boost/intrusive/slist.hpp index 232ab7e..5a264e4 100644 --- a/include/boost/intrusive/slist.hpp +++ b/include/boost/intrusive/slist.hpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -183,10 +183,12 @@ class slist_impl BOOST_STATIC_ASSERT(!(cache_last && ((int)real_value_traits::link_mode == (int)auto_unlink))); node_ptr get_end_node() - { return node_ptr(linear ? 0 : this->get_root_node()); } + { return node_ptr(linear ? node_ptr(0) : this->get_root_node()); } const_node_ptr get_end_node() const - { return const_node_ptr(linear ? 0 : this->get_root_node()); } + { + return const_node_ptr + (linear ? const_node_ptr(0) : this->get_root_node()); } node_ptr get_root_node() { return node_ptr(&data_.root_plus_size_.root_); } @@ -203,13 +205,10 @@ class slist_impl void set_last_node(node_ptr n) { return this->set_last_node(n, detail::bool_()); } - node_ptr get_last_node(detail::bool_) + static node_ptr get_last_node(detail::bool_) { return node_ptr(0); } - const_node_ptr get_last_node(detail::bool_) const - { return const_node_ptr(0); } - - void set_last_node(node_ptr, detail::bool_) + static void set_last_node(node_ptr, detail::bool_) {} node_ptr get_last_node(detail::bool_) @@ -667,18 +666,14 @@ class slist_impl void clone_from(const slist_impl &src, Cloner cloner, Disposer disposer) { this->clear_and_dispose(disposer); - BOOST_INTRUSIVE_TRY{ - iterator prev(this->before_begin()); - const_iterator b(src.begin()), e(src.end()); - for(; b != e; ++b){ - prev = this->insert_after(prev, *cloner(*b)); - } + detail::exception_disposer + rollback(*this, disposer); + iterator prev(this->before_begin()); + const_iterator b(src.begin()), e(src.end()); + for(; b != e; ++b){ + prev = this->insert_after(prev, *cloner(*b)); } - BOOST_INTRUSIVE_CATCH(...){ - this->clear_and_dispose(disposer); - BOOST_INTRUSIVE_RETHROW; - } - BOOST_INTRUSIVE_CATCH_END + rollback.release(); } //! Requires: value must be an lvalue and prev_p must point to an element @@ -849,13 +844,36 @@ class slist_impl if(cache_last && (to_erase == this->get_last_node())){ this->set_last_node(prev_n); } - this->priv_size_traits().decrement(); if(safemode_or_autounlink) node_algorithms::init(to_erase); disposer(get_real_value_traits().to_value_ptr(to_erase)); + this->priv_size_traits().decrement(); return it; } + /// @cond + + template + static iterator s_erase_after_and_dispose(iterator prev, Disposer disposer) + { + BOOST_STATIC_ASSERT(((!cache_last)&&(!constant_time_size)&&(!stateful_value_traits))); + iterator it(prev); + ++it; + node_ptr to_erase(it.pointed_node()); + ++it; + node_ptr prev_n(prev.pointed_node()); + node_algorithms::unlink_after(prev_n); + if(safemode_or_autounlink) + node_algorithms::init(to_erase); + disposer(real_value_traits::to_value_ptr(to_erase)); + return it; + } + + static iterator s_erase_after(iterator prev) + { return s_erase_after_and_dispose(prev, detail::null_disposer()); } + + /// @endcond + //! Requires: Disposer::operator()(pointer) shouldn't throw. //! //! Effects: Erases the range (before_first, last) from diff --git a/include/boost/intrusive/splay_set.hpp b/include/boost/intrusive/splay_set.hpp index 82aa409..104063a 100644 --- a/include/boost/intrusive/splay_set.hpp +++ b/include/boost/intrusive/splay_set.hpp @@ -252,6 +252,34 @@ class splay_set_impl , &splay_set_impl::tree_); } + //! Precondition: it must be a valid iterator of set. + //! + //! Effects: Returns a reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + static splay_set_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &splay_set_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of set. + //! + //! Effects: Returns a const reference to the set associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const splay_set_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &splay_set_impl::tree_); + } + //! Effects: Returns the key_compare object used by the splay_set. //! //! Complexity: Constant. @@ -1161,6 +1189,12 @@ class splay_set static const splay_set &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static splay_set &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const splay_set &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif @@ -1393,6 +1427,34 @@ class splay_multiset_impl , &splay_multiset_impl::tree_); } + //! Precondition: it must be a valid iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static splay_multiset_impl &container_from_iterator(iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &splay_multiset_impl::tree_); + } + + //! Precondition: it must be a valid const_iterator of multiset. + //! + //! Effects: Returns a const reference to the multiset associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + static const splay_multiset_impl &container_from_iterator(const_iterator it) + { + return *detail::parent_from_member + ( &tree_type::container_from_iterator(it) + , &splay_multiset_impl::tree_); + } + //! Effects: Returns the key_compare object used by the splay_multiset. //! //! Complexity: Constant. @@ -2209,6 +2271,12 @@ class splay_multiset static const splay_multiset &container_from_end_iterator(const_iterator end_iterator) { return static_cast(Base::container_from_end_iterator(end_iterator)); } + + static splay_multiset &container_from_iterator(iterator it) + { return static_cast(Base::container_from_iterator(it)); } + + static const splay_multiset &container_from_iterator(const_iterator it) + { return static_cast(Base::container_from_iterator(it)); } }; #endif diff --git a/include/boost/intrusive/splaytree.hpp b/include/boost/intrusive/splaytree.hpp index d733f50..e356963 100644 --- a/include/boost/intrusive/splaytree.hpp +++ b/include/boost/intrusive/splaytree.hpp @@ -387,6 +387,28 @@ class splaytree_impl static const splaytree_impl &container_from_end_iterator(const_iterator end_iterator) { return priv_container_from_end_iterator(end_iterator); } + //! Precondition: it must be a valid iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static splaytree_impl &container_from_iterator(iterator it) + { return priv_container_from_iterator(it); } + + //! Precondition: it must be a valid end const_iterator + //! of rbtree. + //! + //! Effects: Returns a const reference to the tree associated to the iterator + //! + //! Throws: Nothing. + //! + //! Complexity: Logarithmic. + static const splaytree_impl &container_from_iterator(const_iterator it) + { return priv_container_from_iterator(it); } + //! Effects: Returns the value_compare object used by the tree. //! //! Complexity: Constant. @@ -1312,6 +1334,9 @@ class splaytree_impl splaytree_impl *rb = detail::parent_from_member(d, &splaytree_impl::data_); return *rb; } + + static splaytree_impl &priv_container_from_iterator(const const_iterator &it) + { return priv_container_from_end_iterator(it.end_iterator_from_it()); } }; #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED diff --git a/include/boost/intrusive/splaytree_algorithms.hpp b/include/boost/intrusive/splaytree_algorithms.hpp index 2fbcb92..f42cd3c 100644 --- a/include/boost/intrusive/splaytree_algorithms.hpp +++ b/include/boost/intrusive/splaytree_algorithms.hpp @@ -50,13 +50,48 @@ #include #include #include -#include #include #include namespace boost { namespace intrusive { +/// @cond +namespace detail { + +template +struct splaydown_rollback +{ + typedef typename NodeTraits::node_ptr node_ptr; + splaydown_rollback( const node_ptr *pcur_subtree, node_ptr header + , node_ptr leftmost , node_ptr rightmost) + : pcur_subtree_(pcur_subtree) , header_(header) + , leftmost_(leftmost) , rightmost_(rightmost) + {} + + void release() + { pcur_subtree_ = 0; } + + ~splaydown_rollback() + { + if(pcur_subtree_){ + //Exception can only be thrown by comp, but + //tree invariants still hold. *pcur_subtree is the current root + //so link it to the header. + NodeTraits::set_parent(*pcur_subtree_, header_); + NodeTraits::set_parent(header_, *pcur_subtree_); + //Recover leftmost/rightmost pointers + NodeTraits::set_left (header_, leftmost_); + NodeTraits::set_right(header_, rightmost_); + } + } + const node_ptr *pcur_subtree_; + node_ptr header_, leftmost_, rightmost_; +}; + +} //namespace detail { +/// @endcond + //! A splay tree is an implementation of a binary search tree. The tree is //! self balancing using the splay algorithm as described in //! @@ -656,7 +691,8 @@ class splaytree_algorithms node_ptr leftmost = NodeTraits::get_left(header); node_ptr rightmost = NodeTraits::get_right(header); - try{ + { + detail::splaydown_rollback rollback(&t, header, leftmost, rightmost); node_ptr null = header; node_ptr l = null; node_ptr r = null; @@ -712,18 +748,9 @@ class splaytree_algorithms } assemble(t, l, r, null); + rollback.release(); } - catch(...){ - //Exception can only be thrown by comp, but - //tree invariants still hold. t is the current root - //so link it to the header. - NodeTraits::set_parent(t, header); - NodeTraits::set_parent(header, t); - //Recover leftmost/rightmost pointers - NodeTraits::set_left (header, leftmost); - NodeTraits::set_right(header, rightmost); - throw; - } + //t is the current root NodeTraits::set_parent(header, t); NodeTraits::set_parent(t, header); @@ -755,6 +782,17 @@ class splaytree_algorithms static node_ptr rebalance_subtree(node_ptr old_root) { return tree_algorithms::rebalance_subtree(old_root); } + + //! Requires: "n" must be a node inserted in a tree. + //! + //! Effects: Returns a pointer to the header node of the tree. + //! + //! Complexity: Logarithmic. + //! + //! Throws: Nothing. + static node_ptr get_header(node_ptr n) + { return tree_algorithms::get_header(n); } + private: /// @cond diff --git a/include/boost/intrusive/unordered_set.hpp b/include/boost/intrusive/unordered_set.hpp index 780fb46..d35eef4 100644 --- a/include/boost/intrusive/unordered_set.hpp +++ b/include/boost/intrusive/unordered_set.hpp @@ -38,7 +38,8 @@ namespace intrusive { //! //! The container supports the following options: //! \c base_hook<>/member_hook<>/value_traits<>, -//! \c constant_time_size<>, \c size_type<>, \c hash<> and \c equal<> . +//! \c constant_time_size<>, \c size_type<>, \c hash<> and \c equal<> +//! \c bucket_traits<>, power_2_buckets<> and cache_begin<>. //! //! unordered_set only provides forward iterators but it provides 4 iterator types: //! iterator and const_iterator to navigate through the whole container and @@ -167,8 +168,8 @@ class unordered_set_impl //! Effects: Returns an iterator pointing to the beginning of the unordered_set. //! - //! Complexity: Amortized constant time. - //! Worst case (empty unordered_set): O(this->bucket_count()) + //! Complexity: Constant time if `cache_begin<>` is true. Amortized + //! constant time with worst case (empty unordered_set) O(this->bucket_count()) //! //! Throws: Nothing. iterator begin() @@ -177,8 +178,8 @@ class unordered_set_impl //! Effects: Returns a const_iterator pointing to the beginning //! of the unordered_set. //! - //! Complexity: Amortized constant time. - //! Worst case (empty unordered_set): O(this->bucket_count()) + //! Complexity: Constant time if `cache_begin<>` is true. Amortized + //! constant time with worst case (empty unordered_set) O(this->bucket_count()) //! //! Throws: Nothing. const_iterator begin() const @@ -187,8 +188,8 @@ class unordered_set_impl //! Effects: Returns a const_iterator pointing to the beginning //! of the unordered_set. //! - //! Complexity: Amortized constant time. - //! Worst case (empty unordered_set): O(this->bucket_count()) + //! Complexity: Constant time if `cache_begin<>` is true. Amortized + //! constant time with worst case (empty unordered_set) O(this->bucket_count()) //! //! Throws: Nothing. const_iterator cbegin() const @@ -236,8 +237,8 @@ class unordered_set_impl //! Effects: Returns true is the container is empty. //! - //! Complexity: if constant-time size option is disabled, average constant time - //! (worst case, with empty() == true): O(this->bucket_count()). + //! Complexity: if constant-time size and cache_last options are disabled, + //! average constant time (worst case, with empty() == true: O(this->bucket_count()). //! Otherwise constant. //! //! Throws: Nothing. @@ -959,7 +960,7 @@ template template #endif struct make_unordered_set @@ -967,19 +968,19 @@ struct make_unordered_set /// @cond typedef unordered_set_impl < typename make_hashtable_opt - ::type + ::type > implementation_defined; /// @endcond typedef implementation_defined type; }; #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED -template +template class unordered_set - : public make_unordered_set::type + : public make_unordered_set::type { typedef typename make_unordered_set - ::type Base; + ::type Base; //Assert if passed value traits are compatible with the type BOOST_STATIC_ASSERT((detail::is_same::value)); @@ -1032,7 +1033,8 @@ class unordered_set //! //! The container supports the following options: //! \c base_hook<>/member_hook<>/value_traits<>, -//! \c constant_time_size<>, \c size_type<>, \c hash<> and \c equal<> . +//! \c constant_time_size<>, \c size_type<>, \c hash<> and \c equal<> +//! \c bucket_traits<>, power_2_buckets<> and cache_begin<>. //! //! unordered_multiset only provides forward iterators but it provides 4 iterator types: //! iterator and const_iterator to navigate through the whole container and @@ -1161,8 +1163,8 @@ class unordered_multiset_impl //! Effects: Returns an iterator pointing to the beginning of the unordered_multiset. //! - //! Complexity: Amortized constant time. - //! Worst case (empty unordered_multiset): O(this->bucket_count()) + //! Complexity: Constant time if `cache_begin<>` is true. Amortized + //! constant time with worst case (empty unordered_set) O(this->bucket_count()) //! //! Throws: Nothing. iterator begin() @@ -1171,8 +1173,8 @@ class unordered_multiset_impl //! Effects: Returns a const_iterator pointing to the beginning //! of the unordered_multiset. //! - //! Complexity: Amortized constant time. - //! Worst case (empty unordered_multiset): O(this->bucket_count()) + //! Complexity: Constant time if `cache_begin<>` is true. Amortized + //! constant time with worst case (empty unordered_set) O(this->bucket_count()) //! //! Throws: Nothing. const_iterator begin() const @@ -1181,8 +1183,8 @@ class unordered_multiset_impl //! Effects: Returns a const_iterator pointing to the beginning //! of the unordered_multiset. //! - //! Complexity: Amortized constant time. - //! Worst case (empty unordered_multiset): O(this->bucket_count()) + //! Complexity: Constant time if `cache_begin<>` is true. Amortized + //! constant time with worst case (empty unordered_set) O(this->bucket_count()) //! //! Throws: Nothing. const_iterator cbegin() const @@ -1230,8 +1232,8 @@ class unordered_multiset_impl //! Effects: Returns true is the container is empty. //! - //! Complexity: if constant-time size option is disabled, average constant time - //! (worst case, with empty() == true): O(this->bucket_count()). + //! Complexity: if constant-time size and cache_last options are disabled, + //! average constant time (worst case, with empty() == true: O(this->bucket_count()). //! Otherwise constant. //! //! Throws: Nothing. @@ -1891,7 +1893,7 @@ template template #endif struct make_unordered_multiset @@ -1899,19 +1901,19 @@ struct make_unordered_multiset /// @cond typedef unordered_multiset_impl < typename make_hashtable_opt - ::type + ::type > implementation_defined; /// @endcond typedef implementation_defined type; }; #ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED -template +template class unordered_multiset - : public make_unordered_multiset::type + : public make_unordered_multiset::type { typedef typename make_unordered_multiset - ::type Base; + ::type Base; //Assert if passed value traits are compatible with the type BOOST_STATIC_ASSERT((detail::is_same::value)); diff --git a/include/boost/intrusive/unordered_set_hook.hpp b/include/boost/intrusive/unordered_set_hook.hpp index ffac564..4920534 100644 --- a/include/boost/intrusive/unordered_set_hook.hpp +++ b/include/boost/intrusive/unordered_set_hook.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -26,35 +27,69 @@ namespace intrusive { /// @cond -template -struct slist_node_plus_hash +template +struct unordered_node + : public slist_node { typedef typename boost::pointer_to_other - ::type node_ptr; - node_ptr next_; + < VoidPointer + , unordered_node + >::type node_ptr; +// node_ptr next_; + node_ptr prev_in_group_; std::size_t hash_; }; -// slist_node_traits can be used with circular_slist_algorithms and supplies -// a slist_node holding the pointers needed for a singly-linked list -// it is used by slist_base_hook and slist_member_hook template -struct slist_node_traits_plus_hash +struct unordered_node + : public slist_node { - typedef slist_node_plus_hash node; + typedef typename boost::pointer_to_other + < VoidPointer + , unordered_node + >::type node_ptr; +// node_ptr next_; + node_ptr prev_in_group_; +}; + +template +struct unordered_node + : public slist_node +{ + typedef typename boost::pointer_to_other + < VoidPointer + , unordered_node + >::type node_ptr; +// node_ptr next_; + std::size_t hash_; +}; + +template +struct unordered_node_traits + : public slist_node_traits +{ + typedef slist_node_traits reduced_slist_node_traits; + typedef unordered_node node; typedef typename boost::pointer_to_other ::type node_ptr; typedef typename boost::pointer_to_other ::type const_node_ptr; - static const bool store_hash = true; + static const bool store_hash = StoreHash; + static const bool optimize_multikey = OptimizeMultiKey; static node_ptr get_next(const_node_ptr n) - { return n->next_; } + { return node_ptr(&static_cast(*n->next_)); } static void set_next(node_ptr n, node_ptr next) { n->next_ = next; } + static node_ptr get_prev_in_group(const_node_ptr n) + { return n->prev_in_group_; } + + static void set_prev_in_group(node_ptr n, node_ptr prev) + { n->prev_in_group_ = prev; } + static std::size_t get_hash(const_node_ptr n) { return n->hash_; } @@ -62,15 +97,68 @@ struct slist_node_traits_plus_hash { n->hash_ = h; } }; -template +template +struct unordered_group_node_traits +{ + typedef Node node; + typedef typename boost::pointer_to_other + ::type node_ptr; + typedef typename boost::pointer_to_other + ::type const_node_ptr; + + static node_ptr get_next(const_node_ptr n) + { return n->prev_in_group_; } + + static void set_next(node_ptr n, node_ptr next) + { n->prev_in_group_ = next; } +}; + +template +struct unordered_algorithms + : public circular_slist_algorithms +{ + typedef circular_slist_algorithms base_type; + typedef unordered_group_node_traits + < typename boost::pointer_to_other + < typename NodeTraits::node_ptr + , void + >::type + , typename NodeTraits::node + > group_traits; + typedef circular_slist_algorithms group_algorithms; + + static void init(typename base_type::node_ptr n) + { + base_type::init(n); + group_algorithms::init(n); + } + + static void init_header(typename base_type::node_ptr n) + { + base_type::init_header(n); + group_algorithms::init_header(n); + } + + static void unlink(typename base_type::node_ptr n) + { + base_type::unlink(n); + group_algorithms::unlink(n); + } +}; + +template struct get_uset_node_algo { typedef typename detail::if_c - < StoreHash - , slist_node_traits_plus_hash + < (StoreHash || OptimizeMultiKey) + , unordered_node_traits , slist_node_traits >::type node_traits_type; - typedef circular_slist_algorithms type; + typedef typename detail::if_c + < OptimizeMultiKey + , unordered_algorithms + , circular_slist_algorithms + >::type type; }; /// @endcond @@ -90,6 +178,7 @@ struct make_unordered_set_base_hook typedef detail::generic_hook < get_uset_node_algo , typename packed_options::tag , packed_options::link_mode @@ -104,7 +193,7 @@ struct make_unordered_set_base_hook //! the unordered_set/unordered_multi_set and provides an appropriate value_traits class for unordered_set/unordered_multi_set. //! //! The hook admits the following options: \c tag<>, \c void_pointer<>, -//! \c link_mode<> and \c store_hash<>. +//! \c link_mode<>, \c store_hash<> and \c optimize_multikey<>. //! //! \c tag<> defines a tag to identify the node. //! The same tag value can be used in different classes, but if a class is @@ -119,6 +208,10 @@ struct make_unordered_set_base_hook //! //! \c store_hash<> will tell the hook to store the hash of the value //! to speed up rehashings. +//! +//! \c optimize_multikey<> will tell the hook to store a link to form a group +//! with other value with the same value to speed up searches and insertions +//! in unordered_multisets with a great number of with equivalent keys. #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED template #else @@ -211,6 +304,7 @@ struct make_unordered_set_member_hook typedef detail::generic_hook < get_uset_node_algo< typename packed_options::void_pointer , packed_options::store_hash + , packed_options::optimize_multikey > , member_tag , packed_options::link_mode diff --git a/index.html b/index.html index 23984f2..8c018f9 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,6 @@ Automatic redirection failed, please go to -../../doc/html/intrusive.html +../../doc/html/intrusive diff --git a/perf/perf_list.cpp b/perf/perf_list.cpp index 99f3b25..5bf8012 100644 --- a/perf/perf_list.cpp +++ b/perf/perf_list.cpp @@ -23,7 +23,7 @@ using namespace boost::posix_time; //[perf_list_value_type //Iteration and element count defines const int NumIter = 100; -const int NumElements = 100000; +const int NumElements = 50000; using namespace boost::intrusive; diff --git a/proj/vc7ide/_intrusivelib/_intrusivelib.vcproj b/proj/vc7ide/_intrusivelib/_intrusivelib.vcproj index 587661c..6dd0c3f 100644 --- a/proj/vc7ide/_intrusivelib/_intrusivelib.vcproj +++ b/proj/vc7ide/_intrusivelib/_intrusivelib.vcproj @@ -154,10 +154,7 @@ RelativePath="..\..\..\..\..\boost\intrusive\options.hpp"> - - + RelativePath="..\..\..\..\..\boost\intrusive\pointer_plus_bits.hpp"> @@ -240,9 +237,6 @@ - - diff --git a/proj/vc7ide/to-do.txt b/proj/vc7ide/to-do.txt new file mode 100644 index 0000000..7e79f70 --- /dev/null +++ b/proj/vc7ide/to-do.txt @@ -0,0 +1,11 @@ +Add resize() to list +Implement incremental hashing +Add invariants to slist and test them after every operation +Create a generic hook that will work with all containers +Take advantage of store_hash in lookups: Instead of comparing objects, just + compare hashes. This will improve equality for expensive objects. + +Improve the use of cache_begin to unordered containers: +-> Speed up rehash + + diff --git a/test/avl_set_test.cpp b/test/avl_set_test.cpp index 1f7150e..d53b56e 100644 --- a/test/avl_set_test.cpp +++ b/test/avl_set_test.cpp @@ -108,7 +108,6 @@ class test_main_template int main( int, char* [] ) { - test_main_template()(); test_main_template, false>()(); test_main_template()(); diff --git a/test/generic_assoc_test.hpp b/test/generic_assoc_test.hpp index 624fd60..5ee4720 100644 --- a/test/generic_assoc_test.hpp +++ b/test/generic_assoc_test.hpp @@ -52,8 +52,37 @@ struct test_generic_assoc static void test_rebalance(std::vector& values); static void test_rebalance(std::vector& values, boost::intrusive::detail::true_type); static void test_rebalance(std::vector& values, boost::intrusive::detail::false_type); + static void test_container_from_iterator(std::vector& values); }; +template class ContainerDefiner> +void test_generic_assoc:: + test_container_from_iterator(std::vector& values) +{ + typedef typename ContainerDefiner + < value_type + , value_traits + , constant_time_size + >::type assoc_type; + + assoc_type testset(values.begin(), values.end()); + typedef typename assoc_type::iterator it_type; + typedef typename assoc_type::const_iterator cit_type; + typedef typename assoc_type::size_type sz_type; + sz_type sz = testset.size(); + for(it_type b(testset.begin()), e(testset.end()); b != e; ++b) + { + assoc_type &s = assoc_type::container_from_iterator(b); + const assoc_type &cs = assoc_type::container_from_iterator(cit_type(b)); + BOOST_TEST(&s == &cs); + BOOST_TEST(&s == &testset); + s.erase(b); + BOOST_TEST(testset.size() == (sz-1)); + s.insert(*b); + BOOST_TEST(testset.size() == sz); + } +} + template class ContainerDefiner> void test_generic_assoc::test_insert_erase_burst() { @@ -103,12 +132,14 @@ void test_generic_assoc::test_insert_erase_burst( template class ContainerDefiner> void test_generic_assoc::test_all(std::vector& values) { + typedef typename ValueTraits::value_type value_type; test_clone(values); test_container_from_end(values); test_splay_up(values); test_splay_down(values); test_rebalance(values); test_insert_erase_burst(); + test_container_from_iterator(values); } template class ContainerDefiner> diff --git a/test/generic_set_test.hpp b/test/generic_set_test.hpp index db14008..0498759 100644 --- a/test/generic_set_test.hpp +++ b/test/generic_set_test.hpp @@ -35,6 +35,7 @@ struct test_generic_set static void test_impl(); }; + template class ContainerDefiner> void test_generic_set::test_all() { diff --git a/test/itestvalue.hpp b/test/itestvalue.hpp index d6338bb..b45bdd8 100644 --- a/test/itestvalue.hpp +++ b/test/itestvalue.hpp @@ -126,20 +126,30 @@ template struct uset_auto_base_hook_type { typedef unordered_set_base_hook - < link_mode, void_pointer - , tag, store_hash > type; + < link_mode + , void_pointer + , tag + , store_hash + > type; }; template struct uset_member_hook_type -{ typedef unordered_set_member_hook > type; }; +{ + typedef unordered_set_member_hook + < void_pointer + , optimize_multikey + > type; +}; template struct uset_auto_member_hook_type { typedef unordered_set_member_hook < link_mode, void_pointer - , store_hash > type; + , store_hash + , optimize_multikey + > type; }; template @@ -318,6 +328,49 @@ struct testvalue slist_auto_node_.swap_nodes(other.slist_auto_node_); } + bool is_linked() const + { + //Set + return set_base_hook_t::is_linked() || + set_auto_base_hook_t::is_linked() || + set_node_.is_linked() || + set_auto_node_.is_linked() || + + //SplaySet + splay_set_base_hook_t::is_linked() || + splay_set_auto_base_hook_t::is_linked() || + splay_set_node_.is_linked() || + splay_set_auto_node_.is_linked() || + + //ScapeoatSet + bs_set_base_hook_t::is_linked() || + sg_set_node_.is_linked() || + + //AvlSet + avl_set_base_hook_t::is_linked() || + avl_set_auto_base_hook_t::is_linked() || + avl_set_node_.is_linked() || + avl_set_auto_node_.is_linked() || + + //Unordered set + unordered_set_base_hook_t::is_linked() || + unordered_set_auto_base_hook_t::is_linked() || + unordered_set_node_.is_linked() || + unordered_set_auto_node_.is_linked() || + + //List + list_base_hook_t::is_linked() || + list_auto_base_hook_t::is_linked() || + list_node_.is_linked() || + list_auto_node_.is_linked() || + + //Slist + slist_base_hook_t::is_linked() || + slist_auto_base_hook_t::is_linked() || + slist_node_.is_linked() || + slist_auto_node_.is_linked(); + } + ~testvalue() {} diff --git a/test/smart_ptr.hpp b/test/smart_ptr.hpp index 6ed0d07..dd945a8 100644 --- a/test/smart_ptr.hpp +++ b/test/smart_ptr.hpp @@ -12,8 +12,7 @@ #define BOOST_INTRUSIVE_SMART_PTR_HPP #include -#include -#include +#include #if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -109,7 +108,7 @@ class smart_ptr public: //Public Functions //!Constructor from raw pointer (allows "0" pointer conversion). Never throws. - smart_ptr(pointer ptr = 0) + explicit smart_ptr(pointer ptr = 0) : m_ptr(ptr) {} @@ -351,67 +350,34 @@ namespace boost{ //for intrusive containers, saving space namespace intrusive { -template -struct has_pointer_plus_bit, N> +template +struct max_pointer_plus_bits, Alignment> { - static const bool value = has_pointer_plus_bit::value; + static const std::size_t value = max_pointer_plus_bits::value; }; -//Specialization -template -struct pointer_plus_bit > +template +struct pointer_plus_bits, NumBits> { typedef smart_ptr pointer; static pointer get_pointer(const pointer &n) - { return pointer_plus_bit::get_pointer(n.get()); } + { return pointer_plus_bits::get_pointer(n.get()); } static void set_pointer(pointer &n, pointer p) { T *raw_n = n.get(); - pointer_plus_bit::set_pointer(raw_n, p.get()); - n = raw_n; - } - - static bool get_bit(const pointer &n) - { return pointer_plus_bit::get_bit(n.get()); } - - static void set_bit(pointer &n, bool c) - { - T *raw_n = n.get(); - pointer_plus_bit::set_bit(raw_n, c); - n = raw_n; - } -}; - -template -struct has_pointer_plus_2_bits, N> -{ - static const bool value = has_pointer_plus_2_bits::value; -}; - -template -struct pointer_plus_2_bits > -{ - typedef smart_ptr pointer; - - static pointer get_pointer(const pointer &n) - { return pointer_plus_2_bits::get_pointer(n.get()); } - - static void set_pointer(pointer &n, pointer p) - { - T *raw_n = n.get(); - pointer_plus_2_bits::set_pointer(raw_n, p.get()); + pointer_plus_bits::set_pointer(raw_n, p.get()); n = raw_n; } static std::size_t get_bits(const pointer &n) - { return pointer_plus_2_bits::get_bits(n.get()); } + { return pointer_plus_bits::get_bits(n.get()); } static void set_bits(pointer &n, std::size_t c) { T *raw_n = n.get(); - pointer_plus_2_bits::set_bits(raw_n, c); + pointer_plus_bits::set_bits(raw_n, c); n = raw_n; } }; diff --git a/test/unordered_multiset_test.cpp b/test/unordered_multiset_test.cpp index ceb30ba..dee32be 100644 --- a/test/unordered_multiset_test.cpp +++ b/test/unordered_multiset_test.cpp @@ -18,6 +18,7 @@ #include "smart_ptr.hpp" #include "common_functors.hpp" #include +#include //std::sort std::find #include #include #include "test_macros.hpp" @@ -27,7 +28,7 @@ using namespace boost::intrusive; static const std::size_t BucketSize = 11; -template +template struct test_unordered_multiset { typedef typename ValueTraits::value_type value_type; @@ -41,14 +42,15 @@ struct test_unordered_multiset static void test_clone(std::vector& values); }; -template -void test_unordered_multiset::test_all (std::vector& values) +template +void test_unordered_multiset::test_all (std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset - , constant_time_size + , cache_begin > unordered_multiset_type; { typedef typename unordered_multiset_type::bucket_traits bucket_traits; @@ -76,14 +78,15 @@ void test_unordered_multiset::test_all (std::vector -void test_unordered_multiset::test_impl() +template +void test_unordered_multiset::test_impl() { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; @@ -106,14 +109,15 @@ void test_unordered_multiset::test_impl() } //test: constructor, iterator, clear, reverse_iterator, front, back, size: -template -void test_unordered_multiset::test_sort(std::vector& values) +template +void test_unordered_multiset::test_sort(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; @@ -127,104 +131,187 @@ void test_unordered_multiset::test_sort(std::vector -void test_unordered_multiset::test_insert(std::vector& values) +template +void test_unordered_multiset::test_insert(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; + typedef typename unordered_multiset_type::iterator iterator; + { + typename unordered_multiset_type::bucket_type buckets [BucketSize]; + unordered_multiset_type testset(bucket_traits(buckets, BucketSize)); - typename unordered_multiset_type::bucket_type buckets [BucketSize]; - unordered_multiset_type testset(bucket_traits(buckets, BucketSize)); + testset.insert(&values[0] + 2, &values[0] + 5); - testset.insert(&values[0] + 2, &values[0] + 5); + const unordered_multiset_type& const_testset = testset; + { int init_values [] = { 1, 4, 5 }; + TEST_INTRUSIVE_SEQUENCE( init_values, const_testset.begin() ); } - const unordered_multiset_type& const_testset = testset; - { int init_values [] = { 1, 4, 5 }; - TEST_INTRUSIVE_SEQUENCE( init_values, const_testset.begin() ); } + typename unordered_multiset_type::iterator i = testset.begin(); + BOOST_TEST (i->value_ == 1); - typename unordered_multiset_type::iterator i = testset.begin(); - BOOST_TEST (i->value_ == 1); + i = testset.insert (values[0]); + BOOST_TEST (&*i == &values[0]); + + i = testset.iterator_to (values[2]); + BOOST_TEST (&*i == &values[2]); + testset.erase(i); - i = testset.insert (values[0]); - BOOST_TEST (&*i == &values[0]); - - i = testset.iterator_to (values[2]); - BOOST_TEST (&*i == &values[2]); - testset.erase(i); + { int init_values [] = { 1, 3, 5 }; + TEST_INTRUSIVE_SEQUENCE( init_values, const_testset.begin() ); } + testset.clear(); + testset.insert(&values[0], &values[0] + values.size()); - { int init_values [] = { 1, 3, 5 }; - TEST_INTRUSIVE_SEQUENCE( init_values, const_testset.begin() ); } - testset.clear(); - testset.insert(&values[0], &values[0] + values.size()); + { int init_values [] = { 1, 2, 2, 3, 4, 5 }; + TEST_INTRUSIVE_SEQUENCE( init_values, const_testset.begin() ); } - { int init_values [] = { 1, 2, 2, 3, 4, 5 }; - TEST_INTRUSIVE_SEQUENCE( init_values, const_testset.begin() ); } + BOOST_TEST (testset.erase(1) == 1); + BOOST_TEST (testset.erase(2) == 2); + BOOST_TEST (testset.erase(3) == 1); + BOOST_TEST (testset.erase(4) == 1); + BOOST_TEST (testset.erase(5) == 1); + BOOST_TEST (testset.empty() == true); - BOOST_TEST (testset.erase(1) == 1); - BOOST_TEST (testset.erase(2) == 2); - BOOST_TEST (testset.erase(3) == 1); - BOOST_TEST (testset.erase(4) == 1); - BOOST_TEST (testset.erase(5) == 1); - BOOST_TEST (testset.empty() == true); + //Now with a single bucket + typename unordered_multiset_type::bucket_type single_bucket[1]; + unordered_multiset_type testset2(bucket_traits(single_bucket, 1)); + testset2.insert(&values[0], &values[0] + values.size()); + BOOST_TEST (testset2.erase(5) == 1); + BOOST_TEST (testset2.erase(2) == 2); + BOOST_TEST (testset2.erase(1) == 1); + BOOST_TEST (testset2.erase(4) == 1); + BOOST_TEST (testset2.erase(3) == 1); + BOOST_TEST (testset2.empty() == true); + } + { + //Now erase just one per loop + const int random_init[] = { 3, 2, 4, 1, 5, 2, 2 }; + const unsigned int random_size = sizeof(random_init)/sizeof(random_init[0]); + typename unordered_multiset_type::bucket_type single_bucket[1]; + for(unsigned int i = 0, max = random_size; i != max; ++i){ + std::vector data (random_size); + for (unsigned int j = 0; j < random_size; ++j) + data[j].value_ = random_init[j]; + unordered_multiset_type testset_new(bucket_traits(single_bucket, 1)); + testset_new.insert(&data[0], &data[max]); + testset_new.erase(testset_new.iterator_to(data[i])); + BOOST_TEST (testset_new.size() == (max -1)); + } + } + { + typename unordered_multiset_type::bucket_type buckets [BucketSize]; + const unsigned int NumBucketSize = BucketSize; + const unsigned int LoadFactor = 3; + const unsigned int NumIterations = NumBucketSize*LoadFactor; + std::vector random_init(NumIterations);//Preserve memory + std::vector set_tester; + set_tester.reserve(NumIterations); - //Now with a single bucket - typename unordered_multiset_type::bucket_type single_bucket[1]; - unordered_multiset_type testset2(bucket_traits(single_bucket, 1)); - testset2.insert(&values[0], &values[0] + values.size()); - BOOST_TEST (testset2.erase(5) == 1); - BOOST_TEST (testset2.erase(2) == 2); - BOOST_TEST (testset2.erase(1) == 1); - BOOST_TEST (testset2.erase(4) == 1); - BOOST_TEST (testset2.erase(3) == 1); - BOOST_TEST (testset2.empty() == true); + //Initialize values + for (unsigned int i = 0; i < NumIterations; ++i){ + random_init[i].value_ = i*2;//(i/LoadFactor)*LoadFactor; + } + + for(unsigned int initial_pos = 0; initial_pos != (NumIterations+1); ++initial_pos){ + for(unsigned int final_pos = initial_pos; final_pos != (NumIterations+1); ++final_pos){ + + //Create intrusive container inserting values + unordered_multiset_type testset + ( &random_init[0] + , &random_init[0] + random_init.size() + , bucket_traits(buckets, NumBucketSize)); + + BOOST_TEST (testset.size() == random_init.size()); + + //Obtain the iterator range to erase + iterator it_beg_pos = testset.begin(); + for(unsigned int it_beg_pos_num = 0; it_beg_pos_num != initial_pos; ++it_beg_pos_num){ + ++it_beg_pos; + } + iterator it_end_pos(it_beg_pos); + for(unsigned int it_end_pos_num = 0; it_end_pos_num != (final_pos - initial_pos); ++it_end_pos_num){ + ++it_end_pos; + } + + //Erase the same values in both the intrusive and original vector + std::size_t erased_cnt = std::distance(it_beg_pos, it_end_pos); + + //Erase values from the intrusive container + testset.erase(it_beg_pos, it_end_pos); + + BOOST_TEST (testset.size() == (random_init.size()-(final_pos - initial_pos))); + + //Now test... + BOOST_TEST ((random_init.size() - erased_cnt) == testset.size()); + + //Create an ordered copy of the intrusive container + set_tester.insert(set_tester.end(), testset.begin(), testset.end()); + std::sort(set_tester.begin(), set_tester.end()); + { + typename std::vector::iterator it = set_tester.begin(), itend = set_tester.end(); + typename std::vector::iterator random_init_it(random_init.begin()); + for( ; it != itend; ++it){ + while(!random_init_it->is_linked()) + ++random_init_it; + BOOST_TEST(*it == *random_init_it); + ++random_init_it; + } + } + set_tester.clear(); + } + } + } } //test: insert (seq-version), swap, erase (seq-version), size: -template -void test_unordered_multiset::test_swap(std::vector& values) +template +void test_unordered_multiset::test_swap(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; - typename unordered_multiset_type::bucket_type buckets [BucketSize]; - typename unordered_multiset_type::bucket_type buckets2 [BucketSize]; - unordered_multiset_type testset1(&values[0], &values[0] + 2, bucket_traits(buckets, BucketSize)); - unordered_multiset_type testset2(bucket_traits(buckets2, BucketSize)); + { + typename unordered_multiset_type::bucket_type buckets2 [BucketSize]; + unordered_multiset_type testset1(&values[0], &values[0] + 2, bucket_traits(buckets, BucketSize)); + unordered_multiset_type testset2(bucket_traits(buckets2, BucketSize)); - testset2.insert (&values[0] + 2, &values[0] + 6); - testset1.swap (testset2); + testset2.insert (&values[0] + 2, &values[0] + 6); + testset1.swap (testset2); - { int init_values [] = { 1, 2, 4, 5 }; - TEST_INTRUSIVE_SEQUENCE( init_values, testset1.begin() ); } + { int init_values [] = { 1, 2, 4, 5 }; + TEST_INTRUSIVE_SEQUENCE( init_values, testset1.begin() ); } - { int init_values [] = { 2, 3 }; - TEST_INTRUSIVE_SEQUENCE( init_values, testset2.begin() ); } - - testset1.erase (testset1.iterator_to(values[5]), testset1.end()); - BOOST_TEST (testset1.size() == 1); - // BOOST_TEST (&testset1.front() == &values[3]); - BOOST_TEST (&*testset1.begin() == &values[3]); + { int init_values [] = { 2, 3 }; + TEST_INTRUSIVE_SEQUENCE( init_values, testset2.begin() ); } + testset1.erase (testset1.iterator_to(values[5]), testset1.end()); + BOOST_TEST (testset1.size() == 1); + // BOOST_TEST (&testset1.front() == &values[3]); + BOOST_TEST (&*testset1.begin() == &values[3]); + } } //test: rehash: -template -void test_unordered_multiset::test_rehash(std::vector& values) +template +void test_unordered_multiset::test_rehash(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; @@ -261,14 +348,15 @@ void test_unordered_multiset::test_rehash(std::vector -void test_unordered_multiset::test_find(std::vector& values) +template +void test_unordered_multiset::test_find(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; @@ -293,8 +381,8 @@ void test_unordered_multiset::test_find(std::vector -void test_unordered_multiset +template +void test_unordered_multiset ::test_clone(std::vector& values) { typedef typename ValueTraits::value_type value_type; @@ -302,6 +390,7 @@ void test_unordered_multiset , constant_time_size + , cache_begin > unordered_multiset_type; typedef typename unordered_multiset_type::bucket_traits bucket_traits; { @@ -373,6 +462,7 @@ class test_main_template < value_type , typename value_type::unordered_set_base_hook_t >::type + , true >::test_all(data); test_unordered_multiset < typename detail::get_member_value_traits @@ -382,6 +472,7 @@ class test_main_template , &value_type::unordered_set_node_ > >::type + , false >::test_all(data); return 0; @@ -404,6 +495,7 @@ class test_main_template < value_type , typename value_type::unordered_set_base_hook_t >::type + , true >::test_all(data); test_unordered_multiset < typename detail::get_member_value_traits @@ -413,12 +505,14 @@ class test_main_template , &value_type::unordered_set_node_ > >::type + , false >::test_all(data); test_unordered_multiset < typename detail::get_base_value_traits < value_type , typename value_type::unordered_set_auto_base_hook_t >::type + , true >::test_all(data); test_unordered_multiset < typename detail::get_member_value_traits @@ -428,6 +522,7 @@ class test_main_template , &value_type::unordered_set_auto_node_ > >::type + , false >::test_all(data); return 0; } diff --git a/test/unordered_set_test.cpp b/test/unordered_set_test.cpp index 3eeccf6..6fcff90 100644 --- a/test/unordered_set_test.cpp +++ b/test/unordered_set_test.cpp @@ -26,7 +26,7 @@ using namespace boost::intrusive; static const std::size_t BucketSize = 11; -template +template struct test_unordered_set { typedef typename ValueTraits::value_type value_type; @@ -40,14 +40,15 @@ struct test_unordered_set static void test_clone(std::vector& values); }; -template -void test_unordered_set::test_all(std::vector& values) +template +void test_unordered_set::test_all(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; { @@ -75,14 +76,15 @@ void test_unordered_set::test_all(std::vector -void test_unordered_set::test_impl() +template +void test_unordered_set::test_impl() { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; @@ -103,14 +105,15 @@ void test_unordered_set::test_impl() } //test: constructor, iterator, clear, reverse_iterator, front, back, size: -template -void test_unordered_set::test_sort(std::vector& values) +template +void test_unordered_set::test_sort(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; @@ -126,14 +129,15 @@ void test_unordered_set::test_sort(std::vector -void test_unordered_set::test_insert(std::vector& values) +template +void test_unordered_set::test_insert(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; @@ -161,14 +165,15 @@ void test_unordered_set::test_insert(std::vector -void test_unordered_set::test_swap(std::vector& values) +template +void test_unordered_set::test_swap(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; @@ -192,14 +197,15 @@ void test_unordered_set::test_swap(std::vector -void test_unordered_set::test_rehash(std::vector& values) +template +void test_unordered_set::test_rehash(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; @@ -237,14 +243,15 @@ void test_unordered_set::test_rehash(std::vector -void test_unordered_set::test_find(std::vector& values) +template +void test_unordered_set::test_find(std::vector& values) { typedef typename ValueTraits::value_type value_type; typedef unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; @@ -267,8 +274,8 @@ void test_unordered_set::test_find(std::vector -void test_unordered_set +template +void test_unordered_set ::test_clone(std::vector& values) { typedef typename ValueTraits::value_type value_type; @@ -276,6 +283,7 @@ void test_unordered_set , constant_time_size + , cache_begin > unordered_set_type; typedef typename unordered_set_type::bucket_traits bucket_traits; { @@ -347,6 +355,7 @@ class test_main_template < value_type , typename value_type::unordered_set_base_hook_t >::type + , true >::test_all(data); test_unordered_set < typename detail::get_member_value_traits < value_type @@ -355,6 +364,7 @@ class test_main_template , &value_type::unordered_set_node_ > >::type + , false >::test_all(data); return 0; @@ -377,6 +387,7 @@ class test_main_template < value_type , typename value_type::unordered_set_base_hook_t >::type + , true >::test_all(data); test_unordered_set < typename detail::get_member_value_traits @@ -386,12 +397,14 @@ class test_main_template , &value_type::unordered_set_node_ > >::type + , false >::test_all(data); test_unordered_set < typename detail::get_base_value_traits < value_type , typename value_type::unordered_set_auto_base_hook_t >::type + , true >::test_all(data); test_unordered_set < typename detail::get_member_value_traits @@ -401,6 +414,7 @@ class test_main_template , &value_type::unordered_set_auto_node_ > >::type + , false >::test_all(data); return 0; }