diff --git a/include/boost/intrusive/avltree_algorithms.hpp b/include/boost/intrusive/avltree_algorithms.hpp index e6127b8..c4b6102 100644 --- a/include/boost/intrusive/avltree_algorithms.hpp +++ b/include/boost/intrusive/avltree_algorithms.hpp @@ -49,6 +49,45 @@ struct avltree_node_cloner } }; +namespace detail { + +template +struct avltree_node_checker + : public bstree_node_checker +{ + typedef bstree_node_checker base_checker_t; + typedef ValueTraits value_traits; + typedef typename value_traits::node_traits node_traits; + typedef typename node_traits::node_ptr node_ptr; + + struct return_type + : public base_checker_t::return_type + { + return_type() : height(0) {} + int height; + }; + + avltree_node_checker(const NodePtrCompare& comp, ExtraChecker extra_checker) + : base_checker_t(comp, extra_checker) + {} + + void operator () (const node_ptr& p, + const return_type& check_return_left, const return_type& check_return_right, + return_type& check_return) + { + int height_diff = check_return_right.height - check_return_left.height; + BOOST_INTRUSIVE_INVARIANT_ASSERT( + (height_diff == -1 && node_traits::get_balance(p) == node_traits::negative()) || + (height_diff == 0 && node_traits::get_balance(p) == node_traits::zero()) || + (height_diff == 1 && node_traits::get_balance(p) == node_traits::positive()) + ); + check_return.height = 1 + std::max(check_return_left.height, check_return_right.height); + base_checker_t::operator()(p, check_return_left, check_return_right, check_return); + } +}; + +} // namespace detail + /// @endcond //! avltree_algorithms is configured with a NodeTraits class, which encapsulates the @@ -628,6 +667,12 @@ struct get_algo typedef avltree_algorithms type; }; +template +struct get_node_checker +{ + typedef detail::avltree_node_checker type; +}; + /// @endcond } //namespace intrusive diff --git a/include/boost/intrusive/bstree.hpp b/include/boost/intrusive/bstree.hpp index 2af42cc..f9c498d 100644 --- a/include/boost/intrusive/bstree.hpp +++ b/include/boost/intrusive/bstree.hpp @@ -1853,6 +1853,23 @@ class bstree_impl node_algorithms::init(to_remove); } + template + void check(ExtraChecker extra_checker) + { + typedef detail::key_nodeptr_comp nodeptr_comp_t; + nodeptr_comp_t nodeptr_comp(this->comp(), &this->get_value_traits()); + typedef typename get_node_checker::type node_checker_t; + typename node_checker_t::return_type checker_return; + node_algorithms::check(this->header_ptr(), node_checker_t(nodeptr_comp, extra_checker), checker_return); + if (constant_time_size) + BOOST_INTRUSIVE_INVARIANT_ASSERT(this->sz_traits().get_size() == checker_return.node_count); + } + + void check() + { + check(detail::empty_node_checker()); + } + /// @cond private: template diff --git a/include/boost/intrusive/bstree_algorithms.hpp b/include/boost/intrusive/bstree_algorithms.hpp index d155d6d..236ab8f 100644 --- a/include/boost/intrusive/bstree_algorithms.hpp +++ b/include/boost/intrusive/bstree_algorithms.hpp @@ -45,6 +45,50 @@ struct data_for_rebalance_t NodePtr y; }; +namespace detail { + +template +struct bstree_node_checker + : public ExtraChecker +{ + typedef ExtraChecker base_checker_t; + typedef ValueTraits value_traits; + typedef typename value_traits::node_traits node_traits; + typedef typename node_traits::node_ptr node_ptr; + + struct return_type + : public base_checker_t::return_type + { + return_type() : min_key_node_ptr(node_ptr()), max_key_node_ptr(node_ptr()), node_count(0) {} + + node_ptr min_key_node_ptr; + node_ptr max_key_node_ptr; + size_t node_count; + }; + + bstree_node_checker(const NodePtrCompare& comp, ExtraChecker extra_checker) + : base_checker_t(extra_checker), comp_(comp) + {} + + void operator () (const node_ptr& p, + const return_type& check_return_left, const return_type& check_return_right, + return_type& check_return) + { + if (check_return_left.max_key_node_ptr) + BOOST_INTRUSIVE_INVARIANT_ASSERT(!comp_(p, check_return_left.max_key_node_ptr)); + if (check_return_right.min_key_node_ptr) + BOOST_INTRUSIVE_INVARIANT_ASSERT(!comp_(check_return_right.min_key_node_ptr, p)); + check_return.min_key_node_ptr = node_traits::get_left(p)? check_return_left.min_key_node_ptr : p; + check_return.max_key_node_ptr = node_traits::get_right(p)? check_return_right.max_key_node_ptr : p; + check_return.node_count = check_return_left.node_count + check_return_right.node_count + 1; + base_checker_t::operator()(p, check_return_left, check_return_right, check_return); + } + + const NodePtrCompare comp_; +}; + +} // namespace detail + /// @endcond @@ -1442,6 +1486,32 @@ class bstree_algorithms return new_root; } + template + static void check(const node_ptr & header, Checker checker, typename Checker::return_type& checker_return) + { + node_ptr root_node_ptr = NodeTraits::get_parent(header); + if (!root_node_ptr) + { + // check left&right header pointers + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_left(header) == header); + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_right(header) == header); + } + else + { + // check parent pointer of root node + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_parent(root_node_ptr) == header); + // check subtree from root + check_subtree(root_node_ptr, checker, checker_return); + // check left&right header pointers + node_ptr p = root_node_ptr; + while (NodeTraits::get_left(p)) { p = NodeTraits::get_left(p); } + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_left(header) == p); + p = root_node_ptr; + while (NodeTraits::get_right(p)) { p = NodeTraits::get_right(p); } + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_right(header) == p); + } + } + protected: static void erase(const node_ptr & header, const node_ptr & z, data_for_rebalance &info) { @@ -1997,6 +2067,26 @@ class bstree_algorithms } return y; } + + template + static void check_subtree(const node_ptr& node, Checker checker, typename Checker::return_type& check_return) + { + node_ptr left = NodeTraits::get_left(node); + node_ptr right = NodeTraits::get_right(node); + typename Checker::return_type check_return_left; + typename Checker::return_type check_return_right; + if (left) + { + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_parent(left) == node); + check_subtree(left, checker, check_return_left); + } + if (right) + { + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_parent(right) == node); + check_subtree(right, checker, check_return_right); + } + checker(node, check_return_left, check_return_right, check_return); + } }; /// @cond @@ -2007,6 +2097,12 @@ struct get_algo typedef bstree_algorithms type; }; +template +struct get_node_checker +{ + typedef detail::bstree_node_checker type; +}; + /// @endcond } //namespace intrusive diff --git a/include/boost/intrusive/detail/utilities.hpp b/include/boost/intrusive/detail/utilities.hpp index b893d22..e70b7a8 100644 --- a/include/boost/intrusive/detail/utilities.hpp +++ b/include/boost/intrusive/detail/utilities.hpp @@ -52,6 +52,9 @@ enum algo_types template struct get_algo; +template +struct get_node_checker; + template struct is_safe_autounlink { @@ -299,6 +302,18 @@ struct node_disposer const ValueTraits * const traits_; }; +template +struct empty_node_checker +{ + typedef ValueTraits value_traits; + typedef typename value_traits::node_traits node_traits; + typedef typename node_traits::node_ptr node_ptr; + + struct return_type {}; + + void operator () (const node_ptr&, const return_type&, const return_type&, return_type&) {} +}; + template struct dummy_constptr { diff --git a/include/boost/intrusive/hashtable.hpp b/include/boost/intrusive/hashtable.hpp index 8ada6e7..65a6bf0 100644 --- a/include/boost/intrusive/hashtable.hpp +++ b/include/boost/intrusive/hashtable.hpp @@ -2659,6 +2659,7 @@ class hashtable_impl return size_type(*bound); } /// @cond + void check() {} private: size_traits &priv_size_traits() { return static_cast(static_cast(*this)); } diff --git a/include/boost/intrusive/list.hpp b/include/boost/intrusive/list.hpp index c22e3af..61b4937 100644 --- a/include/boost/intrusive/list.hpp +++ b/include/boost/intrusive/list.hpp @@ -1253,6 +1253,41 @@ class list_impl return const_iterator(this->priv_value_traits().to_node_ptr(r), this->priv_value_traits_ptr()); } + //! Effects: Asserts the integrity of the container. + //! + //! Complexity: Linear time. + //! + //! Note: The method has no effect when asserts are turned off (e.g., with NDEBUG). + void check() const + { + const_node_ptr header_ptr = get_root_node(); + // header's next and prev are never null + BOOST_INTRUSIVE_INVARIANT_ASSERT(node_traits::get_next(header_ptr)); + BOOST_INTRUSIVE_INVARIANT_ASSERT(node_traits::get_previous(header_ptr)); + // header's next and prev either both point to header (empty list) or neither does + BOOST_INTRUSIVE_INVARIANT_ASSERT((node_traits::get_next(header_ptr) == header_ptr) + == (node_traits::get_previous(header_ptr) == header_ptr)); + if (node_traits::get_next(header_ptr) == header_ptr) + { + if (constant_time_size) + BOOST_INTRUSIVE_INVARIANT_ASSERT(this->priv_size_traits().get_size() == 0); + return; + } + size_t node_count = 0; + const_node_ptr p = header_ptr; + while (true) + { + const_node_ptr next_p = node_traits::get_next(p); + BOOST_INTRUSIVE_INVARIANT_ASSERT(next_p); + BOOST_INTRUSIVE_INVARIANT_ASSERT(node_traits::get_previous(next_p) == p); + p = next_p; + if (p == header_ptr) break; + ++node_count; + } + if (constant_time_size) + BOOST_INTRUSIVE_INVARIANT_ASSERT(this->priv_size_traits().get_size() == node_count); + } + /// @cond private: diff --git a/include/boost/intrusive/rbtree_algorithms.hpp b/include/boost/intrusive/rbtree_algorithms.hpp index 0c1225e..2772a12 100644 --- a/include/boost/intrusive/rbtree_algorithms.hpp +++ b/include/boost/intrusive/rbtree_algorithms.hpp @@ -57,6 +57,44 @@ struct rbtree_node_cloner } }; +namespace detail { + +template +struct rbtree_node_checker + : public bstree_node_checker +{ + typedef bstree_node_checker base_checker_t; + typedef ValueTraits value_traits; + typedef typename value_traits::node_traits node_traits; + typedef typename node_traits::node_ptr node_ptr; + + struct return_type + : public base_checker_t::return_type + { + return_type() : is_red(false) {} + bool is_red; + }; + + rbtree_node_checker(const NodePtrCompare& comp, ExtraChecker extra_checker) + : base_checker_t(comp, extra_checker) + {} + + void operator () (const node_ptr& p, + const return_type& check_return_left, const return_type& check_return_right, + return_type& check_return) + { + check_return.is_red = (node_traits::get_color(p) == node_traits::red()); + if (check_return.is_red) + { + BOOST_INTRUSIVE_INVARIANT_ASSERT(!check_return_left.is_red); + BOOST_INTRUSIVE_INVARIANT_ASSERT(!check_return_right.is_red); + } + base_checker_t::operator()(p, check_return_left, check_return_right, check_return); + } +}; + +} // namespace detail + #endif //#ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED //! rbtree_algorithms provides basic algorithms to manipulate @@ -519,6 +557,12 @@ struct get_algo typedef rbtree_algorithms type; }; +template +struct get_node_checker +{ + typedef detail::rbtree_node_checker type; +}; + /// @endcond } //namespace intrusive diff --git a/include/boost/intrusive/sgtree_algorithms.hpp b/include/boost/intrusive/sgtree_algorithms.hpp index 2e4ce28..4a36949 100644 --- a/include/boost/intrusive/sgtree_algorithms.hpp +++ b/include/boost/intrusive/sgtree_algorithms.hpp @@ -359,6 +359,12 @@ struct get_algo typedef sgtree_algorithms type; }; +template +struct get_node_checker +{ + typedef detail::bstree_node_checker type; +}; + /// @endcond } //namespace intrusive diff --git a/include/boost/intrusive/slist.hpp b/include/boost/intrusive/slist.hpp index 74d14c1..436f1df 100644 --- a/include/boost/intrusive/slist.hpp +++ b/include/boost/intrusive/slist.hpp @@ -1826,6 +1826,48 @@ class slist_impl ///@endcond + //! Effects: Asserts the integrity of the container. + //! + //! Complexity: Linear time. + //! + //! Note: The method has no effect when asserts are turned off (e.g., with NDEBUG). + void check() const + { + const_node_ptr header_ptr = get_root_node(); + // header's next is never null + BOOST_INTRUSIVE_INVARIANT_ASSERT(node_traits::get_next(header_ptr)); + if (node_traits::get_next(header_ptr) == header_ptr) + { + if (constant_time_size) + BOOST_INTRUSIVE_INVARIANT_ASSERT(this->priv_size_traits().get_size() == 0); + return; + } + size_t node_count = 0; + const_node_ptr p = header_ptr; + while (true) + { + const_node_ptr next_p = node_traits::get_next(p); + if (!linear) + { + BOOST_INTRUSIVE_INVARIANT_ASSERT(next_p); + } + else + { + BOOST_INTRUSIVE_INVARIANT_ASSERT(next_p != header_ptr); + } + if ((!linear && next_p == header_ptr) || (linear && !next_p)) + { + if (cache_last) + BOOST_INTRUSIVE_INVARIANT_ASSERT(get_last_node() == p); + break; + } + p = next_p; + ++node_count; + } + if (constant_time_size) + BOOST_INTRUSIVE_INVARIANT_ASSERT(this->priv_size_traits().get_size() == node_count); + } + private: void priv_splice_after(const node_ptr & prev_pos_n, slist_impl &x, const node_ptr & before_f_n, const node_ptr & before_l_n) { diff --git a/include/boost/intrusive/splaytree_algorithms.hpp b/include/boost/intrusive/splaytree_algorithms.hpp index b70bd86..925628b 100644 --- a/include/boost/intrusive/splaytree_algorithms.hpp +++ b/include/boost/intrusive/splaytree_algorithms.hpp @@ -706,6 +706,12 @@ struct get_algo typedef splaytree_algorithms type; }; +template +struct get_node_checker +{ + typedef detail::bstree_node_checker type; +}; + /// @endcond } //namespace intrusive diff --git a/include/boost/intrusive/treap.hpp b/include/boost/intrusive/treap.hpp index f414cf7..22ed24f 100644 --- a/include/boost/intrusive/treap.hpp +++ b/include/boost/intrusive/treap.hpp @@ -904,6 +904,19 @@ class treap_impl this->tree_type::sz_traits().set_size(0); } + template + void check(ExtraChecker extra_checker) + { + typedef detail::key_nodeptr_comp nodeptr_prio_comp_t; + nodeptr_prio_comp_t nodeptr_prio_comp(priv_pcomp(), &this->get_value_traits()); + tree_type::check(detail::treap_node_extra_checker(nodeptr_prio_comp, extra_checker)); + } + + void check() + { + check(detail::empty_node_checker()); + } + #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED //! @copydoc ::boost::intrusive::bstree::count(const_reference)const size_type count(const_reference value) const; diff --git a/include/boost/intrusive/treap_algorithms.hpp b/include/boost/intrusive/treap_algorithms.hpp index 2b0704b..73d1385 100644 --- a/include/boost/intrusive/treap_algorithms.hpp +++ b/include/boost/intrusive/treap_algorithms.hpp @@ -28,6 +28,44 @@ namespace boost { namespace intrusive { +#ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED + +namespace detail +{ + +template +struct treap_node_extra_checker + : public ExtraChecker +{ + typedef ExtraChecker base_checker_t; + typedef ValueTraits value_traits; + typedef typename value_traits::node_traits node_traits; + typedef typename node_traits::node_ptr node_ptr; + + typedef typename base_checker_t::return_type return_type; + + treap_node_extra_checker(const NodePtrPrioCompare& prio_comp, ExtraChecker extra_checker) + : base_checker_t(extra_checker), prio_comp_(prio_comp) + {} + + void operator () (const node_ptr& p, + const return_type& check_return_left, const return_type& check_return_right, + return_type& check_return) + { + if (node_traits::get_left(p)) + BOOST_INTRUSIVE_INVARIANT_ASSERT(!prio_comp_(node_traits::get_left(p), p)); + if (node_traits::get_right(p)) + BOOST_INTRUSIVE_INVARIANT_ASSERT(!prio_comp_(node_traits::get_right(p), p)); + base_checker_t::operator()(p, check_return_left, check_return_right, check_return); + } + + const NodePtrPrioCompare prio_comp_; +}; + +} // namespace detail + +#endif //#ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED + //! treap_algorithms provides basic algorithms to manipulate //! nodes forming a treap. //! @@ -616,6 +654,12 @@ struct get_algo typedef treap_algorithms type; }; +template +struct get_node_checker +{ + typedef detail::bstree_node_checker type; +}; + /// @endcond } //namespace intrusive diff --git a/test/test_container.hpp b/test/test_container.hpp index fbddaec..eb0b32e 100644 --- a/test/test_container.hpp +++ b/test/test_container.hpp @@ -73,6 +73,7 @@ void test_container( Container & c ) BOOST_TEST( it == itend ); BOOST_TEST( c.size() == i ); } + c.check(); }