blob: 9edeac4c373b186960b8ca4ceaae88eea42d62c0 [file] [log] [blame]
/*
* Copyright (c) 2019 LK Trusty Authors. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <lib/binary_search_tree.h>
/**
* bst_node_rank - Internal helper function
* @node: Node to check.
*
* Return: Rank of @node or 0 if @node is %NULL.
*/
static size_t bst_node_rank(struct bst_node *node) {
return node ? node->rank : 0;
}
/**
* bst_is_right_child - Internal helper function
* @node: Node to check.
*
* Return: %true if @node is the right child of @node->parent. %false if
* @node->parent is %NULL or if @node is the left child of
* @node->parent.
*/
static bool bst_is_right_child(struct bst_node *node) {
DEBUG_ASSERT(node);
DEBUG_ASSERT(!node->parent || node->parent->child[0] == node ||
node->parent->child[1] == node);
return node->parent && node->parent->child[1] == node;
}
/**
* bst_parent_ptr - Internal helper function
* @root: Tree.
* @node: Node in @root.
*
* Return: Pointer where @node is linked in the tree. If @node is the root node
* this is &root->root, otherwise it is the address of child pointer in the
* parent node of @node that points to @node.
*/
static struct bst_node **bst_parent_ptr(struct bst_root *root,
struct bst_node *node) {
DEBUG_ASSERT(root);
DEBUG_ASSERT(node);
struct bst_node **parent_ptr = node->parent ?
&node->parent->child[bst_is_right_child(node)] : &root->root;
DEBUG_ASSERT(*parent_ptr == node);
return parent_ptr;
}
/**
* bst_link_node - Internal helper function
* @parent: Target node.
* @is_right_child: Index of child to set.
* @child: New child node.
*
* Set child node in @parent. If @child is not %NULL, update it to point to
* @parent.
*/
static void bst_link_node(struct bst_node *parent,
bool is_right_child,
struct bst_node *child) {
parent->child[is_right_child] = child;
if (child) {
child->parent = parent;
}
}
/**
* bst_move_node - Internal helper function
* @root: Tree.
* @old_node: Node to unlink.
* @new_node: Node to link where @old_node was.
*
* Replace node in @root at @old_node with @new_node.
*/
static void bst_move_node(struct bst_root *root,
struct bst_node *old_node,
struct bst_node *new_node) {
DEBUG_ASSERT(root);
DEBUG_ASSERT(old_node);
*bst_parent_ptr(root, old_node) = new_node;
if (new_node) {
new_node->parent = old_node->parent;
}
old_node->parent = NULL;
}
/**
* bst_rotate - Internal helper function
* @root: Tree.
* @up: Node to move up.
* @down: Node to move down.
* @up_was_right_child: %true if @up was the right child of @down.
*
* Swap nodes @up and @down (pictured for up_was_right_child==false):
*
* down up
* / \ / \
* up C A down
* / \ / \
* A B B C
*
* Caller is responsible for updating the rank of the moved nodes.
*/
static void bst_rotate(struct bst_root *root, struct bst_node *up,
struct bst_node *down, bool up_was_right_child) {
DEBUG_ASSERT(down->child[up_was_right_child] == up);
struct bst_node *move_subtree = up->child[!up_was_right_child];
bst_move_node(root, down, up);
bst_link_node(down, up_was_right_child, move_subtree);
bst_link_node(up, !up_was_right_child, down);
}
/**
* bst_rotate_insert - Internal helper function
* @root: Tree.
* @up1: Node to move up if a single rotate is enough.
* @down: Node to move down.
* @up_was_right_child: %true if @up1 was the right child of @down.
*
* Rotate sub-tree (once or twice) after insert and update ranks.
*/
static void bst_rotate_insert(struct bst_root *root, struct bst_node *up1,
struct bst_node *down, bool up_was_right_child) {
DEBUG_ASSERT(down->child[up_was_right_child] == up1);
DEBUG_ASSERT(up1->rank == down->rank);
DEBUG_ASSERT(down->rank >=
bst_node_rank(down->child[!up_was_right_child]) + 2);
struct bst_node *up2 = up1->child[!up_was_right_child];
if (bst_node_rank(up2) >= down->rank - 1) {
DEBUG_ASSERT(bst_node_rank(up2) == down->rank - 1);
/*
* Swap nodes @up2 and @up1 then @up2 and @down
* (pictured for up_was_right_child==false):
*
* down down up2
* / \ / \ / \
* up1 D up2 D up1 down
* / \ / \ / \ / \
* A up2 up1 C A B C D
* / \ / \
* B C A B
*/
up2->rank++;
DEBUG_ASSERT(up1->rank == up2->rank);
bst_rotate(root, up2, up1, !up_was_right_child);
up1->rank--;
bst_rotate(root, up2, down, up_was_right_child);
down->rank--;
} else {
/*
* Swap nodes @up1 and @down (pictured for up_was_right_child==false):
*
* down up1
* / \ / \
* up1 C A down
* / \ / \
* A B B C
*/
bst_rotate(root, up1, down, up_was_right_child);
down->rank--;
}
}
/**
* bst_update_rank_insert - Internal helper function
* @root: Tree.
* @node: Node to start scan at.
*
* Promote nodes and/or rotate sub-trees to make @root a valid WAVL tree again.
*/
void bst_update_rank_insert(struct bst_root *root, struct bst_node *node) {
size_t rank;
DEBUG_ASSERT(root);
DEBUG_ASSERT(node);
DEBUG_ASSERT(node->rank == 1); /* Inserted node must have rank 1 */
while (node) {
bool is_right_child = bst_is_right_child(node);
/*
* At this point the rank of @node is 1 greater than it was before the
* insert.
*
* For the tree to be valid, the parent of any node is allowed to a
* rank 1 or 2 greater than its child nodes. Assuming the tree was valid
* before the insert, the @node->parent currently has the same rank as
* @node or it has a rank one grater than the rank of @node. Incremeting
* the rank @node->parent to be 2 greater than the rank of @node would
* be unnecessary as it could not have had that rank already. Leaving
* the rank of @node->parent at the same rank as @node would result in
* en invalid tree. That means that the rank of @node->parent should now
* be 1 greater than the rank of @node (if that is possible).
*/
rank = node->rank + 1;
node = node->parent;
if (!node || node->rank >= rank) {
DEBUG_ASSERT(!node || node->rank == rank);
/*
* Stop updating if we have reached the root, or a node that already
* has a rank greater than the node child node we inserted or
* updated as the tree is now valid.
*/
return;
}
DEBUG_ASSERT(node->rank + 1 == rank);
if (bst_node_rank(node->child[!is_right_child]) + 2 < rank) {
/*
* Rank of @node cannot be incremented. This means it can be moved
* down and demoted instead.
*
* The tree can be rotated as pictured below. (a is @node which
* could not be promoted. Numbers are known relative ranks.)
*
* If rank of c is 2 less than the rank of a (b is inserted or
* promoted node):
*
* a2 b2
* / \ / \
* b2 D0 => A a1
* / \ / \
* A c0 c0 D0
* / \ / \
* B C B C
*
* If rank of c is 1 less than the rank of a (b is promoted node, c
* is inserted or promoted node):
* a2 a2 __c2__
* / \ / \ / \
* b2 D0 c2 D0 b1 a1
* / \ => / \ => / \ / \
* A0 c1 b1 C A0 B C D0
* / \ / \
* B C A0 B
*/
bst_rotate_insert(root, node->child[is_right_child], node,
is_right_child);
return;
}
node->rank = rank;
}
}
/**
* bst_rotate_delete - Internal helper function
* @root: Tree.
* @up1: Node to move up if a single rotate is enough.
* @down: Node to move down.
* @up_was_right_child: %true if @up1 was the right child of @down.
*
* Rotate sub-tree (once or twice) after delete and update ranks.
*/
static void bst_rotate_delete(struct bst_root *root, struct bst_node *up1,
struct bst_node *down, bool up_was_right_child) {
DEBUG_ASSERT(down->child[up_was_right_child] == up1);
DEBUG_ASSERT(up1->rank == down->rank - 1);
DEBUG_ASSERT(down->rank ==
bst_node_rank(down->child[!up_was_right_child]) + 3);
struct bst_node *up2 = up1->child[!up_was_right_child];
if (bst_node_rank(up1->child[up_was_right_child]) <= down->rank - 3) {
DEBUG_ASSERT(bst_node_rank(up2) == down->rank - 2);
/*
* Swap nodes @up2 and @up1 then @up2 and @down
* (pictured for up_was_right_child==false):
*
* down(0) down up2(0)
* / \ / \ / \
* up1(-1) D(-3) up2 D up1(-2) down(-2)
* / \ / \ / \ / \
* A(-3) up2(-2) up1 C A(-3) B C D(-3)
* / \ / \
* B C A(-3) B
*/
DEBUG_ASSERT(up1->rank == down->rank - 1);
DEBUG_ASSERT(up2->rank == down->rank - 2);
bst_rotate(root, up2, up1, !up_was_right_child);
bst_rotate(root, up2, down, up_was_right_child);
up2->rank += 2;
up1->rank--;
down->rank -= 2;
} else {
/*
* Swap nodes @up1 and @down (pictured for up_was_right_child==false):
*
* down(0) up1(0)
* / \ / \
* up1(-1) C(-3) A(-2) down(-1)
* / \ / \
* A(-2) B(-2/-3) B(-2/-3) C(-3)
*/
bst_rotate(root, up1, down, up_was_right_child);
up1->rank++;
down->rank--;
if (bst_node_rank(down->child[0]) == down->rank - 2 &&
bst_node_rank(down->child[1]) == down->rank - 2) {
/* Demote down if possible. (Required if down is a leaf node) */
down->rank--;
}
}
}
/**
* bst_update_rank_delete - Internal helper function
* @root: Tree.
* @node: Node to start scan at. This is the parent of the node that
* was removed from the tree. Note that the removed node will
* be a different node than the node passed to bst_delete if
* that node had two children.
* @is_right_child: %true if the right child of @node was deleted.
*
* Demote nodes and/or rotate sub-trees to make @root a valid WAVL tree again.
*/
static void bst_update_rank_delete(struct bst_root *root, struct bst_node *node,
bool is_right_child) {
DEBUG_ASSERT(root);
DEBUG_ASSERT(node);
DEBUG_ASSERT(bst_node_rank(node->child[is_right_child]) <=
bst_node_rank(node->child[!is_right_child]));
while (node) {
DEBUG_ASSERT(node->rank > bst_node_rank(node->child[!is_right_child]));
DEBUG_ASSERT(node->rank - 1 >
bst_node_rank(node->child[is_right_child]));
DEBUG_ASSERT(node->rank <=
bst_node_rank(node->child[!is_right_child]) + 2);
/*
* At this point the rank of @node->child[is_right_child] has been
* decremented. We may need to also decrement the rank of @node.
*/
if (!node->child[0] && !node->child[1]) {
/* Always demote leaf node (from 2 to 1) */
/* We should not be in this function if the rank is alrady 1 */
DEBUG_ASSERT(node->rank == 2);
} else if (node->rank <=
bst_node_rank(node->child[is_right_child]) + 2) {
/*
* If rank of @node does not need to change then we now have a valid
* tree.
*/
return;
}
DEBUG_ASSERT(node->rank > 1);
node->rank--;
if (node->rank <= bst_node_rank(node->child[!is_right_child])) {
/* We demoted @node, but it is now invalid on the other side */
DEBUG_ASSERT(node->rank ==
bst_node_rank(node->child[!is_right_child]));
if (bst_node_rank(node->child[!is_right_child]->child[0]) ==
node->rank - 2 &&
bst_node_rank(node->child[!is_right_child]->child[1]) ==
node->rank - 2) {
/* If the other child can be demoted, demote it and move up */
node->child[!is_right_child]->rank--;
} else {
/*
* If the other child can not be demoted, rotate instead. This
* will produce a valid tree without changing the rank of the
* node linked at the current spot @node in the tree.
*
* Undo demotion as current bst_rotate_delete implemention
* assumes node rank is unchanged.
*/
node->rank++;
bst_rotate_delete(root, node->child[!is_right_child], node,
!is_right_child);
return;
}
}
is_right_child = bst_is_right_child(node);
node = node->parent;
}
}
/**
* bst_find_edge - Internal helper function
* @node: Node to start search at.
* @edge: Direction if search.
*
* Return: leftmost (if @edge is %false) or rightmost (if @edge is %true) node
* in subtree with @node as root.
*/
static struct bst_node *bst_find_edge(struct bst_node *node, bool edge) {
struct bst_node *saved_node;
DEBUG_ASSERT(node);
do {
saved_node = node;
node = node->child[edge];
} while (node);
return saved_node;
}
/**
* bst_delete_all_helper - Internal helper function
* @root: Tree.
* @node: Node to delete (most be the leftmost node in @root).
*
* Helper function to delete leftmost node in @root, assuming all other nodes
* will be deleted next.
*/
void bst_delete_all_helper(struct bst_root *root, struct bst_node *node) {
DEBUG_ASSERT(root);
DEBUG_ASSERT(node);
DEBUG_ASSERT(!node->child[0]);
bst_move_node(root, node, node->child[1]);
}
void bst_delete(struct bst_root *root, struct bst_node *node) {
DEBUG_ASSERT(root);
DEBUG_ASSERT(node);
struct bst_node *new_child;
bool node_is_right_child = bst_is_right_child(node);
struct bst_node *update_rank_start = node->parent;
bool update_rank_is_right_child = node_is_right_child;
if (!node->child[0]) {
/*
* If @node has no left child, link its right child in its place. (The
* right child could be %NULL in this case)
*/
new_child = node->child[1];
} else if (!node->child[1]) {
/*
* If @node has no right child, link its left child in its place.
*/
DEBUG_ASSERT(node->child[0]);
new_child = node->child[0];
} else {
/*
* If @node has both left and right children, delete (from the tree
* structure point of view) the left-most node in the right sub-tree or
* the right-most node in the left sub-tree instead. Either side would
* work.
*/
struct bst_node *edge_node = bst_find_edge(
node->child[!node_is_right_child], node_is_right_child);
struct bst_node *edge_child = edge_node->child[!node_is_right_child];
update_rank_start = edge_node->parent;
update_rank_is_right_child = bst_is_right_child(edge_node);
if (update_rank_start == node) {
update_rank_start = edge_node;
update_rank_is_right_child = !node_is_right_child;
}
DEBUG_ASSERT(update_rank_start);
bst_move_node(root, edge_node, edge_child);
new_child = edge_node;
DEBUG_ASSERT(new_child);
bst_link_node(new_child, 0, node->child[0]);
bst_link_node(new_child, 1, node->child[1]);
new_child->rank = node->rank;
}
bst_move_node(root, node, new_child);
node->rank = 0;
if (update_rank_start) {
bst_update_rank_delete(root, update_rank_start, update_rank_is_right_child);
}
}
/**
* bst_prev_next - Internal helper function
* @root: Tree.
* @node: Node to move from.
* @dir_next: Directon to move.
*
* Return: If @node is %NULL and @dir_next is %false, right-most node in @root.
* If @node is %NULL and @dir_next is %true, left-most node in @root.
* If @node is not %NULL and @dir_next is %false, right-most node to the
* left of @node.
* If @node is not %NULL and @dir_next is %true, left-most node to the
* right of @node.
* %NULL if the node described above does not exist.
*/
static struct bst_node *bst_prev_next(const struct bst_root *root,
struct bst_node *node,
bool dir_next) {
DEBUG_ASSERT(root);
struct bst_node *next_child = node ? node->child[dir_next] : root->root;
if (!node && !next_child) {
return NULL; /* Empty tree */
}
/*
* Comments below assume @dir_next is %true. For the @dir_next is %false
* case, swap left and right.
*/
if (next_child) {
/* There is a right child, return the left-most node in that subtree */
return bst_find_edge(next_child, !dir_next);
} else {
/* No right child, next node is the first right parent */
struct bst_node *next_parent = node;
while (bst_is_right_child(next_parent) == dir_next) {
next_parent = next_parent->parent;
if (!next_parent) {
return NULL;
}
}
return next_parent->parent;
}
}
struct bst_node *bst_prev(struct bst_root *root, struct bst_node *node) {
return bst_prev_next(root, node, false);
}
struct bst_node *bst_next(const struct bst_root *root, struct bst_node *node) {
return bst_prev_next(root, node, true);
}