/* * 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; 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); }
/* * 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); }