mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8366238: Improve RBTree API with stricter comparator semantics and pluggable validation/printing hooks
Reviewed-by: jsjolen, ayang
This commit is contained in:
parent
ab9f70dd5a
commit
53d4e928ef
@ -118,20 +118,20 @@ static ZMappedCacheEntry* create_entry(const ZVirtualMemory& vmem) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool ZMappedCache::EntryCompare::cmp(const IntrusiveRBNode* a, const IntrusiveRBNode* b) {
|
||||
bool ZMappedCache::EntryCompare::less_than(const IntrusiveRBNode* a, const IntrusiveRBNode* b) {
|
||||
const ZVirtualMemory vmem_a = ZMappedCacheEntry::cast_to_entry(a)->vmem();
|
||||
const ZVirtualMemory vmem_b = ZMappedCacheEntry::cast_to_entry(b)->vmem();
|
||||
|
||||
return vmem_a.end() < vmem_b.start();
|
||||
}
|
||||
|
||||
int ZMappedCache::EntryCompare::cmp(zoffset key, const IntrusiveRBNode* node) {
|
||||
RBTreeOrdering ZMappedCache::EntryCompare::cmp(zoffset key, const IntrusiveRBNode* node) {
|
||||
const ZVirtualMemory vmem = ZMappedCacheEntry::cast_to_entry(node)->vmem();
|
||||
|
||||
if (key < vmem.start()) { return -1; }
|
||||
if (key > vmem.end()) { return 1; }
|
||||
if (key < vmem.start()) { return RBTreeOrdering::LT; }
|
||||
if (key > vmem.end()) { return RBTreeOrdering::GT; }
|
||||
|
||||
return 0; // Containing
|
||||
return RBTreeOrdering::EQ; // Containing
|
||||
}
|
||||
|
||||
void ZMappedCache::Tree::verify() const {
|
||||
@ -168,12 +168,12 @@ void ZMappedCache::Tree::insert(TreeNode* node, const TreeCursor& cursor) {
|
||||
// Insert in tree
|
||||
TreeImpl::insert_at_cursor(node, cursor);
|
||||
|
||||
if (_left_most == nullptr || EntryCompare::cmp(node, _left_most)) {
|
||||
if (_left_most == nullptr || EntryCompare::less_than(node, _left_most)) {
|
||||
// Keep track of left most node
|
||||
_left_most = node;
|
||||
}
|
||||
|
||||
if (_right_most == nullptr || EntryCompare::cmp(_right_most, node)) {
|
||||
if (_right_most == nullptr || EntryCompare::less_than(_right_most, node)) {
|
||||
// Keep track of right most node
|
||||
_right_most = node;
|
||||
}
|
||||
|
||||
@ -40,8 +40,8 @@ class ZMappedCache {
|
||||
|
||||
private:
|
||||
struct EntryCompare {
|
||||
static int cmp(zoffset a, const IntrusiveRBNode* b);
|
||||
static bool cmp(const IntrusiveRBNode* a, const IntrusiveRBNode* b);
|
||||
static RBTreeOrdering cmp(zoffset a, const IntrusiveRBNode* b);
|
||||
static bool less_than(const IntrusiveRBNode* a, const IntrusiveRBNode* b);
|
||||
};
|
||||
|
||||
struct ZSizeClassListNode {
|
||||
|
||||
@ -50,11 +50,10 @@ public:
|
||||
|
||||
class PositionComparator {
|
||||
public:
|
||||
static int cmp(position a, position b) {
|
||||
if (a < b) return -1;
|
||||
if (a == b) return 0;
|
||||
if (a > b) return 1;
|
||||
ShouldNotReachHere();
|
||||
static RBTreeOrdering cmp(position a, position b) {
|
||||
if (a < b) return RBTreeOrdering::LT;
|
||||
if (a > b) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -67,8 +67,10 @@ private:
|
||||
};
|
||||
|
||||
struct Cmp {
|
||||
static int cmp(int a, int b) {
|
||||
return a - b;
|
||||
static RBTreeOrdering cmp(int a, int b) {
|
||||
if (a < b) return RBTreeOrdering::LT;
|
||||
if (a > b) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -35,17 +35,13 @@
|
||||
// An intrusive red-black tree is constructed with two template parameters:
|
||||
// K is the key type used.
|
||||
// COMPARATOR must have a static function `cmp(K a, const IntrusiveRBNode* b)` which returns:
|
||||
// - an int < 0 when a < b
|
||||
// - an int == 0 when a == b
|
||||
// - an int > 0 when a > b
|
||||
// Additional static functions used for extra validation can optionally be provided:
|
||||
// `cmp(K a, K b)` which returns:
|
||||
// - an int < 0 when a < b
|
||||
// - an int == 0 when a == b
|
||||
// - an int > 0 when a > b
|
||||
// `cmp(const IntrusiveRBNode* a, const IntrusiveRBNode* b)` which returns:
|
||||
// - true if a < b
|
||||
// - false otherwise
|
||||
// - RBTreeOrdering::LT when a < b
|
||||
// - RBTreeOrdering::EQ when a == b
|
||||
// - RBTreeOrdering::GT when a > b
|
||||
// A second static function `less_than(const IntrusiveRBNode* a, const IntrusiveRBNode* b)`
|
||||
// used for extra validation can optionally be provided. This should return:
|
||||
// - true if a < b
|
||||
// - false otherwise
|
||||
// K needs to be of a type that is trivially destructible.
|
||||
// K needs to be stored by the user and is not stored inside the tree.
|
||||
// Nodes are address stable and will not change during its lifetime.
|
||||
@ -54,10 +50,10 @@
|
||||
// K is the key type stored in the tree nodes.
|
||||
// V is the value type stored in the tree nodes.
|
||||
// COMPARATOR must have a static function `cmp(K a, K b)` which returns:
|
||||
// - an int < 0 when a < b
|
||||
// - an int == 0 when a == b
|
||||
// - an int > 0 when a > b
|
||||
// A second static function `cmp(const RBNode<K, V>* a, const RBNode<K, V>* b)`
|
||||
// - RBTreeOrdering::LT when a < b
|
||||
// - RBTreeOrdering::EQ when a == b
|
||||
// - RBTreeOrdering::GT when a > b
|
||||
// A second static function `less_than(const RBNode<K, V>* a, const RBNode<K, V>* b)`
|
||||
// used for extra validation can optionally be provided. This should return:
|
||||
// - true if a < b
|
||||
// - false otherwise
|
||||
@ -65,6 +61,8 @@
|
||||
// The tree will call a value's destructor when its node is removed.
|
||||
// Nodes are address stable and will not change during its lifetime.
|
||||
|
||||
enum class RBTreeOrdering : int { LT, EQ, GT };
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
class AbstractRBTree;
|
||||
|
||||
@ -126,10 +124,11 @@ private:
|
||||
// Returns left child (now parent)
|
||||
IntrusiveRBNode* rotate_right();
|
||||
|
||||
template <typename NodeType, typename NodeVerifier>
|
||||
template <typename NodeType, typename NODE_VERIFIER, typename USER_VERIFIER>
|
||||
void verify(size_t& num_nodes, size_t& black_nodes_until_leaf,
|
||||
size_t& shortest_leaf_path, size_t& longest_leaf_path,
|
||||
size_t& tree_depth, bool expect_visited, NodeVerifier verifier) const;
|
||||
size_t& tree_depth, bool expect_visited, NODE_VERIFIER verifier,
|
||||
const USER_VERIFIER& extra_verifier) const;
|
||||
|
||||
};
|
||||
|
||||
@ -203,32 +202,37 @@ private:
|
||||
struct has_cmp_type<CMP, RET, ARG1, ARG2, decltype(static_cast<RET(*)(ARG1, ARG2)>(CMP::cmp), void())> : std::true_type {};
|
||||
|
||||
template <typename CMP>
|
||||
static constexpr bool HasKeyComparator = has_cmp_type<CMP, int, K, K>::value;
|
||||
static constexpr bool HasKeyComparator = has_cmp_type<CMP, RBTreeOrdering, K, K>::value;
|
||||
|
||||
template <typename CMP>
|
||||
static constexpr bool HasNodeComparator = has_cmp_type<CMP, int, K, const NodeType*>::value;
|
||||
static constexpr bool HasNodeComparator = has_cmp_type<CMP, RBTreeOrdering, K, const NodeType*>::value;
|
||||
|
||||
template <typename CMP, typename RET, typename ARG1, typename ARG2, typename = void>
|
||||
struct has_less_than_type : std::false_type {};
|
||||
template <typename CMP, typename RET, typename ARG1, typename ARG2>
|
||||
struct has_less_than_type<CMP, RET, ARG1, ARG2, decltype(static_cast<RET(*)(ARG1, ARG2)>(CMP::less), void())> : std::true_type {};
|
||||
|
||||
template <typename CMP>
|
||||
static constexpr bool HasNodeVerifier = has_cmp_type<CMP, bool, const NodeType*, const NodeType*>::value;
|
||||
static constexpr bool HasNodeVerifier = has_less_than_type<CMP, bool, const NodeType*, const NodeType*>::value;
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasKeyComparator<CMP> && !HasNodeComparator<CMP>)>
|
||||
int cmp(const K& a, const NodeType* b) const {
|
||||
RBTreeOrdering cmp(const K& a, const NodeType* b) const {
|
||||
return COMPARATOR::cmp(a, b->key());
|
||||
}
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasNodeComparator<CMP>)>
|
||||
int cmp(const K& a, const NodeType* b) const {
|
||||
RBTreeOrdering cmp(const K& a, const NodeType* b) const {
|
||||
return COMPARATOR::cmp(a, b);
|
||||
}
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(!HasNodeVerifier<CMP>)>
|
||||
bool cmp(const NodeType* a, const NodeType* b) const {
|
||||
bool less_than(const NodeType* a, const NodeType* b) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasNodeVerifier<CMP>)>
|
||||
bool cmp(const NodeType* a, const NodeType* b) const {
|
||||
return COMPARATOR::cmp(a, b);
|
||||
bool less_than(const NodeType* a, const NodeType* b) const {
|
||||
return COMPARATOR::less_than(a, b);
|
||||
}
|
||||
|
||||
// Cannot assert if no key comparator exist.
|
||||
@ -237,7 +241,7 @@ private:
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasKeyComparator<CMP>)>
|
||||
void assert_key_leq(K a, K b) const {
|
||||
assert(COMPARATOR::cmp(a, b) <= 0, "key a must be less or equal to key b");
|
||||
assert(COMPARATOR::cmp(a, b) != RBTreeOrdering::GT, "key a must be less or equal to key b");
|
||||
}
|
||||
|
||||
// True if node is black (nil nodes count as black)
|
||||
@ -256,10 +260,23 @@ private:
|
||||
// Assumption: node has at most one child. Two children is handled in `remove_at_cursor()`
|
||||
void remove_from_tree(IntrusiveRBNode* node);
|
||||
|
||||
template <typename NodeVerifier>
|
||||
void verify_self(NodeVerifier verifier) const;
|
||||
struct empty_verifier {
|
||||
bool operator()(const NodeType* n) const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void print_node_on(outputStream* st, int depth, const NodeType* n) const;
|
||||
template <typename NODE_VERIFIER, typename USER_VERIFIER>
|
||||
void verify_self(NODE_VERIFIER verifier, const USER_VERIFIER& extra_verifier) const;
|
||||
|
||||
struct default_printer {
|
||||
void operator()(outputStream* st, const NodeType* n, int depth) const {
|
||||
n->print_on(st, depth);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PRINTER>
|
||||
void print_node_on(outputStream* st, int depth, const NodeType* n, const PRINTER& node_printer) const;
|
||||
|
||||
public:
|
||||
NONCOPYABLE(AbstractRBTree);
|
||||
@ -422,22 +439,30 @@ public:
|
||||
// Verifies that the tree is correct and holds rb-properties
|
||||
// If not using a key comparator (when using IntrusiveRBTree for example),
|
||||
// A second `cmp` must exist in COMPARATOR (see top of file).
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasNodeVerifier<CMP>)>
|
||||
void verify_self() const {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::cmp(a, b);});
|
||||
// Accepts an optional callable `bool extra_verifier(const Node* n)`.
|
||||
// This should return true if the node is valid.
|
||||
// If provided, each node is also verified through this callable.
|
||||
template <typename USER_VERIFIER = empty_verifier, typename CMP = COMPARATOR, ENABLE_IF(HasNodeVerifier<CMP>)>
|
||||
void verify_self(const USER_VERIFIER& extra_verifier = USER_VERIFIER()) const {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::less_than(a, b);}, extra_verifier);
|
||||
}
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasKeyComparator<CMP> && !HasNodeVerifier<CMP>)>
|
||||
void verify_self() const {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::cmp(a->key(), b->key()) < 0; });
|
||||
template <typename USER_VERIFIER = empty_verifier, typename CMP = COMPARATOR,
|
||||
ENABLE_IF(HasKeyComparator<CMP> && !HasNodeVerifier<CMP>)>
|
||||
void verify_self(const USER_VERIFIER& extra_verifier = USER_VERIFIER()) const {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::cmp(a->key(), b->key()) == RBTreeOrdering::LT; }, extra_verifier);
|
||||
}
|
||||
|
||||
template <typename CMP = COMPARATOR, ENABLE_IF(HasNodeComparator<CMP> && !HasKeyComparator<CMP> && !HasNodeVerifier<CMP>)>
|
||||
void verify_self() const {
|
||||
verify_self([](const NodeType*, const NodeType*){ return true;});
|
||||
template <typename USER_VERIFIER = empty_verifier, typename CMP = COMPARATOR,
|
||||
ENABLE_IF(HasNodeComparator<CMP> && !HasKeyComparator<CMP> && !HasNodeVerifier<CMP>)>
|
||||
void verify_self(const USER_VERIFIER& extra_verifier = USER_VERIFIER()) const {
|
||||
verify_self([](const NodeType*, const NodeType*){ return true;}, extra_verifier);
|
||||
}
|
||||
|
||||
void print_on(outputStream* st) const;
|
||||
// Accepts an optional printing callable `void node_printer(outputStream* st, const Node* n, int depth)`.
|
||||
// If provided, each node is printed through this callable rather than the default `print_on`.
|
||||
template <typename PRINTER = default_printer>
|
||||
void print_on(outputStream* st, const PRINTER& node_printer = PRINTER()) const;
|
||||
|
||||
};
|
||||
|
||||
@ -451,6 +476,14 @@ class RBTree : public AbstractRBTree<K, RBNode<K, V>, COMPARATOR> {
|
||||
public:
|
||||
RBTree() : BaseType(), _allocator() {}
|
||||
~RBTree() { remove_all(); }
|
||||
RBTree(const RBTree& other) : BaseType(), _allocator() {
|
||||
assert(std::is_copy_constructible<V>(), "Value type must be copy-constructible");
|
||||
other.visit_in_order([&](auto node) {
|
||||
this->upsert(node->key(), node->val());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
RBTree& operator=(const RBTree& other) = delete;
|
||||
|
||||
typedef typename BaseType::Cursor Cursor;
|
||||
using BaseType::cursor;
|
||||
|
||||
@ -123,10 +123,11 @@ inline IntrusiveRBNode* IntrusiveRBNode::next() {
|
||||
return const_cast<IntrusiveRBNode*>(static_cast<const IntrusiveRBNode*>(this)->next());
|
||||
}
|
||||
|
||||
template <typename NodeType, typename NodeVerifier>
|
||||
template <typename NodeType, typename NODE_VERIFIER, typename USER_VERIFIER>
|
||||
inline void IntrusiveRBNode::verify(
|
||||
size_t& num_nodes, size_t& black_nodes_until_leaf, size_t& shortest_leaf_path, size_t& longest_leaf_path,
|
||||
size_t& tree_depth, bool expect_visited, NodeVerifier verifier) const {
|
||||
size_t& tree_depth, bool expect_visited, NODE_VERIFIER verifier, const USER_VERIFIER& extra_verifier) const {
|
||||
assert(extra_verifier(static_cast<const NodeType*>(this)), "user provided verifier failed");
|
||||
assert(expect_visited != _visited, "node already visited");
|
||||
DEBUG_ONLY(_visited = !_visited);
|
||||
|
||||
@ -143,7 +144,7 @@ inline void IntrusiveRBNode::verify(
|
||||
assert(is_black() || _left->is_black(), "2 red nodes in a row");
|
||||
assert(_left->parent() == this, "pointer mismatch");
|
||||
_left->verify<NodeType>(num_nodes, num_black_nodes_left, shortest_leaf_path_left,
|
||||
longest_leaf_path_left, tree_depth_left, expect_visited, verifier);
|
||||
longest_leaf_path_left, tree_depth_left, expect_visited, verifier, extra_verifier);
|
||||
}
|
||||
|
||||
size_t num_black_nodes_right = 0;
|
||||
@ -159,7 +160,7 @@ inline void IntrusiveRBNode::verify(
|
||||
assert(is_black() || _left->is_black(), "2 red nodes in a row");
|
||||
assert(_right->parent() == this, "pointer mismatch");
|
||||
_right->verify<NodeType>(num_nodes, num_black_nodes_right, shortest_leaf_path_right,
|
||||
longest_leaf_path_right, tree_depth_right, expect_visited, verifier);
|
||||
longest_leaf_path_right, tree_depth_right, expect_visited, verifier, extra_verifier);
|
||||
}
|
||||
|
||||
shortest_leaf_path = MAX2(longest_leaf_path_left, longest_leaf_path_right);
|
||||
@ -190,13 +191,13 @@ AbstractRBTree<K, NodeType, COMPARATOR>::cursor(const K& key, const NodeType* hi
|
||||
IntrusiveRBNode* const* insert_location = &_root;
|
||||
|
||||
if (hint_node != nullptr) {
|
||||
const int hint_cmp = cmp(key, hint_node);
|
||||
const RBTreeOrdering hint_cmp = cmp(key, hint_node);
|
||||
while (hint_node->parent() != nullptr) {
|
||||
const int parent_cmp = cmp(key, (NodeType*)hint_node->parent());
|
||||
const RBTreeOrdering parent_cmp = cmp(key, (NodeType*)hint_node->parent());
|
||||
// Move up until the parent would put us on the other side of the key.
|
||||
// Meaning we are in the correct subtree.
|
||||
if ((parent_cmp <= 0 && hint_cmp < 0) ||
|
||||
(parent_cmp >= 0 && hint_cmp > 0)) {
|
||||
if ((parent_cmp != RBTreeOrdering::GT && hint_cmp == RBTreeOrdering::LT) ||
|
||||
(parent_cmp != RBTreeOrdering::LT && hint_cmp == RBTreeOrdering::GT)) {
|
||||
hint_node = (NodeType*)hint_node->parent();
|
||||
} else {
|
||||
break;
|
||||
@ -212,14 +213,14 @@ AbstractRBTree<K, NodeType, COMPARATOR>::cursor(const K& key, const NodeType* hi
|
||||
|
||||
while (*insert_location != nullptr) {
|
||||
NodeType* curr = (NodeType*)*insert_location;
|
||||
const int key_cmp_k = cmp(key, curr);
|
||||
const RBTreeOrdering key_cmp_k = cmp(key, curr);
|
||||
|
||||
if (key_cmp_k == 0) {
|
||||
if (key_cmp_k == RBTreeOrdering::EQ) {
|
||||
break;
|
||||
}
|
||||
|
||||
parent = *insert_location;
|
||||
if (key_cmp_k < 0) {
|
||||
if (key_cmp_k == RBTreeOrdering::LT) {
|
||||
insert_location = &curr->_left;
|
||||
} else {
|
||||
insert_location = &curr->_right;
|
||||
@ -551,19 +552,19 @@ inline void AbstractRBTree<K, NodeType, COMPARATOR>::replace_at_cursor(NodeType*
|
||||
new_node->_parent = old_node->_parent;
|
||||
|
||||
if (new_node->is_left_child()) {
|
||||
assert(cmp(static_cast<const NodeType*>(new_node), static_cast<const NodeType*>(new_node->parent())), "new node not < parent");
|
||||
assert(less_than(static_cast<const NodeType*>(new_node), static_cast<const NodeType*>(new_node->parent())), "new node not < parent");
|
||||
} else if (new_node->is_right_child()) {
|
||||
assert(cmp(static_cast<const NodeType*>(new_node->parent()), static_cast<const NodeType*>(new_node)), "new node not > parent");
|
||||
assert(less_than(static_cast<const NodeType*>(new_node->parent()), static_cast<const NodeType*>(new_node)), "new node not > parent");
|
||||
}
|
||||
|
||||
new_node->_left = old_node->_left;
|
||||
new_node->_right = old_node->_right;
|
||||
if (new_node->_left != nullptr) {
|
||||
assert(cmp(static_cast<const NodeType*>(new_node->_left), static_cast<const NodeType*>(new_node)), "left child not < new node");
|
||||
assert(less_than(static_cast<const NodeType*>(new_node->_left), static_cast<const NodeType*>(new_node)), "left child not < new node");
|
||||
new_node->_left->set_parent(new_node);
|
||||
}
|
||||
if (new_node->_right != nullptr) {
|
||||
assert(cmp(static_cast<const NodeType*>(new_node), static_cast<const NodeType*>(new_node->_right)), "right child not > new node");
|
||||
assert(less_than(static_cast<const NodeType*>(new_node), static_cast<const NodeType*>(new_node->_right)), "right child not > new node");
|
||||
new_node->_right->set_parent(new_node);
|
||||
}
|
||||
|
||||
@ -661,8 +662,8 @@ inline void AbstractRBTree<K, NodeType, COMPARATOR>::visit_range_in_order(const
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
template <typename NodeVerifier>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::verify_self(NodeVerifier verifier) const {
|
||||
template <typename NODE_VERIFIER, typename USER_VERIFIER>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::verify_self(NODE_VERIFIER verifier, const USER_VERIFIER& extra_verifier) const {
|
||||
if (_root == nullptr) {
|
||||
assert(_num_nodes == 0, "rbtree has %zu nodes but no root", _num_nodes);
|
||||
return;
|
||||
@ -679,7 +680,7 @@ inline void AbstractRBTree<K, NodeType, COMPARATOR>::verify_self(NodeVerifier ve
|
||||
bool expected_visited = DEBUG_ONLY(_expected_visited) NOT_DEBUG(false);
|
||||
|
||||
_root->verify<NodeType>(num_nodes, black_depth, shortest_leaf_path, longest_leaf_path,
|
||||
tree_depth, expected_visited, verifier);
|
||||
tree_depth, expected_visited, verifier, extra_verifier);
|
||||
|
||||
const unsigned int maximum_depth = log2i(size() + 1) * 2;
|
||||
|
||||
@ -731,21 +732,23 @@ inline void RBNode<K, V>::print_on(outputStream* st, int depth) const {
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
void AbstractRBTree<K, NodeType, COMPARATOR>::print_node_on(outputStream* st, int depth, const NodeType* n) const {
|
||||
n->print_on(st, depth);
|
||||
template <typename PRINTER>
|
||||
void AbstractRBTree<K, NodeType, COMPARATOR>::print_node_on(outputStream* st, int depth, const NodeType* n, const PRINTER& node_printer) const {
|
||||
node_printer(st, n, depth);
|
||||
depth++;
|
||||
if (n->_right != nullptr) {
|
||||
print_node_on(st, depth, (NodeType*)n->_right);
|
||||
}
|
||||
if (n->_left != nullptr) {
|
||||
print_node_on(st, depth, (NodeType*)n->_left);
|
||||
print_node_on(st, depth, (NodeType*)n->_left, node_printer);
|
||||
}
|
||||
if (n->_right != nullptr) {
|
||||
print_node_on(st, depth, (NodeType*)n->_right, node_printer);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
void AbstractRBTree<K, NodeType, COMPARATOR>::print_on(outputStream* st) const {
|
||||
template <typename PRINTER>
|
||||
void AbstractRBTree<K, NodeType, COMPARATOR>::print_on(outputStream* st, const PRINTER& node_printer) const {
|
||||
if (_root != nullptr) {
|
||||
print_node_on(st, 0, (NodeType*)_root);
|
||||
print_node_on(st, 0, (NodeType*)_root, node_printer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,26 +36,30 @@ public:
|
||||
using RBTreeIntNode = RBNode<int, int>;
|
||||
|
||||
struct Cmp {
|
||||
static int cmp(int a, int b) {
|
||||
return a - b;
|
||||
static RBTreeOrdering cmp(int a, int b) {
|
||||
if (a < b) return RBTreeOrdering::LT;
|
||||
if (a > b) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
|
||||
static bool cmp(const RBTreeIntNode* a, const RBTreeIntNode* b) {
|
||||
static bool less_than(const RBTreeIntNode* a, const RBTreeIntNode* b) {
|
||||
return a->key() < b->key();
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpInverse {
|
||||
static int cmp(int a, int b) {
|
||||
return b - a;
|
||||
static RBTreeOrdering cmp(int a, int b) {
|
||||
if (a < b) return RBTreeOrdering::GT;
|
||||
if (a > b) return RBTreeOrdering::LT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
};
|
||||
|
||||
struct FCmp {
|
||||
static int cmp(float a, float b) {
|
||||
if (a < b) return -1;
|
||||
if (a == b) return 0;
|
||||
return 1;
|
||||
static RBTreeOrdering cmp(float a, float b) {
|
||||
if (a < b) return RBTreeOrdering::LT;
|
||||
if (a > b) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
};
|
||||
|
||||
@ -94,16 +98,15 @@ struct ArrayAllocator {
|
||||
};
|
||||
|
||||
struct IntrusiveCmp {
|
||||
static int cmp(int a, const IntrusiveTreeNode* b) {
|
||||
return a - IntrusiveHolder::cast_to_self(b)->key;
|
||||
}
|
||||
|
||||
static int cmp(int a, int b) {
|
||||
return a - b;
|
||||
static RBTreeOrdering cmp(int a, const IntrusiveTreeNode* b_node) {
|
||||
int b = IntrusiveHolder::cast_to_self(b_node)->key;
|
||||
if (a < b) return RBTreeOrdering::LT;
|
||||
if (a > b) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
|
||||
// true if a < b
|
||||
static bool cmp(const IntrusiveTreeNode* a, const IntrusiveTreeNode* b) {
|
||||
static bool less_than(const IntrusiveTreeNode* a, const IntrusiveTreeNode* b) {
|
||||
return (IntrusiveHolder::cast_to_self(a)->key -
|
||||
IntrusiveHolder::cast_to_self(b)->key) < 0;
|
||||
}
|
||||
@ -837,6 +840,50 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static bool custom_validator(const IntrusiveRBNode* n) {
|
||||
IntrusiveHolder* holder = IntrusiveHolder::cast_to_self(n);
|
||||
assert(holder->key == holder->data, "must be");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_custom_verify_intrusive() {
|
||||
IntrusiveTreeInt intrusive_tree;
|
||||
int num_nodes = 100;
|
||||
|
||||
// Insert values
|
||||
for (int n = 0; n < num_nodes; n++) {
|
||||
IntrusiveCursor cursor = intrusive_tree.cursor(n);
|
||||
EXPECT_NULL(cursor.node());
|
||||
|
||||
// Custom allocation here is just malloc
|
||||
IntrusiveHolder* place = (IntrusiveHolder*)os::malloc(sizeof(IntrusiveHolder), mtTest);
|
||||
new (place) IntrusiveHolder(n, n);
|
||||
|
||||
intrusive_tree.insert_at_cursor(place->get_node(), cursor);
|
||||
IntrusiveCursor cursor2 = intrusive_tree.cursor(n);
|
||||
|
||||
EXPECT_NOT_NULL(cursor2.node());
|
||||
}
|
||||
|
||||
intrusive_tree.verify_self(RBTreeTest::custom_validator);
|
||||
|
||||
int node_count = 0;
|
||||
intrusive_tree.verify_self([&](const IntrusiveRBNode* n) {
|
||||
node_count++;
|
||||
|
||||
IntrusiveHolder* holder = IntrusiveHolder::cast_to_self(n);
|
||||
assert(holder->key >= 0, "must be");
|
||||
assert(holder->data >= 0, "must be");
|
||||
assert(holder->key < num_nodes, "must be");
|
||||
assert(holder->data < num_nodes, "must be");
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
EXPECT_EQ(node_count, num_nodes);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
void test_nodes_visited_once() {
|
||||
constexpr size_t memory_size = 65536;
|
||||
@ -945,10 +992,13 @@ TEST_VM_F(RBTreeTest, LeftMostRightMost) {
|
||||
}
|
||||
|
||||
struct PtrCmp {
|
||||
static int cmp(const void* a, const void* b) {
|
||||
static RBTreeOrdering cmp(const void* a, const void* b) {
|
||||
const uintptr_t ai = p2u(a);
|
||||
const uintptr_t bi = p2u(b);
|
||||
return ai == bi ? 0 : (ai > bi ? 1 : -1);
|
||||
|
||||
if (ai < bi) return RBTreeOrdering::LT;
|
||||
if (ai > bi) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
};
|
||||
|
||||
@ -982,7 +1032,11 @@ TEST_VM(RBTreeTestNonFixture, TestPrintPointerTree) {
|
||||
}
|
||||
|
||||
struct IntCmp {
|
||||
static int cmp(int a, int b) { return a == b ? 0 : (a > b ? 1 : -1); }
|
||||
static RBTreeOrdering cmp(int a, int b) {
|
||||
if (a < b) return RBTreeOrdering::LT;
|
||||
if (a > b) return RBTreeOrdering::GT;
|
||||
return RBTreeOrdering::EQ;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_VM(RBTreeTestNonFixture, TestPrintIntegerTree) {
|
||||
@ -1005,10 +1059,42 @@ TEST_VM(RBTreeTestNonFixture, TestPrintIntegerTree) {
|
||||
ASSERT_NE(strstr(ss.base(), s3), N);
|
||||
}
|
||||
|
||||
TEST_VM(RBTreeTestNonFixture, TestPrintCustomPrinter) {
|
||||
typedef RBTree<int, unsigned, IntCmp, RBTreeCHeapAllocator<mtTest> > TreeType;
|
||||
typedef RBNode<int, unsigned> NodeType;
|
||||
|
||||
TreeType tree;
|
||||
const int i1 = -13591;
|
||||
const int i2 = 0;
|
||||
const int i3 = 82924;
|
||||
tree.upsert(i1, 1U);
|
||||
tree.upsert(i2, 2U);
|
||||
tree.upsert(i3, 3U);
|
||||
|
||||
stringStream ss;
|
||||
int print_count = 0;
|
||||
tree.print_on(&ss, [&](outputStream* st, const NodeType* n, int depth) {
|
||||
st->print_cr("[%d] (%d): %d", depth, n->val(), n->key());
|
||||
print_count++;
|
||||
});
|
||||
|
||||
const char* const expected =
|
||||
"[0] (2): 0\n"
|
||||
"[1] (1): -13591\n"
|
||||
"[1] (3): 82924\n";
|
||||
|
||||
ASSERT_EQ(print_count, 3);
|
||||
ASSERT_STREQ(ss.base(), expected);
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, IntrusiveTest) {
|
||||
this->test_intrusive();
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, IntrusiveCustomVerifyTest) {
|
||||
this->test_custom_verify_intrusive();
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, FillAndVerify) {
|
||||
this->test_fill_verify();
|
||||
}
|
||||
@ -1021,6 +1107,22 @@ TEST_VM_F(RBTreeTest, CursorReplace) {
|
||||
TEST_VM_F(RBTreeTest, NodesVisitedOnce) {
|
||||
this->test_nodes_visited_once();
|
||||
}
|
||||
|
||||
TEST_VM_ASSERT_MSG(RBTreeTestNonFixture, CustomVerifyAssert, ".*failed on key = 7") {
|
||||
typedef RBTreeCHeap<int, int, IntCmp, mtTest> TreeType;
|
||||
typedef RBNode<int, int> NodeType;
|
||||
|
||||
TreeType tree;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
tree.upsert(i, i);
|
||||
}
|
||||
|
||||
tree.verify_self([&](const NodeType* n) {
|
||||
assert(n->key() != 7, "failed on key = %d", n->key());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
#endif // ASSERT
|
||||
|
||||
TEST_VM_F(RBTreeTest, InsertRemoveVerify) {
|
||||
@ -1039,6 +1141,25 @@ TEST_VM_F(RBTreeTest, InsertRemoveVerify) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, CustomVerify) {
|
||||
constexpr int num_nodes = 1000;
|
||||
RBTreeInt tree;
|
||||
for (int i = 0; i < num_nodes; i++) {
|
||||
tree.upsert(i, i);
|
||||
}
|
||||
|
||||
int node_count = 0;
|
||||
tree.verify_self([&](const RBTreeIntNode* n) {
|
||||
node_count++;
|
||||
|
||||
assert(n->key() >= 0, "must be");
|
||||
assert(n->key() < num_nodes, "must be");
|
||||
return true;
|
||||
});
|
||||
|
||||
EXPECT_EQ(node_count, num_nodes);
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, VerifyItThroughStressTest) {
|
||||
{ // Repeatedly verify a tree of moderate size
|
||||
RBTreeInt rbtree;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user