blob: 04c46c85d2c5de1ce0233610b97ebc523a41feb1 [file]
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_UTILITIES_RBTREE_HPP
#define SHARE_UTILITIES_RBTREE_HPP
#include "metaprogramming/enableIf.hpp"
#include "nmt/memTag.hpp"
#include "runtime/os.hpp"
#include "utilities/globalDefinitions.hpp"
#include <type_traits>
// 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
// 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.
// A red-black tree is constructed with four template parameters:
// 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)`
// used for extra validation can optionally be provided. This should return:
// - true if a < b
// - false otherwise
// ALLOCATOR must check for oom and exit, as RBTree does not handle the allocation failing.
// K needs to be of a type that is trivially destructible.
// The tree will call a value's destructor when its node is removed.
// Nodes are address stable and will not change during its lifetime.
template <typename K, typename NodeType, typename COMPARATOR>
class AbstractRBTree;
class outputStream;
class IntrusiveRBNode {
template <typename K, typename NodeType, typename COMPARATOR>
friend class AbstractRBTree;
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
friend class RBTree;
friend class RBTreeTest;
uintptr_t _parent; // LSB encodes color information. 0 = RED, 1 = BLACK
IntrusiveRBNode* _left;
IntrusiveRBNode* _right;
DEBUG_ONLY(mutable bool _visited);
public:
IntrusiveRBNode() : _parent(0), _left(nullptr), _right(nullptr) DEBUG_ONLY(COMMA _visited(false)) {}
// Gets the previous in-order node in the tree.
// nullptr is returned if there is no previous node.
const IntrusiveRBNode* prev() const;
IntrusiveRBNode* prev();
// Gets the next in-order node in the tree.
// nullptr is returned if there is no next node.
const IntrusiveRBNode* next() const;
IntrusiveRBNode* next();
void print_on(outputStream* st, int depth = 0) const;
private:
bool is_black() const { return (_parent & 0x1) != 0; }
bool is_red() const { return (_parent & 0x1) == 0; }
void set_black() { _parent |= 0x1; }
void set_red() { _parent &= ~0x1; }
IntrusiveRBNode* parent() const { return (IntrusiveRBNode*)(_parent & ~0x1); }
void set_parent(IntrusiveRBNode* new_parent) { _parent = (_parent & 0x1) | (uintptr_t)new_parent; }
bool is_right_child() const {
return parent() != nullptr && parent()->_right == this;
}
bool is_left_child() const {
return parent() != nullptr && parent()->_left == this;
}
void replace_child(IntrusiveRBNode* old_child, IntrusiveRBNode* new_child);
// This node down, right child up
// Returns right child (now parent)
IntrusiveRBNode* rotate_left();
// This node down, left child up
// Returns left child (now parent)
IntrusiveRBNode* rotate_right();
template <typename NodeType, typename NodeVerifier>
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;
};
template <typename K, typename V>
class RBNode : public IntrusiveRBNode {
template <typename K2, typename V2, typename COMPARATOR, typename ALLOCATOR>
friend class RBTree;
friend class RBTreeTest;
private:
K _key;
V _value;
public:
const K& key() const { return _key; }
V& val() { return _value; }
const V& val() const { return _value; }
void set_val(const V& v) { _value = v; }
RBNode() {}
RBNode(const K& key) : IntrusiveRBNode(), _key(key) {}
RBNode(const K& key, const V& val) : IntrusiveRBNode(), _key(key), _value(val) {}
const RBNode<K, V>* prev() const { return (RBNode<K, V>*)IntrusiveRBNode::prev(); }
const RBNode<K, V>* next() const { return (RBNode<K, V>*)IntrusiveRBNode::next(); }
RBNode<K, V>* prev() { return (RBNode<K, V>*)IntrusiveRBNode::prev(); }
RBNode<K, V>* next() { return (RBNode<K, V>*)IntrusiveRBNode::next(); }
void print_on(outputStream* st, int depth = 0) const;
};
template <typename K, typename NodeType, typename COMPARATOR>
class AbstractRBTree {
friend class RBTreeTest;
typedef AbstractRBTree<K, NodeType, COMPARATOR> TreeType;
public:
// Represents the location of a (would be) node in the tree.
// If a cursor is valid (valid() == true) it points somewhere in the tree.
// If the cursor points to an existing node (found() == true), node() can be used to access that node.
// If no node is pointed to, node() returns null, regardless if the cursor is valid or not.
class Cursor {
friend AbstractRBTree<K, NodeType, COMPARATOR>;
NodeType** _insert_location;
NodeType* _parent;
Cursor() : _insert_location(nullptr), _parent(nullptr) {}
Cursor(NodeType** insert_location, NodeType* parent)
: _insert_location(insert_location), _parent(parent) {}
Cursor(NodeType* const* insert_location, NodeType* parent)
: _insert_location((NodeType**)insert_location), _parent(parent) {}
public:
bool valid() const { return _insert_location != nullptr; }
bool found() const { return *_insert_location != nullptr; }
NodeType* node() { return _insert_location == nullptr ? nullptr : *_insert_location; }
NodeType* node() const { return _insert_location == nullptr ? nullptr : *_insert_location; }
};
protected:
size_t _num_nodes;
IntrusiveRBNode* _root;
DEBUG_ONLY(mutable bool _expected_visited);
private:
template <typename CMP, typename RET, typename ARG1, typename ARG2, typename = void>
struct has_cmp_type : std::false_type {};
template <typename CMP, typename RET, typename ARG1, typename ARG2>
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;
template <typename CMP>
static constexpr bool HasNodeComparator = has_cmp_type<CMP, int, K, const NodeType*>::value;
template <typename CMP>
static constexpr bool HasNodeVerifier = has_cmp_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 {
return COMPARATOR::cmp(a, b->key());
}
template <typename CMP = COMPARATOR, ENABLE_IF(HasNodeComparator<CMP>)>
int 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 {
return true;
}
template <typename CMP = COMPARATOR, ENABLE_IF(HasNodeVerifier<CMP>)>
bool cmp(const NodeType* a, const NodeType* b) const {
return COMPARATOR::cmp(a, b);
}
// Cannot assert if no key comparator exist.
template <typename CMP = COMPARATOR, ENABLE_IF(!HasKeyComparator<CMP>)>
void assert_key_leq(K a, K b) const {}
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");
}
// True if node is black (nil nodes count as black)
static inline bool is_black(const IntrusiveRBNode* node) {
return node == nullptr || node->is_black();
}
static inline bool is_red(const IntrusiveRBNode* node) {
return node != nullptr && node->is_red();
}
void fix_insert_violations(IntrusiveRBNode* node);
void remove_black_leaf(IntrusiveRBNode* node);
// 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;
void print_node_on(outputStream* st, int depth, const NodeType* n) const;
public:
NONCOPYABLE(AbstractRBTree);
AbstractRBTree() : _num_nodes(0), _root(nullptr) DEBUG_ONLY(COMMA _expected_visited(false)) {
static_assert(std::is_trivially_destructible<K>::value, "key type must be trivially destructable");
static_assert(HasKeyComparator<COMPARATOR> || HasNodeComparator<COMPARATOR>,
"comparator must be of correct type");
}
size_t size() const { return _num_nodes; }
// Gets the cursor associated with the given node or key.
Cursor cursor(const K& key, const NodeType* hint_node = nullptr);
Cursor cursor(const NodeType* node);
const Cursor cursor(const K& key, const NodeType* hint_node = nullptr) const;
const Cursor cursor(const NodeType* node) const;
// Moves to the next existing node.
// If no next node exist, the cursor becomes invalid.
Cursor next(const Cursor& node_cursor);
const Cursor next(const Cursor& node_cursor) const;
// Moves to the previous existing node.
// If no previous node exist, the cursor becomes invalid.
Cursor prev(const Cursor& node_cursor);
const Cursor prev(const Cursor& node_cursor) const;
// Initializes and inserts a node at the cursor location.
// The cursor must not point to an existing node.
void insert_at_cursor(NodeType* node, const Cursor& node_cursor);
// Removes the node referenced by the cursor
// The cursor must point to a valid existing node
void remove_at_cursor(const Cursor& node_cursor);
// Replace the node referenced by the cursor with a new node.
// The old node is destroyed.
// The user must ensure that no tree properties are broken:
// There must not exist any node with the new key
// For all nodes with key < old_key, must also have key < new_key
// For all nodes with key > old_key, must also have key > new_key
void replace_at_cursor(NodeType* new_node, const Cursor& node_cursor);
// Finds the node associated with the given key.
NodeType* find_node(const K& key, const NodeType* hint_node = nullptr) const {
Cursor node_cursor = cursor(key, hint_node);
return node_cursor.node();
}
NodeType* find_node(const K& key, const NodeType* hint_node = nullptr) {
Cursor node_cursor = cursor(key, hint_node);
return node_cursor.node();
}
// Inserts the given node into the tree.
void insert(const K& key, NodeType* node, const NodeType* hint_node = nullptr) {
Cursor node_cursor = cursor(key, hint_node);
insert_at_cursor(node, node_cursor);
}
void remove(NodeType* node) {
Cursor node_cursor = cursor(node);
remove_at_cursor(node_cursor);
}
// Finds the node with the closest key <= the given key.
// If no node is found, null is returned instead.
NodeType* closest_leq(const K& key) const {
Cursor node_cursor = cursor(key);
return node_cursor.found() ? node_cursor.node() : prev(node_cursor).node();
}
NodeType* closest_leq(const K& key) {
Cursor node_cursor = cursor(key);
return node_cursor.found() ? node_cursor.node() : prev(node_cursor).node();
}
// Finds the node with the closest key > the given key.
// If no node is found, null is returned instead.
NodeType* closest_gt(const K& key) const {
Cursor node_cursor = cursor(key);
return next(node_cursor).node();
}
NodeType* closest_gt(const K& key) {
Cursor node_cursor = cursor(key);
return next(node_cursor).node();
}
// Finds the node with the closest key >= the given key.
// If no node is found, null is returned instead.
NodeType* closest_ge(const K& key) const {
Cursor node_cursor = cursor(key);
return node_cursor.found() ? node_cursor.node() : next(node_cursor).node();
}
NodeType* closest_ge(const K& key) {
Cursor node_cursor = cursor(key);
return node_cursor.found() ? node_cursor.node() : next(node_cursor).node();
}
// Returns leftmost node, nullptr if tree is empty.
// If COMPARATOR::cmp(a, b) behaves canonically (positive value for a > b), this will the smallest key value.
const NodeType* leftmost() const {
IntrusiveRBNode* n = _root, *n2 = nullptr;
while (n != nullptr) {
n2 = n;
n = n->_left;
}
return (NodeType*)n2;
}
// Returns rightmost node, nullptr if tree is empty.
// If COMPARATOR::cmp(a, b) behaves canonically (positive value for a > b), this will the largest key value.
const NodeType* rightmost() const {
IntrusiveRBNode* n = _root, *n2 = nullptr;
while (n != nullptr) {
n2 = n;
n = n->_right;
}
return (NodeType*)n2;
}
NodeType* leftmost() { return const_cast<NodeType*>(static_cast<const TreeType*>(this)->leftmost()); }
NodeType* rightmost() { return const_cast<NodeType*>(static_cast<const TreeType*>(this)->rightmost()); }
struct Range {
NodeType* start;
NodeType* end;
Range(NodeType* start, NodeType* end)
: start(start), end(end) {}
};
// Return the range [start, end)
// where start->key() <= addr < end->key().
// Failure to find the range leads to start and/or end being null.
Range find_enclosing_range(K key) const {
NodeType* start = closest_leq(key);
NodeType* end = closest_gt(key);
return Range(start, end);
}
// Visit all RBNodes in ascending order, calling f on each node.
template <typename F>
void visit_in_order(F f) const;
// Visit all RBNodes in ascending order whose keys are in range [from, to], calling f on each node.
template <typename F>
void visit_range_in_order(const K& from, const K& to, F f) const;
// 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);});
}
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 CMP = COMPARATOR, ENABLE_IF(HasNodeComparator<CMP> && !HasKeyComparator<CMP> && !HasNodeVerifier<CMP>)>
void verify_self() const {
verify_self([](const NodeType*, const NodeType*){ return true;});
}
void print_on(outputStream* st) const;
};
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
class RBTree : public AbstractRBTree<K, RBNode<K, V>, COMPARATOR> {
friend class RBTreeTest;
typedef AbstractRBTree<K, RBNode<K, V>, COMPARATOR> BaseType;
ALLOCATOR _allocator;
public:
RBTree() : BaseType(), _allocator() {}
~RBTree() { remove_all(); }
typedef typename BaseType::Cursor Cursor;
using BaseType::cursor;
using BaseType::insert_at_cursor;
using BaseType::remove_at_cursor;
using BaseType::next;
using BaseType::prev;
void replace_at_cursor(RBNode<K, V>* new_node, const Cursor& node_cursor) {
RBNode<K, V>* old_node = node_cursor.node();
BaseType::replace_at_cursor(new_node, node_cursor);
free_node(old_node);
}
RBNode<K, V>* allocate_node(const K& key, const V& val) {
void* node_place = _allocator.allocate(sizeof(RBNode<K, V>));
assert(node_place != nullptr, "rb-tree allocator must exit on failure");
return new (node_place) RBNode<K, V>(key, val);
}
void free_node(RBNode<K, V>* node) {
node->_value.~V();
_allocator.free(node);
}
// Inserts a node with the given key/value into the tree,
// if the key already exist, the value is updated instead.
void upsert(const K& key, const V& val, const RBNode<K, V>* hint_node = nullptr) {
Cursor node_cursor = cursor(key, hint_node);
RBNode<K, V>* node = node_cursor.node();
if (node != nullptr) {
node->set_val(val);
return;
}
node = allocate_node(key, val);
insert_at_cursor(node, node_cursor);
}
// Finds the value of the node associated with the given key.
V* find(const K& key) {
Cursor node_cursor = cursor(key);
return node_cursor.found() ? &node_cursor.node()->_value : nullptr;
}
V* find(const K& key) const {
const Cursor node_cursor = cursor(key);
return node_cursor.found() ? &node_cursor.node()->_value : nullptr;
}
void remove(RBNode<K, V>* node) {
Cursor node_cursor = cursor(node);
remove_at_cursor(node_cursor);
free_node(node);
}
// Removes the node with the given key from the tree if it exists.
// Returns true if the node was successfully removed, false otherwise.
bool remove(const K& key) {
Cursor node_cursor = cursor(key);
if (!node_cursor.found()) {
return false;
}
RBNode<K, V>* node = node_cursor.node();
remove_at_cursor(node_cursor);
free_node((RBNode<K, V>*)node);
return true;
}
// Removes all existing nodes from the tree.
void remove_all() {
IntrusiveRBNode* to_delete[64];
int stack_idx = 0;
to_delete[stack_idx++] = BaseType::_root;
while (stack_idx > 0) {
IntrusiveRBNode* head = to_delete[--stack_idx];
if (head == nullptr) continue;
to_delete[stack_idx++] = head->_left;
to_delete[stack_idx++] = head->_right;
free_node((RBNode<K, V>*)head);
}
BaseType::_num_nodes = 0;
BaseType::_root = nullptr;
}
};
template <MemTag mem_tag>
class RBTreeCHeapAllocator {
public:
void* allocate(size_t sz) {
void* allocation = os::malloc(sz, mem_tag);
if (allocation == nullptr) {
vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR,
"red-black tree failed allocation");
}
return allocation;
}
void free(void* ptr) { os::free(ptr); }
};
template <typename K, typename V, typename COMPARATOR, MemTag mem_tag>
using RBTreeCHeap = RBTree<K, V, COMPARATOR, RBTreeCHeapAllocator<mem_tag>>;
template <typename K, typename COMPARATOR>
using IntrusiveRBTree = AbstractRBTree<K, IntrusiveRBNode, COMPARATOR>;
#endif // SHARE_UTILITIES_RBTREE_HPP