/** * Rotate the nodes in an AVL tree * Direction specifies the direction to rotate (ie: the direction away from the heavy node) * * @param[in] tree The tree * @param[in] walk The root of the tree to search * @param[in] direction The direction to rotate * @param[in|out] heightChange The height change * * @return The heavy node */ static J9AVLTreeNode * rotate(J9AVLTree *tree, J9AVLTreeNode *walk, intptr_t direction, intptr_t *heightChange) { J9WSRP *heavyNodePtr; J9WSRP *graftNodePtr; J9AVLTreeNode *heavyNode; Trc_AVL_rotate_Entry(tree, walk, direction, heightChange); if (tree->genericActionHook) { tree->genericActionHook(tree, (J9AVLTreeNode *)walk, J9AVLTREE_ACTION_SINGLE_ROTATE); } if (direction < 0) { heavyNodePtr = &walk->rightChild; heavyNode = AVL_NNSRP_GETNODE(*heavyNodePtr); graftNodePtr = &heavyNode->leftChild; } else { heavyNodePtr = &walk->leftChild; heavyNode = AVL_NNSRP_GETNODE(*heavyNodePtr); graftNodePtr = &heavyNode->rightChild; } AVL_SRP_PTR_SETNODE(heavyNodePtr, AVL_SRP_GETNODE(*graftNodePtr)); AVL_NNSRP_PTR_SETNODE(graftNodePtr, walk); if (AVL_GETBALANCE(heavyNode) != AVL_BALANCED) { /* this rotation reduces the amount the tree is growing by one */ /* if the height was growing, it is no longer growing */ if (*heightChange > 0) { *heightChange = 0; } AVL_SETBALANCE(heavyNode, AVL_BALANCED); AVL_SETBALANCE(walk, AVL_BALANCED); } else { /* the only way that the heavy node can be balanced is via deletion * otherwise a single or double rotation will have already occured */ *heightChange = 0; if (direction < 0) { AVL_SETBALANCE(heavyNode, AVL_LEFTHEAVY); AVL_SETBALANCE(walk, AVL_RIGHTHEAVY); } else { AVL_SETBALANCE(heavyNode, AVL_RIGHTHEAVY); AVL_SETBALANCE(walk, AVL_LEFTHEAVY); } } Trc_AVL_rotate_Exit(heavyNode); return heavyNode; }
/* * Insert a new node into an AVL tree at the specified (from avl_find()) place. * * Newly inserted nodes are always leaf nodes in the tree, since avl_find() * searches out to the leaf positions. The avl_index_t indicates the node * which will be the parent of the new node. * * After the node is inserted, a single rotation further up the tree may * be necessary to maintain an acceptable AVL balance. */ void avl_insert(avl_tree_t *tree, void *new_data, avl_index_t where) { avl_node_t *node; avl_node_t *parent = AVL_INDEX2NODE(where); int old_balance; int new_balance; int which_child = AVL_INDEX2CHILD(where); size_t off = tree->avl_offset; if (tree == NULL) { filebench_log(LOG_ERROR, "No Tree Supplied"); return; } #if defined(_LP64) || (__WORDSIZE == 64) if (((uintptr_t)new_data & 0x7) != 0) { filebench_log(LOG_ERROR, "Missaligned pointer to new data"); return; } #endif node = AVL_DATA2NODE(new_data, off); /* * First, add the node to the tree at the indicated position. */ ++tree->avl_numnodes; node->avl_child[0] = NULL; node->avl_child[1] = NULL; AVL_SETCHILD(node, which_child); AVL_SETBALANCE(node, 0); AVL_SETPARENT(node, parent); if (parent != NULL) { if (parent->avl_child[which_child] != NULL) filebench_log(LOG_DEBUG_IMPL, "Overwriting existing pointer"); parent->avl_child[which_child] = node; } else { if (tree->avl_root != NULL) filebench_log(LOG_DEBUG_IMPL, "Overwriting existing pointer"); tree->avl_root = node; } /* * Now, back up the tree modifying the balance of all nodes above the * insertion point. If we get to a highly unbalanced ancestor, we * need to do a rotation. If we back out of the tree we are done. * If we brought any subtree into perfect balance (0), we are also done. */ for (;;) { node = parent; if (node == NULL) return; /* * Compute the new balance */ old_balance = AVL_XBALANCE(node); new_balance = old_balance + avl_child2balance[which_child]; /* * If we introduced equal balance, then we are done immediately */ if (new_balance == 0) { AVL_SETBALANCE(node, 0); return; } /* * If both old and new are not zero we went * from -1 to -2 balance, do a rotation. */ if (old_balance != 0) break; AVL_SETBALANCE(node, new_balance); parent = AVL_XPARENT(node); which_child = AVL_XCHILD(node); } /* * perform a rotation to fix the tree and return */ (void) avl_rotation(tree, node, new_balance); }
/* * Perform a rotation to restore balance at the subtree given by depth. * * This routine is used by both insertion and deletion. The return value * indicates: * 0 : subtree did not change height * !0 : subtree was reduced in height * * The code is written as if handling left rotations, right rotations are * symmetric and handled by swapping values of variables right/left[_heavy] * * On input balance is the "new" balance at "node". This value is either * -2 or +2. */ static int avl_rotation(avl_tree_t *tree, avl_node_t *node, int balance) { int left = !(balance < 0); /* when balance = -2, left will be 0 */ int right = 1 - left; int left_heavy = balance >> 1; int right_heavy = -left_heavy; avl_node_t *parent = AVL_XPARENT(node); avl_node_t *child = node->avl_child[left]; avl_node_t *cright; avl_node_t *gchild; avl_node_t *gright; avl_node_t *gleft; int which_child = AVL_XCHILD(node); int child_bal = AVL_XBALANCE(child); /* BEGIN CSTYLED */ /* * case 1 : node is overly left heavy, the left child is balanced or * also left heavy. This requires the following rotation. * * (node bal:-2) * / \ * / \ * (child bal:0 or -1) * / \ * / \ * cright * * becomes: * * (child bal:1 or 0) * / \ * / \ * (node bal:-1 or 0) * / \ * / \ * cright * * we detect this situation by noting that child's balance is not * right_heavy. */ /* END CSTYLED */ if (child_bal != right_heavy) { /* * compute new balance of nodes * * If child used to be left heavy (now balanced) we reduced * the height of this sub-tree -- used in "return...;" below */ child_bal += right_heavy; /* adjust towards right */ /* * move "cright" to be node's left child */ cright = child->avl_child[right]; node->avl_child[left] = cright; if (cright != NULL) { AVL_SETPARENT(cright, node); AVL_SETCHILD(cright, left); } /* * move node to be child's right child */ child->avl_child[right] = node; AVL_SETBALANCE(node, -child_bal); AVL_SETCHILD(node, right); AVL_SETPARENT(node, child); /* * update the pointer into this subtree */ AVL_SETBALANCE(child, child_bal); AVL_SETCHILD(child, which_child); AVL_SETPARENT(child, parent); if (parent != NULL) parent->avl_child[which_child] = child; else tree->avl_root = child; return (child_bal == 0); } /* BEGIN CSTYLED */ /* * case 2 : When node is left heavy, but child is right heavy we use * a different rotation. * * (node b:-2) * / \ * / \ * / \ * (child b:+1) * / \ * / \ * (gchild b: != 0) * / \ * / \ * gleft gright * * becomes: * * (gchild b:0) * / \ * / \ * / \ * (child b:?) (node b:?) * / \ / \ * / \ / \ * gleft gright * * computing the new balances is more complicated. As an example: * if gchild was right_heavy, then child is now left heavy * else it is balanced */ /* END CSTYLED */ gchild = child->avl_child[right]; gleft = gchild->avl_child[left]; gright = gchild->avl_child[right]; /* * move gright to left child of node and * * move gleft to right child of node */ node->avl_child[left] = gright; if (gright != NULL) { AVL_SETPARENT(gright, node); AVL_SETCHILD(gright, left); } child->avl_child[right] = gleft; if (gleft != NULL) { AVL_SETPARENT(gleft, child); AVL_SETCHILD(gleft, right); } /* * move child to left child of gchild and * * move node to right child of gchild and * * fixup parent of all this to point to gchild */ balance = AVL_XBALANCE(gchild); gchild->avl_child[left] = child; AVL_SETBALANCE(child, (balance == right_heavy ? left_heavy : 0)); AVL_SETPARENT(child, gchild); AVL_SETCHILD(child, left); gchild->avl_child[right] = node; AVL_SETBALANCE(node, (balance == left_heavy ? right_heavy : 0)); AVL_SETPARENT(node, gchild); AVL_SETCHILD(node, right); AVL_SETBALANCE(gchild, 0); AVL_SETPARENT(gchild, parent); AVL_SETCHILD(gchild, which_child); if (parent != NULL) parent->avl_child[which_child] = gchild; else tree->avl_root = gchild; return (1); /* the new tree is always shorter */ }
/* * Insert a new node into an AVL tree at the specified (from avl_find()) place. * * Newly inserted nodes are always leaf nodes in the tree, since avl_find() * searches out to the leaf positions. The avl_index_t indicates the node * which will be the parent of the new node. * * After the node is inserted, a single rotation further up the tree may * be necessary to maintain an acceptable AVL balance. */ void avl_insert(avl_tree_t *tree, void *new_data, avl_index_t where) { avl_node_t *node; avl_node_t *parent = AVL_INDEX2NODE(where); int old_balance; int new_balance; int which_child = AVL_INDEX2CHILD(where); size_t off = tree->avl_offset; node = AVL_DATA2NODE(new_data, off); /* * First, add the node to the tree at the indicated position. */ ++tree->avl_numnodes; node->avl_child[0] = NULL; node->avl_child[1] = NULL; AVL_SETCHILD(node, which_child); AVL_SETBALANCE(node, 0); AVL_SETPARENT(node, parent); if (parent != NULL) { parent->avl_child[which_child] = node; } else { tree->avl_root = node; } /* * Now, back up the tree modifying the balance of all nodes above the * insertion point. If we get to a highly unbalanced ancestor, we * need to do a rotation. If we back out of the tree we are done. * If we brought any subtree into perfect balance (0), we are also done. */ for (;;) { node = parent; if (node == NULL) return; /* * Compute the new balance */ old_balance = AVL_XBALANCE(node); new_balance = old_balance + avl_child2balance[which_child]; /* * If we introduced equal balance, then we are done immediately */ if (new_balance == 0) { AVL_SETBALANCE(node, 0); return; } /* * If both old and new are not zero we went * from -1 to -2 balance, do a rotation. */ if (old_balance != 0) break; AVL_SETBALANCE(node, new_balance); parent = AVL_XPARENT(node); which_child = AVL_XCHILD(node); } /* * perform a rotation to fix the tree and return */ (void) avl_rotation(tree, node, new_balance); }
/** * Delete a node from an AVL tree. * This function should always be called externally with walkSRPPtr being NULL. * All recursive calls will have walkPtr==NULL and a value for walkSRPPtr. * * @param[in] tree The tree * @param[in] walkPtr A pointer to the root of the tree * @param[in] walkSRPPtr Should always be NULL (only used when the function calls itself) * @param[in] node The node to delete (can be a node or an SRP node) * @param[in|out] heightChange The height change * * @return The node deleted or NULL */ static J9AVLTreeNode * deleteNode(J9AVLTree *tree, J9AVLTreeNode **walkPtr, J9WSRP *walkSRPPtr, J9AVLTreeNode *node, intptr_t *heightChange) { J9AVLTreeNode *find; J9AVLTreeNode *walk; intptr_t dir; Trc_AVL_deleteNode_Entry(tree, walkPtr, walkSRPPtr, node, heightChange); /* Assumption is that if walkSRPPtr is NULL, walkPtr is not */ if (!walkSRPPtr) { walk = AVL_GETNODE(*walkPtr); } else { walk = AVL_SRP_GETNODE(*walkSRPPtr); } if (!walk) { if (tree->genericActionHook) { tree->genericActionHook(tree, walk, J9AVLTREE_ACTION_REMOVE_NOT_IN_TREE); } Trc_AVL_deleteNode_NotInTree(); return NULL; } dir = tree->insertionComparator(tree, node, walk); if (!dir) { J9AVLTreeNode *walkLeftChild, *walkRightChild; walkLeftChild = AVL_SRP_GETNODE(walk->leftChild); walkRightChild = AVL_SRP_GETNODE(walk->rightChild); if (!walkLeftChild) { if (!walkSRPPtr) { AVL_SETNODE(*walkPtr, walkRightChild); } else { AVL_SRP_PTR_SETNODE(walkSRPPtr, walkRightChild); } AVL_SRP_SET_TO_NULL(walk->rightChild); *heightChange = -1; } else if (!walkRightChild) { if (!walkSRPPtr) { AVL_SETNODE(*walkPtr, walkLeftChild); } else { AVL_SRP_PTR_SETNODE(walkSRPPtr, walkLeftChild); } AVL_SRP_SET_TO_NULL(walk->leftChild); *heightChange = -1; } else { J9AVLTreeNode *leaf; leaf = findRightMostLeaf(tree, &(walk->leftChild), heightChange); AVL_SRP_SETNODE(leaf->leftChild, AVL_SRP_GETNODE(walk->leftChild)); AVL_SRP_SETNODE(leaf->rightChild, AVL_SRP_GETNODE(walk->rightChild)); AVL_SETBALANCE(leaf, AVL_GETBALANCE(walk)); AVL_SRP_SET_TO_NULL(walk->leftChild); AVL_SRP_SET_TO_NULL(walk->rightChild); if (!walkSRPPtr) { AVL_SETNODE(*walkPtr, leaf); } else { AVL_NNSRP_PTR_SETNODE(walkSRPPtr, leaf); } rebalance(tree, walkPtr, walkSRPPtr, -1, heightChange); } AVL_SETBALANCE(walk, AVL_BALANCED); if (tree->genericActionHook) { tree->genericActionHook(tree, walk, J9AVLTREE_ACTION_REMOVE); } Trc_AVL_deleteNode_Removed(walk); return walk; } if (dir < 0) { find = deleteNode(tree, NULL, &(walk->leftChild), node, heightChange); } else { find = deleteNode(tree, NULL, &(walk->rightChild), node, heightChange); } /* if we deleted a node */ if (find) { rebalance(tree, walkPtr, walkSRPPtr, dir, heightChange); } Trc_AVL_deleteNode_Recursive(find); return find; }
/** * Rebalances an AVL tree. * This function should be called with either walkPtr OR walkSRPPtr set. * Use walkPtr if you have a direct pointer to the node or walkSRPPtr if you have a pointer to a WSRP * * @param[in] tree The tree * @param[in] walkPtr A pointer to the root of the tree to balance (walkSRPPtr is NULL) * @param[in] walkSRPPtr A pointer to an SRP of the root of the tree to balance (walkPtr is NULL) * @param[in|out] heightChange The height change */ static void rebalance(J9AVLTree *tree, J9AVLTreeNode **walkPtr, J9WSRP *walkSRPPtr, intptr_t direction, intptr_t *heightChange) { J9AVLTreeNode *walk = NULL; uintptr_t walkBalance; if (!(*heightChange)) { return; } Trc_AVL_rebalance_Entry(tree, walkPtr, walkSRPPtr, direction, heightChange); /* if we shrunk nodes, flip the direction, thus direction points towards heavy direction */ if (*heightChange < 0) { direction = -direction; } /* Assumption is that if walkSRPPtr is NULL, walkPtr is not */ if (!walkSRPPtr) { walk = AVL_GETNODE(*walkPtr); } else { walk = AVL_NNSRP_GETNODE(*walkSRPPtr); } walkBalance = AVL_GETBALANCE(walk); if (walkBalance == AVL_BALANCED) { AVL_SETBALANCE(walk, (direction < 0) ? AVL_LEFTHEAVY : AVL_RIGHTHEAVY); /* if the tree was shrinking, we've just unbalanced a node .. no longer shrinking */ if (*heightChange < 0) { *heightChange = 0; } } else if ((walkBalance == AVL_LEFTHEAVY) == (direction < 0)) { J9AVLTreeNode *rotateResult = NULL; /* left heavy adding to the left or right heavy adding to the right */ if ((direction < 0) && (AVL_GETBALANCE(AVL_NNSRP_GETNODE(walk->leftChild)) == AVL_RIGHTHEAVY)) { /* doubleRotate */ rotateResult = (J9AVLTreeNode *)doubleRotate(tree, walk, -direction, heightChange); } else if ((direction > 0) && (AVL_GETBALANCE(AVL_NNSRP_GETNODE(walk->rightChild)) == AVL_LEFTHEAVY)) { /* doubleRotate */ rotateResult = (J9AVLTreeNode *)doubleRotate(tree, walk, -direction, heightChange); } else { /* single rotate */ rotateResult = (J9AVLTreeNode *)rotate(tree, walk, -direction, heightChange); } /* Assumption is that if walkSRPPtr is NULL, walkPtr is not */ if (!walkSRPPtr) { AVL_SETNODE(*walkPtr, rotateResult); } else { AVL_NNSRP_PTR_SETNODE(walkSRPPtr, rotateResult); } } else { AVL_SETBALANCE(walk, AVL_BALANCED); /* added height making node become equal -- no more growing */ if (*heightChange > 0) { *heightChange = 0; } } Trc_AVL_rebalance_Exit(*heightChange); }
/** * Double-rotate the nodes in an AVL tree * Direction specifies the direction to rotate (ie: the direction away from the heavy node) * * @param[in] tree The tree * @param[in] walk The root of the tree to search * @param[in] direction The direction to rotate * @param[in|out] heightChange The height change * * @return The new root node */ static J9AVLTreeNode * doubleRotate(J9AVLTree *tree, J9AVLTreeNode *walk, intptr_t direction, intptr_t *heightChange) { J9WSRP *heavyNodePtr; J9WSRP *graft1NodePtr; J9WSRP *graft2NodePtr; J9WSRP *newrootNodePtr; J9AVLTreeNode *heavyNode; J9AVLTreeNode *newrootNode; Trc_AVL_doubleRotate_Entry(tree, walk, direction, heightChange); if (tree->genericActionHook) { tree->genericActionHook(tree, (J9AVLTreeNode *)walk, J9AVLTREE_ACTION_DOUBLE_ROTATE); } if (direction < 0) { heavyNodePtr = &walk->rightChild; heavyNode = AVL_NNSRP_GETNODE(*heavyNodePtr); newrootNodePtr = &heavyNode->leftChild; newrootNode = AVL_NNSRP_GETNODE(*newrootNodePtr); graft1NodePtr = &newrootNode->rightChild; graft2NodePtr = &newrootNode->leftChild; } else { heavyNodePtr = &walk->leftChild; heavyNode = AVL_NNSRP_GETNODE(*heavyNodePtr); newrootNodePtr = &heavyNode->rightChild; newrootNode = AVL_NNSRP_GETNODE(*newrootNodePtr); graft1NodePtr = &newrootNode->leftChild; graft2NodePtr = &newrootNode->rightChild; } AVL_SRP_PTR_SETNODE(newrootNodePtr, AVL_SRP_GETNODE(*graft1NodePtr)); AVL_NNSRP_PTR_SETNODE(graft1NodePtr, heavyNode); AVL_SRP_PTR_SETNODE(heavyNodePtr, AVL_SRP_GETNODE(*graft2NodePtr)); AVL_NNSRP_PTR_SETNODE(graft2NodePtr, walk); if (AVL_GETBALANCE(newrootNode) == AVL_BALANCED) { AVL_SETBALANCE(heavyNode, AVL_BALANCED); AVL_SETBALANCE(walk, AVL_BALANCED); } else if (AVL_GETBALANCE(newrootNode) == AVL_LEFTHEAVY) { if (direction < 0) { AVL_SETBALANCE(heavyNode, AVL_RIGHTHEAVY); AVL_SETBALANCE(walk, AVL_BALANCED); } else { AVL_SETBALANCE(heavyNode, AVL_BALANCED); AVL_SETBALANCE(walk, AVL_RIGHTHEAVY); } } else if (direction < 0) { AVL_SETBALANCE(heavyNode, AVL_BALANCED); AVL_SETBALANCE(walk, AVL_LEFTHEAVY); } else { AVL_SETBALANCE(heavyNode, AVL_LEFTHEAVY); AVL_SETBALANCE(walk, AVL_BALANCED); } AVL_SETBALANCE(newrootNode, AVL_BALANCED); /* if the height was growing, it is no longer growing */ if (*heightChange > 0) { *heightChange = 0; } Trc_AVL_doubleRotate_Exit(newrootNode); return newrootNode; }