// Copyright (c) Meta Platforms, Inc. and affiliates. // SPDX-License-Identifier: LGPL-2.1-or-later #include "binary_search_tree.h" // IWYU pragma: associated /* * Binary search tree splay operation based on the original paper [1]. Rotations * are inlined to avoid redundant pointer assignments. We still do redundant * assignments to great_grandparent->{left,right} and node->{left,right}; this * was faster in microbenchmarks than doing the extra tracking to avoid the * reassignments. * * This assumes that the node is not already the root. * * 1: "Self-Adjusting Binary Search Trees" (Sleator & Tarjan, 1985): * http://www.cs.cmu.edu/~sleator/papers/self-adjusting.pdf */ void drgn_splay_tree_splay(struct binary_tree_node **root, struct binary_tree_node *node, struct binary_tree_node *parent) { for (;;) { struct binary_tree_node *grandparent, *great_grandparent; grandparent = parent->parent; if (node == parent->left) { if (!grandparent) { /* * Zig step: rotate_right(parent). The node * takes the place of its parent, which is the * root. */ parent->left = node->right; node->right = parent; if (parent->left) parent->left->parent = parent; parent->parent = node; break; } else { great_grandparent = grandparent->parent; if (parent == grandparent->left) { /* * Zig-zig step: * rotate_right(grandparent), * rotate_right(parent). */ grandparent->left = parent->right; parent->left = node->right; parent->right = grandparent; node->right = parent; if (grandparent->left) grandparent->left->parent = grandparent; grandparent->parent = parent; if (parent->left) parent->left->parent = parent; parent->parent = node; } else { /* * Zig-zag step: rotate_right(parent), * rotate_left(grandparent). */ grandparent->right = node->left; parent->left = node->right; node->left = grandparent; node->right = parent; if (grandparent->right) grandparent->right->parent = grandparent; if (parent->left) parent->left->parent = parent; grandparent->parent = node; parent->parent = node; } } } else { if (!grandparent) { /* * Zig step: rotate_left(parent). The node * takes the place of its parent, which is the * root. */ parent->right = node->left; node->left = parent; if (parent->right) parent->right->parent = parent; parent->parent = node; break; } else { great_grandparent = grandparent->parent; if (parent == grandparent->right) { /* * Zig-zig step: * rotate_left(grandparent), * rotate_left(parent). */ grandparent->right = parent->left; parent->right = node->left; parent->left = grandparent; node->left = parent; if (grandparent->right) grandparent->right->parent = grandparent; grandparent->parent = parent; if (parent->right) parent->right->parent = parent; parent->parent = node; } else { /* * Zig-zag step: rotate_left(parent), * rotate_right(grandparent). */ grandparent->left = node->right; parent->right = node->left; node->right = grandparent; node->left = parent; if (grandparent->left) grandparent->left->parent = grandparent; if (parent->right) parent->right->parent = parent; grandparent->parent = node; parent->parent = node; } } } /* * Common code for zig-zig and zig-zag steps, both left and * right. The node took the place of its grandparent, which may * have been the root. We don't need to update the node's parent * pointer because it is always NULL in the end. */ if (!great_grandparent) break; if (grandparent == great_grandparent->left) great_grandparent->left = node; else great_grandparent->right = node; parent = great_grandparent; } /* The node reached the root. */ *root = node; node->parent = NULL; } static inline void drgn_splay_tree_transplant(struct binary_tree_node **root, struct binary_tree_node *old, struct binary_tree_node *new) { if (!old->parent) *root = new; else if (old == old->parent->left) old->parent->left = new; else old->parent->right = new; if (new) new->parent = old->parent; } void drgn_splay_tree_delete(struct binary_tree_node **root, struct binary_tree_node *node) { if (node->left == NULL) { drgn_splay_tree_transplant(root, node, node->right); } else if (node->right == NULL) { drgn_splay_tree_transplant(root, node, node->left); } else { struct binary_tree_node *successor; successor = node->right; if (successor->left) { do { successor = successor->left; } while (successor->left); drgn_splay_tree_transplant(root, successor, successor->right); successor->right = node->right; successor->right->parent = successor; } drgn_splay_tree_transplant(root, node, successor); successor->left = node->left; successor->left->parent = successor; } if (node->parent && node->parent->parent) drgn_splay_tree_splay(root, node->parent, node->parent->parent); }