Skip to content

Commit 2efb6aa

Browse files
committedJan 30, 2025
8345314: Add a red–black tree as a utility data structure
Reviewed-by: aboldtch, jsjolen, stuefe
1 parent a937f6d commit 2efb6aa

File tree

3 files changed

+1455
-0
lines changed

3 files changed

+1455
-0
lines changed
 
+341
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#ifndef SHARE_UTILITIES_RBTREE_HPP
26+
#define SHARE_UTILITIES_RBTREE_HPP
27+
28+
#include "nmt/memTag.hpp"
29+
#include "runtime/os.hpp"
30+
#include "utilities/globalDefinitions.hpp"
31+
#include <type_traits>
32+
33+
// COMPARATOR must have a static function `cmp(a,b)` which returns:
34+
// - an int < 0 when a < b
35+
// - an int == 0 when a == b
36+
// - an int > 0 when a > b
37+
// ALLOCATOR must check for oom and exit, as RBTree currently does not handle the
38+
// allocation failing.
39+
// Key needs to be of a type that is trivially destructible.
40+
// The tree will call a value's destructor when its node is removed.
41+
// Nodes are address stable and will not change during its lifetime.
42+
43+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
44+
class RBTree {
45+
friend class RBTreeTest;
46+
47+
private:
48+
ALLOCATOR _allocator;
49+
size_t _num_nodes;
50+
51+
public:
52+
class RBNode {
53+
friend RBTree;
54+
friend class RBTreeTest;
55+
56+
private:
57+
uintptr_t _parent; // LSB encodes color information. 0 = RED, 1 = BLACK
58+
RBNode* _left;
59+
RBNode* _right;
60+
61+
const K _key;
62+
V _value;
63+
64+
DEBUG_ONLY(bool _visited);
65+
66+
public:
67+
const K& key() const { return _key; }
68+
V& val() { return _value; }
69+
const V& val() const { return _value; }
70+
71+
private:
72+
bool is_black() const { return (_parent & 0x1) != 0; }
73+
bool is_red() const { return (_parent & 0x1) == 0; }
74+
75+
void set_black() { _parent |= 0x1; }
76+
void set_red() { _parent &= ~0x1; }
77+
78+
RBNode* parent() const { return (RBNode*)(_parent & ~0x1); }
79+
void set_parent(RBNode* new_parent) { _parent = (_parent & 0x1) | (uintptr_t)new_parent; }
80+
81+
RBNode(const K& key, const V& val DEBUG_ONLY(COMMA bool visited))
82+
: _parent(0), _left(nullptr), _right(nullptr),
83+
_key(key), _value(val) DEBUG_ONLY(COMMA _visited(visited)) {}
84+
85+
bool is_right_child() const {
86+
return parent() != nullptr && parent()->_right == this;
87+
}
88+
89+
bool is_left_child() const {
90+
return parent() != nullptr && parent()->_left == this;
91+
}
92+
93+
void replace_child(RBNode* old_child, RBNode* new_child);
94+
95+
// This node down, right child up
96+
// Returns right child (now parent)
97+
RBNode* rotate_left();
98+
99+
// This node down, left child up
100+
// Returns left child (now parent)
101+
RBNode* rotate_right();
102+
103+
RBNode* prev();
104+
105+
RBNode* next();
106+
107+
#ifdef ASSERT
108+
void verify(size_t& num_nodes, size_t& black_nodes_until_leaf,
109+
size_t& shortest_leaf_path, size_t& longest_leaf_path,
110+
size_t& tree_depth, bool expect_visited);
111+
#endif // ASSERT
112+
};
113+
114+
private:
115+
RBNode* _root;
116+
DEBUG_ONLY(bool _expected_visited);
117+
118+
RBNode* allocate_node(const K& key, const V& val) {
119+
void* node_place = _allocator.allocate(sizeof(RBNode));
120+
assert(node_place != nullptr, "rb-tree allocator must exit on failure");
121+
_num_nodes++;
122+
return new (node_place) RBNode(key, val DEBUG_ONLY(COMMA _expected_visited));
123+
}
124+
125+
void free_node(RBNode* node) {
126+
node->_value.~V();
127+
_allocator.free(node);
128+
_num_nodes--;
129+
}
130+
131+
// True if node is black (nil nodes count as black)
132+
static inline bool is_black(const RBNode* node) {
133+
return node == nullptr || node->is_black();
134+
}
135+
136+
static inline bool is_red(const RBNode* node) {
137+
return node != nullptr && node->is_red();
138+
}
139+
140+
141+
// If the node with key k already exist, the value is updated instead.
142+
RBNode* insert_node(const K& key, const V& val);
143+
144+
void fix_insert_violations(RBNode* node);
145+
146+
void remove_black_leaf(RBNode* node);
147+
148+
// Assumption: node has at most one child. Two children is handled in `remove()`
149+
void remove_from_tree(RBNode* node);
150+
151+
public:
152+
NONCOPYABLE(RBTree);
153+
154+
RBTree() : _allocator(), _num_nodes(0), _root(nullptr) DEBUG_ONLY(COMMA _expected_visited(false)) {
155+
static_assert(std::is_trivially_destructible<K>::value, "key type must be trivially destructable");
156+
}
157+
~RBTree() { this->remove_all(); }
158+
159+
size_t size() { return _num_nodes; }
160+
161+
// Inserts a node with the given k/v into the tree,
162+
// if the key already exist, the value is updated instead.
163+
void upsert(const K& key, const V& val) {
164+
RBNode* node = insert_node(key, val);
165+
fix_insert_violations(node);
166+
}
167+
168+
// Removes the node with the given key from the tree if it exists.
169+
// Returns true if the node was successfully removed, false otherwise.
170+
bool remove(const K& key) {
171+
RBNode* node = find_node(key);
172+
if (node == nullptr){
173+
return false;
174+
}
175+
remove(node);
176+
return true;
177+
}
178+
179+
// Removes the given node from the tree. node must be a valid node
180+
void remove(RBNode* node);
181+
182+
// Removes all existing nodes from the tree.
183+
void remove_all() {
184+
RBNode* to_delete[64];
185+
int stack_idx = 0;
186+
to_delete[stack_idx++] = _root;
187+
188+
while (stack_idx > 0) {
189+
RBNode* head = to_delete[--stack_idx];
190+
if (head == nullptr) continue;
191+
to_delete[stack_idx++] = head->_left;
192+
to_delete[stack_idx++] = head->_right;
193+
free_node(head);
194+
}
195+
_num_nodes = 0;
196+
_root = nullptr;
197+
}
198+
199+
// Finds the node with the closest key <= the given key
200+
const RBNode* closest_leq(const K& key) const {
201+
RBNode* candidate = nullptr;
202+
RBNode* pos = _root;
203+
while (pos != nullptr) {
204+
const int cmp_r = COMPARATOR::cmp(pos->key(), key);
205+
if (cmp_r == 0) { // Exact match
206+
candidate = pos;
207+
break; // Can't become better than that.
208+
}
209+
if (cmp_r < 0) {
210+
// Found a match, try to find a better one.
211+
candidate = pos;
212+
pos = pos->_right;
213+
} else {
214+
pos = pos->_left;
215+
}
216+
}
217+
return candidate;
218+
}
219+
220+
// Finds the node with the closest key > the given key
221+
const RBNode* closest_gt(const K& key) const {
222+
RBNode* candidate = nullptr;
223+
RBNode* pos = _root;
224+
while (pos != nullptr) {
225+
const int cmp_r = COMPARATOR::cmp(pos->key(), key);
226+
if (cmp_r > 0) {
227+
// Found a match, try to find a better one.
228+
candidate = pos;
229+
pos = pos->_left;
230+
} else {
231+
pos = pos->_right;
232+
}
233+
}
234+
return candidate;
235+
}
236+
237+
// Finds the node with the closest key >= the given key
238+
const RBNode* closest_geq(const K& key) const {
239+
RBNode* candidate = nullptr;
240+
RBNode* pos = _root;
241+
while (pos != nullptr) {
242+
const int cmp_r = COMPARATOR::cmp(pos->key(), key);
243+
if (cmp_r == 0) { // Exact match
244+
candidate = pos;
245+
break; // Can't become better than that.
246+
}
247+
if (cmp_r > 0) {
248+
// Found a match, try to find a better one.
249+
candidate = pos;
250+
pos = pos->_left;
251+
} else {
252+
pos = pos->_right;
253+
}
254+
}
255+
return candidate;
256+
}
257+
258+
RBNode* closest_leq(const K& key) {
259+
return const_cast<RBNode*>(
260+
static_cast<const RBTree<K, V, COMPARATOR, ALLOCATOR>*>(this)->closest_leq(key));
261+
}
262+
263+
RBNode* closest_gt(const K& key) {
264+
return const_cast<RBNode*>(
265+
static_cast<const RBTree<K, V, COMPARATOR, ALLOCATOR>*>(this)->closest_gt(key));
266+
}
267+
268+
RBNode* closest_geq(const K& key) {
269+
return const_cast<RBNode*>(
270+
static_cast<const RBTree<K, V, COMPARATOR, ALLOCATOR>*>(this)->closest_geq(key));
271+
}
272+
273+
struct Range {
274+
RBNode* start;
275+
RBNode* end;
276+
Range(RBNode* start, RBNode* end)
277+
: start(start), end(end) {}
278+
};
279+
280+
// Return the range [start, end)
281+
// where start->key() <= addr < end->key().
282+
// Failure to find the range leads to start and/or end being null.
283+
Range find_enclosing_range(K key) {
284+
RBNode* start = closest_leq(key);
285+
RBNode* end = closest_gt(key);
286+
return Range(start, end);
287+
}
288+
289+
// Finds the node associated with the key
290+
const RBNode* find_node(const K& key) const;
291+
292+
RBNode* find_node(const K& key) {
293+
return const_cast<RBNode*>(
294+
static_cast<const RBTree<K, V, COMPARATOR, ALLOCATOR>*>(this)->find_node(key));
295+
}
296+
297+
// Finds the value associated with the key
298+
V* find(const K& key) {
299+
RBNode* node = find_node(key);
300+
return node == nullptr ? nullptr : &node->val();
301+
}
302+
303+
const V* find(const K& key) const {
304+
const RBNode* node = find_node(key);
305+
return node == nullptr ? nullptr : &node->val();
306+
}
307+
308+
// Visit all RBNodes in ascending order, calling f on each node.
309+
template <typename F>
310+
void visit_in_order(F f) const;
311+
312+
// Visit all RBNodes in ascending order whose keys are in range [from, to), calling f on each node.
313+
template <typename F>
314+
void visit_range_in_order(const K& from, const K& to, F f);
315+
316+
#ifdef ASSERT
317+
// Verifies that the tree is correct and holds rb-properties
318+
void verify_self();
319+
#endif // ASSERT
320+
321+
};
322+
323+
template <MemTag mem_tag>
324+
class RBTreeCHeapAllocator {
325+
public:
326+
void* allocate(size_t sz) {
327+
void* allocation = os::malloc(sz, mem_tag);
328+
if (allocation == nullptr) {
329+
vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR,
330+
"red-black tree failed allocation");
331+
}
332+
return allocation;
333+
}
334+
335+
void free(void* ptr) { os::free(ptr); }
336+
};
337+
338+
template <typename K, typename V, typename COMPARATOR, MemTag mem_tag>
339+
using RBTreeCHeap = RBTree<K, V, COMPARATOR, RBTreeCHeapAllocator<mem_tag>>;
340+
341+
#endif // SHARE_UTILITIES_RBTREE_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,541 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#ifndef SHARE_UTILITIES_RBTREE_INLINE_HPP
26+
#define SHARE_UTILITIES_RBTREE_INLINE_HPP
27+
28+
#include "utilities/debug.hpp"
29+
#include "utilities/globalDefinitions.hpp"
30+
#include "utilities/powerOfTwo.hpp"
31+
#include "utilities/rbTree.hpp"
32+
33+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
34+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode::replace_child(
35+
RBNode* old_child, RBNode* new_child) {
36+
if (_left == old_child) {
37+
_left = new_child;
38+
} else if (_right == old_child) {
39+
_right = new_child;
40+
} else {
41+
ShouldNotReachHere();
42+
}
43+
}
44+
45+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
46+
inline typename RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode*
47+
RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode::rotate_left() {
48+
// This node down, right child up
49+
RBNode* old_right = _right;
50+
51+
_right = old_right->_left;
52+
if (_right != nullptr) {
53+
_right->set_parent(this);
54+
}
55+
56+
old_right->set_parent(parent());
57+
if (parent() != nullptr) {
58+
parent()->replace_child(this, old_right);
59+
}
60+
61+
old_right->_left = this;
62+
set_parent(old_right);
63+
64+
return old_right;
65+
}
66+
67+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
68+
inline typename RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode*
69+
RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode::rotate_right() {
70+
// This node down, left child up
71+
RBNode* old_left = _left;
72+
73+
_left = old_left->_right;
74+
if (_left != nullptr) {
75+
_left->set_parent(this);
76+
}
77+
78+
old_left->set_parent(parent());
79+
if (parent() != nullptr) {
80+
parent()->replace_child(this, old_left);
81+
}
82+
83+
old_left->_right = this;
84+
set_parent(old_left);
85+
86+
return old_left;
87+
}
88+
89+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
90+
inline typename RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode*
91+
RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode::prev() {
92+
RBNode* node = this;
93+
if (_left != nullptr) { // right subtree exists
94+
node = _left;
95+
while (node->_right != nullptr) {
96+
node = node->_right;
97+
}
98+
return node;
99+
}
100+
101+
while (node != nullptr && node->is_left_child()) {
102+
node = node->parent();
103+
}
104+
return node->parent();
105+
}
106+
107+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
108+
inline typename RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode*
109+
RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode::next() {
110+
RBNode* node = this;
111+
if (_right != nullptr) { // right subtree exists
112+
node = _right;
113+
while (node->_left != nullptr) {
114+
node = node->_left;
115+
}
116+
return node;
117+
}
118+
119+
while (node != nullptr && node->is_right_child()) {
120+
node = node->parent();
121+
}
122+
return node->parent();
123+
}
124+
125+
#ifdef ASSERT
126+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
127+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode::verify(
128+
size_t& num_nodes, size_t& black_nodes_until_leaf, size_t& shortest_leaf_path, size_t& longest_leaf_path,
129+
size_t& tree_depth, bool expect_visited) {
130+
assert(expect_visited != _visited, "node already visited");
131+
_visited = !_visited;
132+
133+
size_t num_black_nodes_left = 0;
134+
size_t shortest_leaf_path_left = 0;
135+
size_t longest_leaf_path_left = 0;
136+
size_t tree_depth_left = 0;
137+
138+
if (_left != nullptr) {
139+
if (_right == nullptr) {
140+
assert(is_black() && _left->is_red(), "if one child it must be red and node black");
141+
}
142+
assert(COMPARATOR::cmp(_left->key(), _key) < 0, "left node must be less than parent");
143+
assert(is_black() || _left->is_black(), "2 red nodes in a row");
144+
assert(_left->parent() == this, "pointer mismatch");
145+
_left->verify(num_nodes, num_black_nodes_left, shortest_leaf_path_left,
146+
longest_leaf_path_left, tree_depth_left, expect_visited);
147+
}
148+
149+
size_t num_black_nodes_right = 0;
150+
size_t shortest_leaf_path_right = 0;
151+
size_t longest_leaf_path_right = 0;
152+
size_t tree_depth_right = 0;
153+
154+
if (_right != nullptr) {
155+
if (_left == nullptr) {
156+
assert(is_black() && _right->is_red(), "if one child it must be red and node black");
157+
}
158+
assert(COMPARATOR::cmp(_right->key(), _key) > 0, "right node must be greater than parent");
159+
assert(is_black() || _left->is_black(), "2 red nodes in a row");
160+
assert(_right->parent() == this, "pointer mismatch");
161+
_right->verify(num_nodes, num_black_nodes_right, shortest_leaf_path_right,
162+
longest_leaf_path_right, tree_depth_right, expect_visited);
163+
}
164+
165+
shortest_leaf_path = MAX2(longest_leaf_path_left, longest_leaf_path_right);
166+
longest_leaf_path = MAX2(longest_leaf_path_left, longest_leaf_path_right);
167+
168+
assert(shortest_leaf_path <= longest_leaf_path && longest_leaf_path <= shortest_leaf_path * 2,
169+
"tree imbalanced, shortest path: %zu longest: %zu", shortest_leaf_path, longest_leaf_path);
170+
assert(num_black_nodes_left == num_black_nodes_right,
171+
"number of black nodes in left/right subtree should match");
172+
173+
num_nodes++;
174+
tree_depth = 1 + MAX2(tree_depth_left, tree_depth_right);
175+
176+
shortest_leaf_path++;
177+
longest_leaf_path++;
178+
179+
black_nodes_until_leaf = num_black_nodes_left;
180+
if (is_black()) {
181+
black_nodes_until_leaf++;
182+
}
183+
184+
}
185+
186+
#endif // ASSERT
187+
188+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
189+
inline const typename RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode*
190+
RBTree<K, V, COMPARATOR, ALLOCATOR>::find_node(const K& key) const {
191+
RBNode* curr = _root;
192+
while (curr != nullptr) {
193+
const int key_cmp_k = COMPARATOR::cmp(key, curr->key());
194+
195+
if (key_cmp_k == 0) {
196+
return curr;
197+
} else if (key_cmp_k < 0) {
198+
curr = curr->_left;
199+
} else {
200+
curr = curr->_right;
201+
}
202+
}
203+
204+
return nullptr;
205+
}
206+
207+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
208+
inline typename RBTree<K, V, COMPARATOR, ALLOCATOR>::RBNode*
209+
RBTree<K, V, COMPARATOR, ALLOCATOR>::insert_node(const K& key, const V& val) {
210+
RBNode* curr = _root;
211+
if (curr == nullptr) { // Tree is empty
212+
_root = allocate_node(key, val);
213+
return _root;
214+
}
215+
216+
RBNode* parent = nullptr;
217+
while (curr != nullptr) {
218+
const int key_cmp_k = COMPARATOR::cmp(key, curr->key());
219+
220+
if (key_cmp_k == 0) {
221+
curr->_value = val;
222+
return curr;
223+
}
224+
225+
parent = curr;
226+
if (key_cmp_k < 0) {
227+
curr = curr->_left;
228+
} else {
229+
curr = curr->_right;
230+
}
231+
}
232+
233+
// Create and insert new node
234+
RBNode* node = allocate_node(key, val);
235+
node->set_parent(parent);
236+
237+
const int key_cmp_k = COMPARATOR::cmp(key, parent->key());
238+
if (key_cmp_k < 0) {
239+
parent->_left = node;
240+
} else {
241+
parent->_right = node;
242+
}
243+
244+
return node;
245+
}
246+
247+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
248+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::fix_insert_violations(RBNode* node) {
249+
if (node->is_black()) { // node's value was updated
250+
return; // Tree is already correct
251+
}
252+
253+
RBNode* parent = node->parent();
254+
while (parent != nullptr && parent->is_red()) {
255+
// Node and parent are both red, creating a red-violation
256+
257+
RBNode* grandparent = parent->parent();
258+
if (grandparent == nullptr) { // Parent is the tree root
259+
assert(parent == _root, "parent must be root");
260+
parent->set_black(); // Color parent black to eliminate the red-violation
261+
return;
262+
}
263+
264+
RBNode* uncle = parent->is_left_child() ? grandparent->_right : grandparent->_left;
265+
if (is_black(uncle)) { // Parent is red, uncle is black
266+
// Rotate the parent to the position of the grandparent
267+
if (parent->is_left_child()) {
268+
if (node->is_right_child()) { // Node is an "inner" node
269+
// Rotate and swap node and parent to make it an "outer" node
270+
parent->rotate_left();
271+
parent = node;
272+
}
273+
grandparent->rotate_right(); // Rotate the parent to the position of the grandparent
274+
} else if (parent->is_right_child()) {
275+
if (node->is_left_child()) { // Node is an "inner" node
276+
// Rotate and swap node and parent to make it an "outer" node
277+
parent->rotate_right();
278+
parent = node;
279+
}
280+
grandparent->rotate_left(); // Rotate the parent to the position of the grandparent
281+
}
282+
283+
// Swap parent and grandparent colors to eliminate the red-violation
284+
parent->set_black();
285+
grandparent->set_red();
286+
287+
if (_root == grandparent) {
288+
_root = parent;
289+
}
290+
291+
return;
292+
}
293+
294+
// Parent and uncle are both red
295+
// Paint both black, paint grandparent red to not create a black-violation
296+
parent->set_black();
297+
uncle->set_black();
298+
grandparent->set_red();
299+
300+
// Move up two levels to check for new potential red-violation
301+
node = grandparent;
302+
parent = grandparent->parent();
303+
}
304+
}
305+
306+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
307+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::remove_black_leaf(RBNode* node) {
308+
// Black node removed, balancing needed
309+
RBNode* parent = node->parent();
310+
while (parent != nullptr) {
311+
// Sibling must exist. If it did not, node would need to be red to not break
312+
// tree properties, and could be trivially removed before reaching here
313+
RBNode* sibling = node->is_left_child() ? parent->_right : parent->_left;
314+
if (is_red(sibling)) { // Sibling red, parent and nephews must be black
315+
assert(is_black(parent), "parent must be black");
316+
assert(is_black(sibling->_left), "nephew must be black");
317+
assert(is_black(sibling->_right), "nephew must be black");
318+
// Swap parent and sibling colors
319+
parent->set_red();
320+
sibling->set_black();
321+
322+
// Rotate parent down and sibling up
323+
if (node->is_left_child()) {
324+
parent->rotate_left();
325+
sibling = parent->_right;
326+
} else {
327+
parent->rotate_right();
328+
sibling = parent->_left;
329+
}
330+
331+
if (_root == parent) {
332+
_root = parent->parent();
333+
}
334+
// Further balancing needed
335+
}
336+
337+
RBNode* close_nephew = node->is_left_child() ? sibling->_left : sibling->_right;
338+
RBNode* distant_nephew = node->is_left_child() ? sibling->_right : sibling->_left;
339+
if (is_red(distant_nephew) || is_red(close_nephew)) {
340+
if (is_black(distant_nephew)) { // close red, distant black
341+
// Rotate sibling down and inner nephew up
342+
if (node->is_left_child()) {
343+
sibling->rotate_right();
344+
} else {
345+
sibling->rotate_left();
346+
}
347+
348+
distant_nephew = sibling;
349+
sibling = close_nephew;
350+
351+
distant_nephew->set_red();
352+
sibling->set_black();
353+
}
354+
355+
// Distant nephew red
356+
// Rotate parent down and sibling up
357+
if (node->is_left_child()) {
358+
parent->rotate_left();
359+
} else {
360+
parent->rotate_right();
361+
}
362+
if (_root == parent) {
363+
_root = sibling;
364+
}
365+
366+
// Swap parent and sibling colors
367+
if (parent->is_black()) {
368+
sibling->set_black();
369+
} else {
370+
sibling->set_red();
371+
}
372+
parent->set_black();
373+
374+
// Color distant nephew black to restore black balance
375+
distant_nephew->set_black();
376+
return;
377+
}
378+
379+
if (is_red(parent)) { // parent red, sibling and nephews black
380+
// Swap parent and sibling colors to restore black balance
381+
sibling->set_red();
382+
parent->set_black();
383+
return;
384+
}
385+
386+
// Parent, sibling, and both nephews black
387+
// Color sibling red and move up one level
388+
sibling->set_red();
389+
node = parent;
390+
parent = node->parent();
391+
}
392+
}
393+
394+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
395+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::remove_from_tree(RBNode* node) {
396+
RBNode* parent = node->parent();
397+
RBNode* left = node->_left;
398+
RBNode* right = node->_right;
399+
if (left != nullptr) { // node has a left only-child
400+
// node must be black, and child red, otherwise a black-violation would
401+
// exist Remove node and color the child black.
402+
assert(right == nullptr, "right must be nullptr");
403+
assert(is_black(node), "node must be black");
404+
assert(is_red(left), "child must be red");
405+
left->set_black();
406+
left->set_parent(parent);
407+
if (parent == nullptr) {
408+
assert(node == _root, "node must be root");
409+
_root = left;
410+
} else {
411+
parent->replace_child(node, left);
412+
}
413+
} else if (right != nullptr) { // node has a right only-child
414+
// node must be black, and child red, otherwise a black-violation would
415+
// exist Remove node and color the child black.
416+
assert(left == nullptr, "left must be nullptr");
417+
assert(is_black(node), "node must be black");
418+
assert(is_red(right), "child must be red");
419+
right->set_black();
420+
right->set_parent(parent);
421+
if (parent == nullptr) {
422+
assert(node == _root, "node must be root");
423+
_root = right;
424+
} else {
425+
parent->replace_child(node, right);
426+
}
427+
} else { // node has no children
428+
if (node == _root) { // Tree empty
429+
_root = nullptr;
430+
} else {
431+
if (is_black(node)) {
432+
// Removed node is black, creating a black imbalance
433+
remove_black_leaf(node);
434+
}
435+
parent->replace_child(node, nullptr);
436+
}
437+
}
438+
}
439+
440+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
441+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::remove(RBNode* node) {
442+
assert(node != nullptr, "must be");
443+
444+
if (node->_left != nullptr && node->_right != nullptr) { // node has two children
445+
// Swap place with the in-order successor and delete there instead
446+
RBNode* curr = node->_right;
447+
while (curr->_left != nullptr) {
448+
curr = curr->_left;
449+
}
450+
451+
if (_root == node) _root = curr;
452+
453+
swap(curr->_left, node->_left);
454+
swap(curr->_parent, node->_parent); // Swaps parent and color
455+
456+
// If node is curr's parent, parent and right pointers become invalid
457+
if (node->_right == curr) {
458+
node->_right = curr->_right;
459+
node->set_parent(curr);
460+
curr->_right = node;
461+
} else {
462+
swap(curr->_right, node->_right);
463+
node->parent()->replace_child(curr, node);
464+
curr->_right->set_parent(curr);
465+
}
466+
467+
if (curr->parent() != nullptr) curr->parent()->replace_child(node, curr);
468+
curr->_left->set_parent(curr);
469+
470+
471+
if (node->_left != nullptr) node->_left->set_parent(node);
472+
if (node->_right != nullptr) node->_right->set_parent(node);
473+
}
474+
475+
remove_from_tree(node);
476+
free_node(node);
477+
}
478+
479+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
480+
template <typename F>
481+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::visit_in_order(F f) const {
482+
RBNode* to_visit[64];
483+
int stack_idx = 0;
484+
RBNode* head = _root;
485+
while (stack_idx > 0 || head != nullptr) {
486+
while (head != nullptr) {
487+
to_visit[stack_idx++] = head;
488+
head = head->_left;
489+
}
490+
head = to_visit[--stack_idx];
491+
f(head);
492+
head = head->_right;
493+
}
494+
}
495+
496+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
497+
template <typename F>
498+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::visit_range_in_order(const K& from, const K& to, F f) {
499+
assert(COMPARATOR::cmp(from, to) <= 0, "from must be less or equal to to");
500+
RBNode* curr = closest_geq(from);
501+
if (curr == nullptr) return;
502+
RBNode* end = closest_geq(to);
503+
504+
while (curr != nullptr && curr != end) {
505+
f(curr);
506+
curr = curr->next();
507+
}
508+
}
509+
510+
#ifdef ASSERT
511+
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
512+
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::verify_self() {
513+
if (_root == nullptr) {
514+
assert(_num_nodes == 0, "rbtree has nodes but no root");
515+
return;
516+
}
517+
518+
assert(_root->parent() == nullptr, "root of rbtree has a parent");
519+
520+
size_t num_nodes = 0;
521+
size_t black_depth = 0;
522+
size_t tree_depth = 0;
523+
size_t shortest_leaf_path = 0;
524+
size_t longest_leaf_path = 0;
525+
_expected_visited = !_expected_visited;
526+
527+
_root->verify(num_nodes, black_depth, shortest_leaf_path, longest_leaf_path, tree_depth, _expected_visited);
528+
529+
const unsigned int maximum_depth = log2i(size() + 1) * 2;
530+
531+
assert(shortest_leaf_path <= longest_leaf_path && longest_leaf_path <= shortest_leaf_path * 2,
532+
"tree imbalanced, shortest path: %zu longest: %zu",
533+
shortest_leaf_path, longest_leaf_path);
534+
assert(tree_depth <= maximum_depth, "rbtree is too deep");
535+
assert(size() == num_nodes,
536+
"unexpected number of nodes in rbtree. expected: %zu"
537+
", actual: %zu", size(), num_nodes);
538+
}
539+
#endif // ASSERT
540+
541+
#endif // SHARE_UTILITIES_RBTREE_INLINE_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,573 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "memory/resourceArea.hpp"
26+
#include "runtime/os.hpp"
27+
#include "testutils.hpp"
28+
#include "unittest.hpp"
29+
#include "utilities/growableArray.hpp"
30+
#include "utilities/rbTree.hpp"
31+
#include "utilities/rbTree.inline.hpp"
32+
33+
34+
class RBTreeTest : public testing::Test {
35+
public:
36+
struct Cmp {
37+
static int cmp(int a, int b) {
38+
return a - b;
39+
}
40+
};
41+
42+
struct CmpInverse {
43+
static int cmp(int a, int b) {
44+
return b - a;
45+
}
46+
};
47+
48+
struct FCmp {
49+
static int cmp(float a, float b) {
50+
if (a < b) return -1;
51+
if (a == b) return 0;
52+
return 1;
53+
}
54+
};
55+
56+
// Bump-pointer style allocator that can't free
57+
template <size_t AreaSize>
58+
struct ArrayAllocator {
59+
uint8_t area[AreaSize];
60+
size_t offset = 0;
61+
62+
void* allocate(size_t sz) {
63+
if (offset + sz > AreaSize) {
64+
vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR,
65+
"red-black tree failed allocation");
66+
}
67+
void* place = &area[offset];
68+
offset += sz;
69+
return place;
70+
}
71+
72+
void free(void* ptr) { }
73+
};
74+
75+
#ifdef ASSERT
76+
template<typename K, typename V, typename CMP, typename ALLOC>
77+
void verify_it(RBTree<K, V, CMP, ALLOC>& t) {
78+
t.verify_self();
79+
}
80+
#endif // ASSERT
81+
82+
using RBTreeInt = RBTreeCHeap<int, int, Cmp, mtOther>;
83+
84+
public:
85+
void inserting_duplicates_results_in_one_value() {
86+
constexpr int up_to = 10;
87+
GrowableArrayCHeap<int, mtTest> nums_seen(up_to, up_to, 0);
88+
RBTreeInt rbtree;
89+
90+
for (int i = 0; i < up_to; i++) {
91+
rbtree.upsert(i, i);
92+
rbtree.upsert(i, i);
93+
rbtree.upsert(i, i);
94+
rbtree.upsert(i, i);
95+
rbtree.upsert(i, i);
96+
}
97+
98+
rbtree.visit_in_order([&](RBTreeInt::RBNode* node) {
99+
nums_seen.at(node->key())++;
100+
});
101+
for (int i = 0; i < up_to; i++) {
102+
EXPECT_EQ(1, nums_seen.at(i));
103+
}
104+
}
105+
106+
void rbtree_ought_not_leak() {
107+
struct LeakCheckedAllocator {
108+
int allocations;
109+
110+
LeakCheckedAllocator()
111+
: allocations(0) {
112+
}
113+
114+
void* allocate(size_t sz) {
115+
void* allocation = os::malloc(sz, mtTest);
116+
if (allocation == nullptr) {
117+
vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR, "rbtree failed allocation");
118+
}
119+
++allocations;
120+
return allocation;
121+
}
122+
123+
void free(void* ptr) {
124+
--allocations;
125+
os::free(ptr);
126+
}
127+
};
128+
129+
constexpr int up_to = 10;
130+
{
131+
RBTree<int, int, Cmp, LeakCheckedAllocator> rbtree;
132+
for (int i = 0; i < up_to; i++) {
133+
rbtree.upsert(i, i);
134+
}
135+
EXPECT_EQ(up_to, rbtree._allocator.allocations);
136+
for (int i = 0; i < up_to; i++) {
137+
rbtree.remove(i);
138+
}
139+
EXPECT_EQ(0, rbtree._allocator.allocations);
140+
EXPECT_EQ(nullptr, rbtree._root);
141+
}
142+
143+
{
144+
RBTree<int, int, Cmp, LeakCheckedAllocator> rbtree;
145+
for (int i = 0; i < up_to; i++) {
146+
rbtree.upsert(i, i);
147+
}
148+
rbtree.remove_all();
149+
EXPECT_EQ(0, rbtree._allocator.allocations);
150+
EXPECT_EQ(nullptr, rbtree._root);
151+
}
152+
}
153+
154+
void test_find() {
155+
struct Empty {};
156+
RBTreeCHeap<float, Empty, FCmp, mtOther> rbtree;
157+
using Node = RBTreeCHeap<float, Empty, FCmp, mtOther>::RBNode;
158+
159+
Node* n = nullptr;
160+
auto test = [&](float f) {
161+
EXPECT_EQ(nullptr, rbtree.find(f));
162+
rbtree.upsert(f, Empty{});
163+
const Node* n = rbtree.find_node(f);
164+
EXPECT_NE(nullptr, n);
165+
EXPECT_EQ(f, n->key());
166+
};
167+
168+
test(1.0f);
169+
test(5.0f);
170+
test(0.0f);
171+
}
172+
173+
void test_visitors() {
174+
{ // Tests with 'default' ordering (ascending)
175+
RBTreeInt rbtree;
176+
using Node = RBTreeInt::RBNode;
177+
178+
rbtree.visit_range_in_order(0, 100, [&](Node* x) {
179+
EXPECT_TRUE(false) << "Empty rbtree has no nodes to visit";
180+
});
181+
182+
// Single-element set
183+
rbtree.upsert(1, 0);
184+
int count = 0;
185+
rbtree.visit_range_in_order(0, 100, [&](Node* x) {
186+
count++;
187+
});
188+
EXPECT_EQ(1, count);
189+
190+
count = 0;
191+
rbtree.visit_in_order([&](Node* x) {
192+
count++;
193+
});
194+
EXPECT_EQ(1, count);
195+
196+
// Add an element outside of the range that should not be visited on the right side and
197+
// one on the left side.
198+
rbtree.upsert(101, 0);
199+
rbtree.upsert(-1, 0);
200+
count = 0;
201+
rbtree.visit_range_in_order(0, 100, [&](Node* x) {
202+
count++;
203+
});
204+
EXPECT_EQ(1, count);
205+
206+
count = 0;
207+
rbtree.visit_in_order([&](Node* x) {
208+
count++;
209+
});
210+
EXPECT_EQ(3, count);
211+
212+
// Visiting empty range [0, 0) == {}
213+
rbtree.upsert(0, 0); // This node should not be visited.
214+
rbtree.visit_range_in_order(0, 0, [&](Node* x) {
215+
EXPECT_TRUE(false) << "Empty visiting range should not visit any node";
216+
});
217+
218+
rbtree.remove_all();
219+
for (int i = 0; i < 11; i++) {
220+
rbtree.upsert(i, 0);
221+
}
222+
223+
ResourceMark rm;
224+
GrowableArray<int> seen;
225+
rbtree.visit_range_in_order(0, 10, [&](Node* x) {
226+
seen.push(x->key());
227+
});
228+
EXPECT_EQ(10, seen.length());
229+
for (int i = 0; i < 10; i++) {
230+
EXPECT_EQ(i, seen.at(i));
231+
}
232+
233+
seen.clear();
234+
rbtree.visit_in_order([&](Node* x) {
235+
seen.push(x->key());
236+
});
237+
EXPECT_EQ(11, seen.length());
238+
for (int i = 0; i < 10; i++) {
239+
EXPECT_EQ(i, seen.at(i));
240+
}
241+
242+
seen.clear();
243+
rbtree.visit_range_in_order(10, 12, [&](Node* x) {
244+
seen.push(x->key());
245+
});
246+
EXPECT_EQ(1, seen.length());
247+
EXPECT_EQ(10, seen.at(0));
248+
}
249+
{ // Test with descending ordering
250+
RBTreeCHeap<int, int, CmpInverse, mtOther> rbtree;
251+
using Node = RBTreeCHeap<int, int, CmpInverse, mtOther>::RBNode;
252+
253+
for (int i = 0; i < 10; i++) {
254+
rbtree.upsert(i, 0);
255+
}
256+
ResourceMark rm;
257+
GrowableArray<int> seen;
258+
rbtree.visit_range_in_order(9, -1, [&](Node* x) {
259+
seen.push(x->key());
260+
});
261+
EXPECT_EQ(10, seen.length());
262+
for (int i = 0; i < 10; i++) {
263+
EXPECT_EQ(10-i-1, seen.at(i));
264+
}
265+
seen.clear();
266+
267+
rbtree.visit_in_order([&](Node* x) {
268+
seen.push(x->key());
269+
});
270+
EXPECT_EQ(10, seen.length());
271+
for (int i = 0; i < 10; i++) {
272+
EXPECT_EQ(10 - i - 1, seen.at(i));
273+
}
274+
}
275+
}
276+
277+
void test_closest_leq() {
278+
using Node = RBTreeInt::RBNode;
279+
{
280+
RBTreeInt rbtree;
281+
Node* n = rbtree.closest_leq(0);
282+
EXPECT_EQ(nullptr, n);
283+
284+
rbtree.upsert(0, 0);
285+
n = rbtree.closest_leq(0);
286+
EXPECT_EQ(0, n->key());
287+
288+
rbtree.upsert(-1, -1);
289+
n = rbtree.closest_leq(0);
290+
EXPECT_EQ(0, n->key());
291+
292+
rbtree.upsert(6, 0);
293+
n = rbtree.closest_leq(6);
294+
EXPECT_EQ(6, n->key());
295+
296+
n = rbtree.closest_leq(-2);
297+
EXPECT_EQ(nullptr, n);
298+
}
299+
}
300+
301+
void test_node_prev() {
302+
RBTreeInt _tree;
303+
using Node = RBTreeInt::RBNode;
304+
constexpr int num_nodes = 100;
305+
306+
for (int i = num_nodes; i > 0; i--) {
307+
_tree.upsert(i, i);
308+
}
309+
310+
Node* node = _tree.find_node(num_nodes);
311+
int count = num_nodes;
312+
while (node != nullptr) {
313+
EXPECT_EQ(count, node->val());
314+
node = node->prev();
315+
count--;
316+
}
317+
318+
EXPECT_EQ(count, 0);
319+
}
320+
321+
void test_node_next() {
322+
RBTreeInt _tree;
323+
using Node = RBTreeInt::RBNode;
324+
constexpr int num_nodes = 100;
325+
326+
for (int i = 0; i < num_nodes; i++) {
327+
_tree.upsert(i, i);
328+
}
329+
330+
Node* node = _tree.find_node(0);
331+
int count = 0;
332+
while (node != nullptr) {
333+
EXPECT_EQ(count, node->val());
334+
node = node->next();
335+
count++;
336+
}
337+
338+
EXPECT_EQ(count, num_nodes);
339+
}
340+
341+
void test_stable_nodes() {
342+
using Node = RBTreeInt::RBNode;
343+
RBTreeInt rbtree;
344+
ResourceMark rm;
345+
GrowableArray<Node*> a(10000);
346+
for (int i = 0; i < 10000; i++) {
347+
rbtree.upsert(i, i);
348+
a.push(rbtree.find_node(i));
349+
}
350+
351+
for (int i = 0; i < 2000; i++) {
352+
int r = os::random() % 10000;
353+
Node* to_delete = rbtree.find_node(r);
354+
if (to_delete != nullptr && to_delete->_left != nullptr &&
355+
to_delete->_right != nullptr) {
356+
rbtree.remove(to_delete);
357+
}
358+
}
359+
360+
// After deleting, nodes should have been moved around but kept their values
361+
for (int i = 0; i < 10000; i++) {
362+
const Node* n = rbtree.find_node(i);
363+
if (n != nullptr) {
364+
EXPECT_EQ(a.at(i), n);
365+
}
366+
}
367+
}
368+
369+
void test_stable_nodes_addresses() {
370+
using Tree = RBTreeCHeap<int, void*, Cmp, mtOther>;
371+
using Node = Tree::RBNode;
372+
Tree rbtree;
373+
for (int i = 0; i < 10000; i++) {
374+
rbtree.upsert(i, nullptr);
375+
Node* inserted_node = rbtree.find_node(i);
376+
inserted_node->val() = inserted_node;
377+
}
378+
379+
for (int i = 0; i < 2000; i++) {
380+
int r = os::random() % 10000;
381+
Node* to_delete = rbtree.find_node(r);
382+
if (to_delete != nullptr && to_delete->_left != nullptr &&
383+
to_delete->_right != nullptr) {
384+
rbtree.remove(to_delete);
385+
}
386+
}
387+
388+
// After deleting, values should have remained consistant
389+
rbtree.visit_in_order([&](Node* node) {
390+
EXPECT_EQ(node, node->val());
391+
});
392+
}
393+
394+
#ifdef ASSERT
395+
void test_fill_verify() {
396+
RBTreeInt rbtree;
397+
398+
ResourceMark rm;
399+
GrowableArray<int> allocations;
400+
401+
int size = 10000;
402+
// Create random values
403+
for (int i = 0; i < size; i++) {
404+
int r = os::random() % size;
405+
allocations.append(r);
406+
}
407+
408+
// Insert ~half of the values
409+
for (int i = 0; i < size; i++) {
410+
int r = os::random();
411+
if (r % 2 == 0) {
412+
rbtree.upsert(allocations.at(i), allocations.at(i));
413+
}
414+
if (i % 100 == 0) {
415+
verify_it(rbtree);
416+
}
417+
}
418+
419+
// Insert and remove randomly
420+
for (int i = 0; i < size; i++) {
421+
int r = os::random();
422+
if (r % 2 == 0) {
423+
rbtree.upsert(allocations.at(i), allocations.at(i));
424+
} else {
425+
rbtree.remove(allocations.at(i));
426+
}
427+
if (i % 100 == 0) {
428+
verify_it(rbtree);
429+
}
430+
}
431+
432+
// Remove all elements
433+
for (int i = 0; i < size; i++) {
434+
rbtree.remove(allocations.at(i));
435+
}
436+
437+
verify_it(rbtree);
438+
EXPECT_EQ(rbtree.size(), 0UL);
439+
}
440+
441+
void test_nodes_visited_once() {
442+
constexpr size_t memory_size = 65536;
443+
using Tree = RBTree<int, int, Cmp, ArrayAllocator<memory_size>>;
444+
using Node = Tree::RBNode;
445+
446+
Tree tree;
447+
448+
int num_nodes = memory_size / sizeof(Node);
449+
for (int i = 0; i < num_nodes; i++) {
450+
tree.upsert(i, i);
451+
}
452+
453+
Node* start = tree.find_node(0);
454+
455+
Node* node = start;
456+
for (int i = 0; i < num_nodes; i++) {
457+
EXPECT_EQ(tree._expected_visited, node->_visited);
458+
node += 1;
459+
}
460+
461+
verify_it(tree);
462+
463+
node = start;
464+
for (int i = 0; i < num_nodes; i++) {
465+
EXPECT_EQ(tree._expected_visited, node->_visited);
466+
node += 1;
467+
}
468+
469+
}
470+
#endif // ASSERT
471+
472+
};
473+
474+
TEST_VM_F(RBTreeTest, InsertingDuplicatesResultsInOneValue) {
475+
this->inserting_duplicates_results_in_one_value();
476+
}
477+
478+
TEST_VM_F(RBTreeTest, RBTreeOughtNotLeak) {
479+
this->rbtree_ought_not_leak();
480+
}
481+
482+
TEST_VM_F(RBTreeTest, TestFind) {
483+
this->test_find();
484+
}
485+
486+
TEST_VM_F(RBTreeTest, TestVisitors) {
487+
this->test_visitors();
488+
}
489+
490+
TEST_VM_F(RBTreeTest, TestClosestLeq) {
491+
this->test_closest_leq();
492+
}
493+
494+
TEST_VM_F(RBTreeTest, NodePrev) {
495+
this->test_node_prev();
496+
}
497+
498+
TEST_VM_F(RBTreeTest, NodeNext) {
499+
this->test_node_next();
500+
}
501+
502+
TEST_VM_F(RBTreeTest, NodeStableTest) {
503+
this->test_stable_nodes();
504+
}
505+
506+
TEST_VM_F(RBTreeTest, NodeStableAddressTest) {
507+
this->test_stable_nodes_addresses();
508+
}
509+
510+
#ifdef ASSERT
511+
TEST_VM_F(RBTreeTest, FillAndVerify) {
512+
this->test_fill_verify();
513+
}
514+
515+
TEST_VM_F(RBTreeTest, NodesVisitedOnce) {
516+
this->test_nodes_visited_once();
517+
}
518+
519+
TEST_VM_F(RBTreeTest, InsertRemoveVerify) {
520+
constexpr int num_nodes = 100;
521+
for (int n_t1 = 0; n_t1 < num_nodes; n_t1++) {
522+
for (int n_t2 = 0; n_t2 < n_t1; n_t2++) {
523+
RBTreeInt tree;
524+
for (int i = 0; i < n_t1; i++) {
525+
tree.upsert(i, i);
526+
}
527+
for (int i = 0; i < n_t2; i++) {
528+
tree.remove(i);
529+
}
530+
verify_it(tree);
531+
}
532+
}
533+
}
534+
535+
TEST_VM_F(RBTreeTest, VerifyItThroughStressTest) {
536+
{ // Repeatedly verify a tree of moderate size
537+
RBTreeInt rbtree;
538+
constexpr int ten_thousand = 10000;
539+
for (int i = 0; i < ten_thousand; i++) {
540+
int r = os::random();
541+
if (r % 2 == 0) {
542+
rbtree.upsert(i, i);
543+
} else {
544+
rbtree.remove(i);
545+
}
546+
if (i % 100 == 0) {
547+
verify_it(rbtree);
548+
}
549+
}
550+
for (int i = 0; i < ten_thousand; i++) {
551+
int r = os::random();
552+
if (r % 2 == 0) {
553+
rbtree.upsert(i, i);
554+
} else {
555+
rbtree.remove(i);
556+
}
557+
if (i % 100 == 0) {
558+
verify_it(rbtree);
559+
}
560+
}
561+
}
562+
{ // Make a very large tree and verify at the end
563+
struct Nothing {};
564+
RBTreeCHeap<int, Nothing, Cmp, mtOther> rbtree;
565+
constexpr int one_hundred_thousand = 100000;
566+
for (int i = 0; i < one_hundred_thousand; i++) {
567+
rbtree.upsert(i, Nothing());
568+
}
569+
verify_it(rbtree);
570+
}
571+
}
572+
573+
#endif // ASSERT

0 commit comments

Comments
 (0)
Please sign in to comment.