/** @file | |
An OrderedCollectionLib instance that provides a red-black tree | |
implementation, and allocates and releases tree nodes with | |
MemoryAllocationLib. | |
This library instance is useful when a fast associative container is needed. | |
Worst case time complexity is O(log n) for Find(), Next(), Prev(), Min(), | |
Max(), Insert(), and Delete(), where "n" is the number of elements in the | |
tree. Complete ordered traversal takes O(n) time. | |
The implementation is also useful as a fast priority queue. | |
Copyright (C) 2014, Red Hat, Inc. | |
Copyright (c) 2014, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials are licensed and made available | |
under the terms and conditions of the BSD License that accompanies this | |
distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php. | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT | |
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <Library/OrderedCollectionLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
typedef enum { | |
RedBlackTreeRed, | |
RedBlackTreeBlack | |
} RED_BLACK_TREE_COLOR; | |
// | |
// Incomplete types and convenience typedefs are present in the library class | |
// header. Beside completing the types, we introduce typedefs here that reflect | |
// the implementation closely. | |
// | |
typedef ORDERED_COLLECTION RED_BLACK_TREE; | |
typedef ORDERED_COLLECTION_ENTRY RED_BLACK_TREE_NODE; | |
typedef ORDERED_COLLECTION_USER_COMPARE RED_BLACK_TREE_USER_COMPARE; | |
typedef ORDERED_COLLECTION_KEY_COMPARE RED_BLACK_TREE_KEY_COMPARE; | |
struct ORDERED_COLLECTION { | |
RED_BLACK_TREE_NODE *Root; | |
RED_BLACK_TREE_USER_COMPARE UserStructCompare; | |
RED_BLACK_TREE_KEY_COMPARE KeyCompare; | |
}; | |
struct ORDERED_COLLECTION_ENTRY { | |
VOID *UserStruct; | |
RED_BLACK_TREE_NODE *Parent; | |
RED_BLACK_TREE_NODE *Left; | |
RED_BLACK_TREE_NODE *Right; | |
RED_BLACK_TREE_COLOR Color; | |
}; | |
/** | |
Retrieve the user structure linked by the specified tree node. | |
Read-only operation. | |
@param[in] Node Pointer to the tree node whose associated user structure we | |
want to retrieve. The caller is responsible for passing a | |
non-NULL argument. | |
@return Pointer to user structure linked by Node. | |
**/ | |
VOID * | |
EFIAPI | |
OrderedCollectionUserStruct ( | |
IN CONST RED_BLACK_TREE_NODE *Node | |
) | |
{ | |
return Node->UserStruct; | |
} | |
/** | |
A slow function that asserts that the tree is a valid red-black tree, and | |
that it orders user structures correctly. | |
Read-only operation. | |
This function uses the stack for recursion and is not recommended for | |
"production use". | |
@param[in] Tree The tree to validate. | |
**/ | |
VOID | |
RedBlackTreeValidate ( | |
IN CONST RED_BLACK_TREE *Tree | |
); | |
/** | |
Allocate and initialize the RED_BLACK_TREE structure. | |
Allocation occurs via MemoryAllocationLib's AllocatePool() function. | |
@param[in] UserStructCompare This caller-provided function will be used to | |
order two user structures linked into the | |
tree, during the insertion procedure. | |
@param[in] KeyCompare This caller-provided function will be used to | |
order the standalone search key against user | |
structures linked into the tree, during the | |
lookup procedure. | |
@retval NULL If allocation failed. | |
@return Pointer to the allocated, initialized RED_BLACK_TREE structure, | |
otherwise. | |
**/ | |
RED_BLACK_TREE * | |
EFIAPI | |
OrderedCollectionInit ( | |
IN RED_BLACK_TREE_USER_COMPARE UserStructCompare, | |
IN RED_BLACK_TREE_KEY_COMPARE KeyCompare | |
) | |
{ | |
RED_BLACK_TREE *Tree; | |
Tree = AllocatePool (sizeof *Tree); | |
if (Tree == NULL) { | |
return NULL; | |
} | |
Tree->Root = NULL; | |
Tree->UserStructCompare = UserStructCompare; | |
Tree->KeyCompare = KeyCompare; | |
if (FeaturePcdGet (PcdValidateOrderedCollection)) { | |
RedBlackTreeValidate (Tree); | |
} | |
return Tree; | |
} | |
/** | |
Check whether the tree is empty (has no nodes). | |
Read-only operation. | |
@param[in] Tree The tree to check for emptiness. | |
@retval TRUE The tree is empty. | |
@retval FALSE The tree is not empty. | |
**/ | |
BOOLEAN | |
EFIAPI | |
OrderedCollectionIsEmpty ( | |
IN CONST RED_BLACK_TREE *Tree | |
) | |
{ | |
return (BOOLEAN)(Tree->Root == NULL); | |
} | |
/** | |
Uninitialize and release an empty RED_BLACK_TREE structure. | |
Read-write operation. | |
Release occurs via MemoryAllocationLib's FreePool() function. | |
It is the caller's responsibility to delete all nodes from the tree before | |
calling this function. | |
@param[in] Tree The empty tree to uninitialize and release. | |
**/ | |
VOID | |
EFIAPI | |
OrderedCollectionUninit ( | |
IN RED_BLACK_TREE *Tree | |
) | |
{ | |
ASSERT (OrderedCollectionIsEmpty (Tree)); | |
FreePool (Tree); | |
} | |
/** | |
Look up the tree node that links the user structure that matches the | |
specified standalone key. | |
Read-only operation. | |
@param[in] Tree The tree to search for StandaloneKey. | |
@param[in] StandaloneKey The key to locate among the user structures linked | |
into Tree. StandaloneKey will be passed to | |
Tree->KeyCompare(). | |
@retval NULL StandaloneKey could not be found. | |
@return The tree node that links to the user structure matching | |
StandaloneKey, otherwise. | |
**/ | |
RED_BLACK_TREE_NODE * | |
EFIAPI | |
OrderedCollectionFind ( | |
IN CONST RED_BLACK_TREE *Tree, | |
IN CONST VOID *StandaloneKey | |
) | |
{ | |
RED_BLACK_TREE_NODE *Node; | |
Node = Tree->Root; | |
while (Node != NULL) { | |
INTN Result; | |
Result = Tree->KeyCompare (StandaloneKey, Node->UserStruct); | |
if (Result == 0) { | |
break; | |
} | |
Node = (Result < 0) ? Node->Left : Node->Right; | |
} | |
return Node; | |
} | |
/** | |
Find the tree node of the minimum user structure stored in the tree. | |
Read-only operation. | |
@param[in] Tree The tree to return the minimum node of. The user structure | |
linked by the minimum node compares less than all other user | |
structures in the tree. | |
@retval NULL If Tree is empty. | |
@return The tree node that links the minimum user structure, otherwise. | |
**/ | |
RED_BLACK_TREE_NODE * | |
EFIAPI | |
OrderedCollectionMin ( | |
IN CONST RED_BLACK_TREE *Tree | |
) | |
{ | |
RED_BLACK_TREE_NODE *Node; | |
Node = Tree->Root; | |
if (Node == NULL) { | |
return NULL; | |
} | |
while (Node->Left != NULL) { | |
Node = Node->Left; | |
} | |
return Node; | |
} | |
/** | |
Find the tree node of the maximum user structure stored in the tree. | |
Read-only operation. | |
@param[in] Tree The tree to return the maximum node of. The user structure | |
linked by the maximum node compares greater than all other | |
user structures in the tree. | |
@retval NULL If Tree is empty. | |
@return The tree node that links the maximum user structure, otherwise. | |
**/ | |
RED_BLACK_TREE_NODE * | |
EFIAPI | |
OrderedCollectionMax ( | |
IN CONST RED_BLACK_TREE *Tree | |
) | |
{ | |
RED_BLACK_TREE_NODE *Node; | |
Node = Tree->Root; | |
if (Node == NULL) { | |
return NULL; | |
} | |
while (Node->Right != NULL) { | |
Node = Node->Right; | |
} | |
return Node; | |
} | |
/** | |
Get the tree node of the least user structure that is greater than the one | |
linked by Node. | |
Read-only operation. | |
@param[in] Node The node to get the successor node of. | |
@retval NULL If Node is NULL, or Node is the maximum node of its containing | |
tree (ie. Node has no successor node). | |
@return The tree node linking the least user structure that is greater | |
than the one linked by Node, otherwise. | |
**/ | |
RED_BLACK_TREE_NODE * | |
EFIAPI | |
OrderedCollectionNext ( | |
IN CONST RED_BLACK_TREE_NODE *Node | |
) | |
{ | |
RED_BLACK_TREE_NODE *Walk; | |
CONST RED_BLACK_TREE_NODE *Child; | |
if (Node == NULL) { | |
return NULL; | |
} | |
// | |
// If Node has a right subtree, then the successor is the minimum node of | |
// that subtree. | |
// | |
Walk = Node->Right; | |
if (Walk != NULL) { | |
while (Walk->Left != NULL) { | |
Walk = Walk->Left; | |
} | |
return Walk; | |
} | |
// | |
// Otherwise we have to ascend as long as we're our parent's right child (ie. | |
// ascending to the left). | |
// | |
Child = Node; | |
Walk = Child->Parent; | |
while (Walk != NULL && Child == Walk->Right) { | |
Child = Walk; | |
Walk = Child->Parent; | |
} | |
return Walk; | |
} | |
/** | |
Get the tree node of the greatest user structure that is less than the one | |
linked by Node. | |
Read-only operation. | |
@param[in] Node The node to get the predecessor node of. | |
@retval NULL If Node is NULL, or Node is the minimum node of its containing | |
tree (ie. Node has no predecessor node). | |
@return The tree node linking the greatest user structure that is less | |
than the one linked by Node, otherwise. | |
**/ | |
RED_BLACK_TREE_NODE * | |
EFIAPI | |
OrderedCollectionPrev ( | |
IN CONST RED_BLACK_TREE_NODE *Node | |
) | |
{ | |
RED_BLACK_TREE_NODE *Walk; | |
CONST RED_BLACK_TREE_NODE *Child; | |
if (Node == NULL) { | |
return NULL; | |
} | |
// | |
// If Node has a left subtree, then the predecessor is the maximum node of | |
// that subtree. | |
// | |
Walk = Node->Left; | |
if (Walk != NULL) { | |
while (Walk->Right != NULL) { | |
Walk = Walk->Right; | |
} | |
return Walk; | |
} | |
// | |
// Otherwise we have to ascend as long as we're our parent's left child (ie. | |
// ascending to the right). | |
// | |
Child = Node; | |
Walk = Child->Parent; | |
while (Walk != NULL && Child == Walk->Left) { | |
Child = Walk; | |
Walk = Child->Parent; | |
} | |
return Walk; | |
} | |
/** | |
Rotate tree nodes around Pivot to the right. | |
Parent Parent | |
| | | |
Pivot LeftChild | |
/ . . \_ | |
LeftChild Node1 ---> Node2 Pivot | |
. \ / . | |
Node2 LeftRightChild LeftRightChild Node1 | |
The ordering Node2 < LeftChild < LeftRightChild < Pivot < Node1 is kept | |
intact. Parent (if any) is either at the left extreme or the right extreme of | |
this ordering, and that relation is also kept intact. | |
Edges marked with a dot (".") don't change during rotation. | |
Internal read-write operation. | |
@param[in,out] Pivot The tree node to rotate other nodes right around. It | |
is the caller's responsibility to ensure that | |
Pivot->Left is not NULL. | |
@param[out] NewRoot If Pivot has a parent node on input, then the | |
function updates Pivot's original parent on output | |
according to the rotation, and NewRoot is not | |
accessed. | |
If Pivot has no parent node on input (ie. Pivot is | |
the root of the tree), then the function stores the | |
new root node of the tree in NewRoot. | |
**/ | |
VOID | |
RedBlackTreeRotateRight ( | |
IN OUT RED_BLACK_TREE_NODE *Pivot, | |
OUT RED_BLACK_TREE_NODE **NewRoot | |
) | |
{ | |
RED_BLACK_TREE_NODE *Parent; | |
RED_BLACK_TREE_NODE *LeftChild; | |
RED_BLACK_TREE_NODE *LeftRightChild; | |
Parent = Pivot->Parent; | |
LeftChild = Pivot->Left; | |
LeftRightChild = LeftChild->Right; | |
Pivot->Left = LeftRightChild; | |
if (LeftRightChild != NULL) { | |
LeftRightChild->Parent = Pivot; | |
} | |
LeftChild->Parent = Parent; | |
if (Parent == NULL) { | |
*NewRoot = LeftChild; | |
} else { | |
if (Pivot == Parent->Left) { | |
Parent->Left = LeftChild; | |
} else { | |
Parent->Right = LeftChild; | |
} | |
} | |
LeftChild->Right = Pivot; | |
Pivot->Parent = LeftChild; | |
} | |
/** | |
Rotate tree nodes around Pivot to the left. | |
Parent Parent | |
| | | |
Pivot RightChild | |
. \ / . | |
Node1 RightChild ---> Pivot Node2 | |
/. . \_ | |
RightLeftChild Node2 Node1 RightLeftChild | |
The ordering Node1 < Pivot < RightLeftChild < RightChild < Node2 is kept | |
intact. Parent (if any) is either at the left extreme or the right extreme of | |
this ordering, and that relation is also kept intact. | |
Edges marked with a dot (".") don't change during rotation. | |
Internal read-write operation. | |
@param[in,out] Pivot The tree node to rotate other nodes left around. It | |
is the caller's responsibility to ensure that | |
Pivot->Right is not NULL. | |
@param[out] NewRoot If Pivot has a parent node on input, then the | |
function updates Pivot's original parent on output | |
according to the rotation, and NewRoot is not | |
accessed. | |
If Pivot has no parent node on input (ie. Pivot is | |
the root of the tree), then the function stores the | |
new root node of the tree in NewRoot. | |
**/ | |
VOID | |
RedBlackTreeRotateLeft ( | |
IN OUT RED_BLACK_TREE_NODE *Pivot, | |
OUT RED_BLACK_TREE_NODE **NewRoot | |
) | |
{ | |
RED_BLACK_TREE_NODE *Parent; | |
RED_BLACK_TREE_NODE *RightChild; | |
RED_BLACK_TREE_NODE *RightLeftChild; | |
Parent = Pivot->Parent; | |
RightChild = Pivot->Right; | |
RightLeftChild = RightChild->Left; | |
Pivot->Right = RightLeftChild; | |
if (RightLeftChild != NULL) { | |
RightLeftChild->Parent = Pivot; | |
} | |
RightChild->Parent = Parent; | |
if (Parent == NULL) { | |
*NewRoot = RightChild; | |
} else { | |
if (Pivot == Parent->Left) { | |
Parent->Left = RightChild; | |
} else { | |
Parent->Right = RightChild; | |
} | |
} | |
RightChild->Left = Pivot; | |
Pivot->Parent = RightChild; | |
} | |
/** | |
Insert (link) a user structure into the tree. | |
Read-write operation. | |
This function allocates the new tree node with MemoryAllocationLib's | |
AllocatePool() function. | |
@param[in,out] Tree The tree to insert UserStruct into. | |
@param[out] Node The meaning of this optional, output-only | |
parameter depends on the return value of the | |
function. | |
When insertion is successful (RETURN_SUCCESS), | |
Node is set on output to the new tree node that | |
now links UserStruct. | |
When insertion fails due to lack of memory | |
(RETURN_OUT_OF_RESOURCES), Node is not changed. | |
When insertion fails due to key collision (ie. | |
another user structure is already in the tree that | |
compares equal to UserStruct), with return value | |
RETURN_ALREADY_STARTED, then Node is set on output | |
to the node that links the colliding user | |
structure. This enables "find-or-insert" in one | |
function call, or helps with later removal of the | |
colliding element. | |
@param[in] UserStruct The user structure to link into the tree. | |
UserStruct is ordered against in-tree user | |
structures with the Tree->UserStructCompare() | |
function. | |
@retval RETURN_SUCCESS Insertion successful. A new tree node has | |
been allocated, linking UserStruct. The new | |
tree node is reported back in Node (if the | |
caller requested it). | |
Existing RED_BLACK_TREE_NODE pointers into | |
Tree remain valid. For example, on-going | |
iterations in the caller can continue with | |
OrderedCollectionNext() / | |
OrderedCollectionPrev(), and they will | |
return the new node at some point if user | |
structure order dictates it. | |
@retval RETURN_OUT_OF_RESOURCES AllocatePool() failed to allocate memory for | |
the new tree node. The tree has not been | |
changed. Existing RED_BLACK_TREE_NODE | |
pointers into Tree remain valid. | |
@retval RETURN_ALREADY_STARTED A user structure has been found in the tree | |
that compares equal to UserStruct. The node | |
linking the colliding user structure is | |
reported back in Node (if the caller | |
requested it). The tree has not been | |
changed. Existing RED_BLACK_TREE_NODE | |
pointers into Tree remain valid. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
OrderedCollectionInsert ( | |
IN OUT RED_BLACK_TREE *Tree, | |
OUT RED_BLACK_TREE_NODE **Node OPTIONAL, | |
IN VOID *UserStruct | |
) | |
{ | |
RED_BLACK_TREE_NODE *Tmp; | |
RED_BLACK_TREE_NODE *Parent; | |
INTN Result; | |
RETURN_STATUS Status; | |
RED_BLACK_TREE_NODE *NewRoot; | |
Tmp = Tree->Root; | |
Parent = NULL; | |
Result = 0; | |
// | |
// First look for a collision, saving the last examined node for the case | |
// when there's no collision. | |
// | |
while (Tmp != NULL) { | |
Result = Tree->UserStructCompare (UserStruct, Tmp->UserStruct); | |
if (Result == 0) { | |
break; | |
} | |
Parent = Tmp; | |
Tmp = (Result < 0) ? Tmp->Left : Tmp->Right; | |
} | |
if (Tmp != NULL) { | |
if (Node != NULL) { | |
*Node = Tmp; | |
} | |
Status = RETURN_ALREADY_STARTED; | |
goto Done; | |
} | |
// | |
// no collision, allocate a new node | |
// | |
Tmp = AllocatePool (sizeof *Tmp); | |
if (Tmp == NULL) { | |
Status = RETURN_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
if (Node != NULL) { | |
*Node = Tmp; | |
} | |
// | |
// reference the user structure from the node | |
// | |
Tmp->UserStruct = UserStruct; | |
// | |
// Link the node as a child to the correct side of the parent. | |
// If there's no parent, the new node is the root node in the tree. | |
// | |
Tmp->Parent = Parent; | |
Tmp->Left = NULL; | |
Tmp->Right = NULL; | |
if (Parent == NULL) { | |
Tree->Root = Tmp; | |
Tmp->Color = RedBlackTreeBlack; | |
Status = RETURN_SUCCESS; | |
goto Done; | |
} | |
if (Result < 0) { | |
Parent->Left = Tmp; | |
} else { | |
Parent->Right = Tmp; | |
} | |
Tmp->Color = RedBlackTreeRed; | |
// | |
// Red-black tree properties: | |
// | |
// #1 Each node is either red or black (RED_BLACK_TREE_NODE.Color). | |
// | |
// #2 Each leaf (ie. a pseudo-node pointed-to by a NULL valued | |
// RED_BLACK_TREE_NODE.Left or RED_BLACK_TREE_NODE.Right field) is black. | |
// | |
// #3 Each red node has two black children. | |
// | |
// #4 For any node N, and for any leaves L1 and L2 reachable from N, the | |
// paths N..L1 and N..L2 contain the same number of black nodes. | |
// | |
// #5 The root node is black. | |
// | |
// By replacing a leaf with a red node above, only property #3 may have been | |
// broken. (Note that this is the only edge across which property #3 might | |
// not hold in the entire tree.) Restore property #3. | |
// | |
NewRoot = Tree->Root; | |
while (Tmp != NewRoot && Parent->Color == RedBlackTreeRed) { | |
RED_BLACK_TREE_NODE *GrandParent; | |
RED_BLACK_TREE_NODE *Uncle; | |
// | |
// Tmp is not the root node. Tmp is red. Tmp's parent is red. (Breaking | |
// property #3.) | |
// | |
// Due to property #5, Tmp's parent cannot be the root node, hence Tmp's | |
// grandparent exists. | |
// | |
// Tmp's grandparent is black, because property #3 is only broken between | |
// Tmp and Tmp's parent. | |
// | |
GrandParent = Parent->Parent; | |
if (Parent == GrandParent->Left) { | |
Uncle = GrandParent->Right; | |
if (Uncle != NULL && Uncle->Color == RedBlackTreeRed) { | |
// | |
// GrandParent (black) | |
// / \_ | |
// Parent (red) Uncle (red) | |
// | | |
// Tmp (red) | |
// | |
Parent->Color = RedBlackTreeBlack; | |
Uncle->Color = RedBlackTreeBlack; | |
GrandParent->Color = RedBlackTreeRed; | |
// | |
// GrandParent (red) | |
// / \_ | |
// Parent (black) Uncle (black) | |
// | | |
// Tmp (red) | |
// | |
// We restored property #3 between Tmp and Tmp's parent, without | |
// breaking property #4. However, we may have broken property #3 | |
// between Tmp's grandparent and Tmp's great-grandparent (if any), so | |
// repeat the loop for Tmp's grandparent. | |
// | |
// If Tmp's grandparent has no parent, then the loop will terminate, | |
// and we will have broken property #5, by coloring the root red. We'll | |
// restore property #5 after the loop, without breaking any others. | |
// | |
Tmp = GrandParent; | |
Parent = Tmp->Parent; | |
} else { | |
// | |
// Tmp's uncle is black (satisfied by the case too when Tmp's uncle is | |
// NULL, see property #2). | |
// | |
if (Tmp == Parent->Right) { | |
// | |
// GrandParent (black): D | |
// / \_ | |
// Parent (red): A Uncle (black): E | |
// \_ | |
// Tmp (red): B | |
// \_ | |
// black: C | |
// | |
// Rotate left, pivoting on node A. This keeps the breakage of | |
// property #3 in the same spot, and keeps other properties intact | |
// (because both Tmp and its parent are red). | |
// | |
Tmp = Parent; | |
RedBlackTreeRotateLeft (Tmp, &NewRoot); | |
Parent = Tmp->Parent; | |
// | |
// With the rotation we reached the same configuration as if Tmp had | |
// been a left child to begin with. | |
// | |
// GrandParent (black): D | |
// / \_ | |
// Parent (red): B Uncle (black): E | |
// / \_ | |
// Tmp (red): A black: C | |
// | |
ASSERT (GrandParent == Parent->Parent); | |
} | |
Parent->Color = RedBlackTreeBlack; | |
GrandParent->Color = RedBlackTreeRed; | |
// | |
// Property #3 is now restored, but we've broken property #4. Namely, | |
// paths going through node E now see a decrease in black count, while | |
// paths going through node B don't. | |
// | |
// GrandParent (red): D | |
// / \_ | |
// Parent (black): B Uncle (black): E | |
// / \_ | |
// Tmp (red): A black: C | |
// | |
RedBlackTreeRotateRight (GrandParent, &NewRoot); | |
// | |
// Property #4 has been restored for node E, and preserved for others. | |
// | |
// Parent (black): B | |
// / \_ | |
// Tmp (red): A [GrandParent] (red): D | |
// / \_ | |
// black: C [Uncle] (black): E | |
// | |
// This configuration terminates the loop because Tmp's parent is now | |
// black. | |
// | |
} | |
} else { | |
// | |
// Symmetrical to the other branch. | |
// | |
Uncle = GrandParent->Left; | |
if (Uncle != NULL && Uncle->Color == RedBlackTreeRed) { | |
Parent->Color = RedBlackTreeBlack; | |
Uncle->Color = RedBlackTreeBlack; | |
GrandParent->Color = RedBlackTreeRed; | |
Tmp = GrandParent; | |
Parent = Tmp->Parent; | |
} else { | |
if (Tmp == Parent->Left) { | |
Tmp = Parent; | |
RedBlackTreeRotateRight (Tmp, &NewRoot); | |
Parent = Tmp->Parent; | |
ASSERT (GrandParent == Parent->Parent); | |
} | |
Parent->Color = RedBlackTreeBlack; | |
GrandParent->Color = RedBlackTreeRed; | |
RedBlackTreeRotateLeft (GrandParent, &NewRoot); | |
} | |
} | |
} | |
NewRoot->Color = RedBlackTreeBlack; | |
Tree->Root = NewRoot; | |
Status = RETURN_SUCCESS; | |
Done: | |
if (FeaturePcdGet (PcdValidateOrderedCollection)) { | |
RedBlackTreeValidate (Tree); | |
} | |
return Status; | |
} | |
/** | |
Check if a node is black, allowing for leaf nodes (see property #2). | |
This is a convenience shorthand. | |
param[in] Node The node to check. Node may be NULL, corresponding to a leaf. | |
@return If Node is NULL or colored black. | |
**/ | |
BOOLEAN | |
NodeIsNullOrBlack ( | |
IN CONST RED_BLACK_TREE_NODE *Node | |
) | |
{ | |
return (BOOLEAN)(Node == NULL || Node->Color == RedBlackTreeBlack); | |
} | |
/** | |
Delete a node from the tree, unlinking the associated user structure. | |
Read-write operation. | |
@param[in,out] Tree The tree to delete Node from. | |
@param[in] Node The tree node to delete from Tree. The caller is | |
responsible for ensuring that Node belongs to | |
Tree, and that Node is non-NULL and valid. Node is | |
typically an earlier return value, or output | |
parameter, of: | |
- OrderedCollectionFind(), for deleting a node by | |
user structure key, | |
- OrderedCollectionMin() / OrderedCollectionMax(), | |
for deleting the minimum / maximum node, | |
- OrderedCollectionNext() / | |
OrderedCollectionPrev(), for deleting a node | |
found during an iteration, | |
- OrderedCollectionInsert() with return value | |
RETURN_ALREADY_STARTED, for deleting a node | |
whose linked user structure caused collision | |
during insertion. | |
Given a non-empty Tree, Tree->Root is also a valid | |
Node argument (typically used for simplicity in | |
loops that empty the tree completely). | |
Node is released with MemoryAllocationLib's | |
FreePool() function. | |
Existing RED_BLACK_TREE_NODE pointers (ie. | |
iterators) *different* from Node remain valid. For | |
example: | |
- OrderedCollectionNext() / | |
OrderedCollectionPrev() iterations in the caller | |
can be continued from Node, if | |
OrderedCollectionNext() or | |
OrderedCollectionPrev() is called on Node | |
*before* OrderedCollectionDelete() is. That is, | |
fetch the successor / predecessor node first, | |
then delete Node. | |
- On-going iterations in the caller that would | |
have otherwise returned Node at some point, as | |
dictated by user structure order, will correctly | |
reflect the absence of Node after | |
OrderedCollectionDelete() is called | |
mid-iteration. | |
@param[out] UserStruct If the caller provides this optional output-only | |
parameter, then on output it is set to the user | |
structure originally linked by Node (which is now | |
freed). | |
This is a convenience that may save the caller a | |
OrderedCollectionUserStruct() invocation before | |
calling OrderedCollectionDelete(), in order to | |
retrieve the user structure being unlinked. | |
**/ | |
VOID | |
EFIAPI | |
OrderedCollectionDelete ( | |
IN OUT RED_BLACK_TREE *Tree, | |
IN RED_BLACK_TREE_NODE *Node, | |
OUT VOID **UserStruct OPTIONAL | |
) | |
{ | |
RED_BLACK_TREE_NODE *NewRoot; | |
RED_BLACK_TREE_NODE *OrigLeftChild; | |
RED_BLACK_TREE_NODE *OrigRightChild; | |
RED_BLACK_TREE_NODE *OrigParent; | |
RED_BLACK_TREE_NODE *Child; | |
RED_BLACK_TREE_NODE *Parent; | |
RED_BLACK_TREE_COLOR ColorOfUnlinked; | |
NewRoot = Tree->Root; | |
OrigLeftChild = Node->Left, | |
OrigRightChild = Node->Right, | |
OrigParent = Node->Parent; | |
if (UserStruct != NULL) { | |
*UserStruct = Node->UserStruct; | |
} | |
// | |
// After this block, no matter which branch we take: | |
// - Child will point to the unique (or NULL) original child of the node that | |
// we will have unlinked, | |
// - Parent will point to the *position* of the original parent of the node | |
// that we will have unlinked. | |
// | |
if (OrigLeftChild == NULL || OrigRightChild == NULL) { | |
// | |
// Node has at most one child. We can connect that child (if any) with | |
// Node's parent (if any), unlinking Node. This will preserve ordering | |
// because the subtree rooted in Node's child (if any) remains on the same | |
// side of Node's parent (if any) that Node was before. | |
// | |
Parent = OrigParent; | |
Child = (OrigLeftChild != NULL) ? OrigLeftChild : OrigRightChild; | |
ColorOfUnlinked = Node->Color; | |
if (Child != NULL) { | |
Child->Parent = Parent; | |
} | |
if (OrigParent == NULL) { | |
NewRoot = Child; | |
} else { | |
if (Node == OrigParent->Left) { | |
OrigParent->Left = Child; | |
} else { | |
OrigParent->Right = Child; | |
} | |
} | |
} else { | |
// | |
// Node has two children. We unlink Node's successor, and then link it into | |
// Node's place, keeping Node's original color. This preserves ordering | |
// because: | |
// - Node's left subtree is less than Node, hence less than Node's | |
// successor. | |
// - Node's right subtree is greater than Node. Node's successor is the | |
// minimum of that subtree, hence Node's successor is less than Node's | |
// right subtree with its minimum removed. | |
// - Node's successor is in Node's subtree, hence it falls on the same side | |
// of Node's parent as Node itself. The relinking doesn't change this | |
// relation. | |
// | |
RED_BLACK_TREE_NODE *ToRelink; | |
ToRelink = OrigRightChild; | |
if (ToRelink->Left == NULL) { | |
// | |
// OrigRightChild itself is Node's successor, it has no left child: | |
// | |
// OrigParent | |
// | | |
// Node: B | |
// / \_ | |
// OrigLeftChild: A OrigRightChild: E <--- Parent, ToRelink | |
// \_ | |
// F <--- Child | |
// | |
Parent = OrigRightChild; | |
Child = OrigRightChild->Right; | |
} else { | |
do { | |
ToRelink = ToRelink->Left; | |
} while (ToRelink->Left != NULL); | |
// | |
// Node's successor is the minimum of OrigRightChild's proper subtree: | |
// | |
// OrigParent | |
// | | |
// Node: B | |
// / \_ | |
// OrigLeftChild: A OrigRightChild: E <--- Parent | |
// / | |
// C <--- ToRelink | |
// \_ | |
// D <--- Child | |
Parent = ToRelink->Parent; | |
Child = ToRelink->Right; | |
// | |
// Unlink Node's successor (ie. ToRelink): | |
// | |
// OrigParent | |
// | | |
// Node: B | |
// / \_ | |
// OrigLeftChild: A OrigRightChild: E <--- Parent | |
// / | |
// D <--- Child | |
// | |
// C <--- ToRelink | |
// | |
Parent->Left = Child; | |
if (Child != NULL) { | |
Child->Parent = Parent; | |
} | |
// | |
// We start to link Node's unlinked successor into Node's place: | |
// | |
// OrigParent | |
// | | |
// Node: B C <--- ToRelink | |
// / \_ | |
// OrigLeftChild: A OrigRightChild: E <--- Parent | |
// / | |
// D <--- Child | |
// | |
// | |
// | |
ToRelink->Right = OrigRightChild; | |
OrigRightChild->Parent = ToRelink; | |
} | |
// | |
// The rest handles both cases, attaching ToRelink (Node's original | |
// successor) to OrigLeftChild and OrigParent. | |
// | |
// Parent, | |
// OrigParent ToRelink OrigParent | |
// | | | | |
// Node: B | Node: B Parent | |
// v | | |
// OrigRightChild: E C <--- ToRelink | | |
// / \ / \ v | |
// OrigLeftChild: A F OrigLeftChild: A OrigRightChild: E | |
// ^ / | |
// | D <--- Child | |
// Child | |
// | |
ToRelink->Left = OrigLeftChild; | |
OrigLeftChild->Parent = ToRelink; | |
// | |
// Node's color must be preserved in Node's original place. | |
// | |
ColorOfUnlinked = ToRelink->Color; | |
ToRelink->Color = Node->Color; | |
// | |
// Finish linking Node's unlinked successor into Node's place. | |
// | |
// Parent, | |
// Node: B ToRelink Node: B | |
// | | |
// OrigParent | OrigParent Parent | |
// | v | | | |
// OrigRightChild: E C <--- ToRelink | | |
// / \ / \ v | |
// OrigLeftChild: A F OrigLeftChild: A OrigRightChild: E | |
// ^ / | |
// | D <--- Child | |
// Child | |
// | |
ToRelink->Parent = OrigParent; | |
if (OrigParent == NULL) { | |
NewRoot = ToRelink; | |
} else { | |
if (Node == OrigParent->Left) { | |
OrigParent->Left = ToRelink; | |
} else { | |
OrigParent->Right = ToRelink; | |
} | |
} | |
} | |
FreePool (Node); | |
// | |
// If the node that we unlinked from its original spot (ie. Node itself, or | |
// Node's successor), was red, then we broke neither property #3 nor property | |
// #4: we didn't create any red-red edge between Child and Parent, and we | |
// didn't change the black count on any path. | |
// | |
if (ColorOfUnlinked == RedBlackTreeBlack) { | |
// | |
// However, if the unlinked node was black, then we have to transfer its | |
// "black-increment" to its unique child (pointed-to by Child), lest we | |
// break property #4 for its ancestors. | |
// | |
// If Child is red, we can simply color it black. If Child is black | |
// already, we can't technically transfer a black-increment to it, due to | |
// property #1. | |
// | |
// In the following loop we ascend searching for a red node to color black, | |
// or until we reach the root (in which case we can drop the | |
// black-increment). Inside the loop body, Child has a black value of 2, | |
// transitorily breaking property #1 locally, but maintaining property #4 | |
// globally. | |
// | |
// Rotations in the loop preserve property #4. | |
// | |
while (Child != NewRoot && NodeIsNullOrBlack (Child)) { | |
RED_BLACK_TREE_NODE *Sibling; | |
RED_BLACK_TREE_NODE *LeftNephew; | |
RED_BLACK_TREE_NODE *RightNephew; | |
if (Child == Parent->Left) { | |
Sibling = Parent->Right; | |
// | |
// Sibling can never be NULL (ie. a leaf). | |
// | |
// If Sibling was NULL, then the black count on the path from Parent to | |
// Sibling would equal Parent's black value, plus 1 (due to property | |
// #2). Whereas the black count on the path from Parent to any leaf via | |
// Child would be at least Parent's black value, plus 2 (due to Child's | |
// black value of 2). This would clash with property #4. | |
// | |
// (Sibling can be black of course, but it has to be an internal node. | |
// Internality allows Sibling to have children, bumping the black | |
// counts of paths that go through it.) | |
// | |
ASSERT (Sibling != NULL); | |
if (Sibling->Color == RedBlackTreeRed) { | |
// | |
// Sibling's red color implies its children (if any), node C and node | |
// E, are black (property #3). It also implies that Parent is black. | |
// | |
// grandparent grandparent | |
// | | | |
// Parent,b:B b:D | |
// / \ / \_ | |
// Child,2b:A Sibling,r:D ---> Parent,r:B b:E | |
// /\ /\_ | |
// b:C b:E Child,2b:A Sibling,b:C | |
// | |
Sibling->Color = RedBlackTreeBlack; | |
Parent->Color = RedBlackTreeRed; | |
RedBlackTreeRotateLeft (Parent, &NewRoot); | |
Sibling = Parent->Right; | |
// | |
// Same reasoning as above. | |
// | |
ASSERT (Sibling != NULL); | |
} | |
// | |
// Sibling is black, and not NULL. (Ie. Sibling is a black internal | |
// node.) | |
// | |
ASSERT (Sibling->Color == RedBlackTreeBlack); | |
LeftNephew = Sibling->Left; | |
RightNephew = Sibling->Right; | |
if (NodeIsNullOrBlack (LeftNephew) && | |
NodeIsNullOrBlack (RightNephew)) { | |
// | |
// In this case we can "steal" one black value from Child and Sibling | |
// each, and pass it to Parent. "Stealing" means that Sibling (black | |
// value 1) becomes red, Child (black value 2) becomes singly-black, | |
// and Parent will have to be examined if it can eat the | |
// black-increment. | |
// | |
// Sibling is allowed to become red because both of its children are | |
// black (property #3). | |
// | |
// grandparent Parent | |
// | | | |
// Parent,x:B Child,x:B | |
// / \ / \_ | |
// Child,2b:A Sibling,b:D ---> b:A r:D | |
// /\ /\_ | |
// LeftNephew,b:C RightNephew,b:E b:C b:E | |
// | |
Sibling->Color = RedBlackTreeRed; | |
Child = Parent; | |
Parent = Parent->Parent; | |
// | |
// Continue ascending. | |
// | |
} else { | |
// | |
// At least one nephew is red. | |
// | |
if (NodeIsNullOrBlack (RightNephew)) { | |
// | |
// Since the right nephew is black, the left nephew is red. Due to | |
// property #3, LeftNephew has two black children, hence node E is | |
// black. | |
// | |
// Together with the rotation, this enables us to color node F red | |
// (because property #3 will be satisfied). We flip node D to black | |
// to maintain property #4. | |
// | |
// grandparent grandparent | |
// | | | |
// Parent,x:B Parent,x:B | |
// /\ /\_ | |
// Child,2b:A Sibling,b:F ---> Child,2b:A Sibling,b:D | |
// /\ / \_ | |
// LeftNephew,r:D RightNephew,b:G b:C RightNephew,r:F | |
// /\ /\_ | |
// b:C b:E b:E b:G | |
// | |
LeftNephew->Color = RedBlackTreeBlack; | |
Sibling->Color = RedBlackTreeRed; | |
RedBlackTreeRotateRight (Sibling, &NewRoot); | |
Sibling = Parent->Right; | |
RightNephew = Sibling->Right; | |
// | |
// These operations ensure that... | |
// | |
} | |
// | |
// ... RightNephew is definitely red here, plus Sibling is (still) | |
// black and non-NULL. | |
// | |
ASSERT (RightNephew != NULL); | |
ASSERT (RightNephew->Color == RedBlackTreeRed); | |
ASSERT (Sibling != NULL); | |
ASSERT (Sibling->Color == RedBlackTreeBlack); | |
// | |
// In this case we can flush the extra black-increment immediately, | |
// restoring property #1 for Child (node A): we color RightNephew | |
// (node E) from red to black. | |
// | |
// In order to maintain property #4, we exchange colors between | |
// Parent and Sibling (nodes B and D), and rotate left around Parent | |
// (node B). The transformation doesn't change the black count | |
// increase incurred by each partial path, eg. | |
// - ascending from node A: 2 + x == 1 + 1 + x | |
// - ascending from node C: y + 1 + x == y + 1 + x | |
// - ascending from node E: 0 + 1 + x == 1 + x | |
// | |
// The color exchange is valid, because even if x stands for red, | |
// both children of node D are black after the transformation | |
// (preserving property #3). | |
// | |
// grandparent grandparent | |
// | | | |
// Parent,x:B x:D | |
// / \ / \_ | |
// Child,2b:A Sibling,b:D ---> b:B b:E | |
// / \ / \_ | |
// y:C RightNephew,r:E b:A y:C | |
// | |
// | |
Sibling->Color = Parent->Color; | |
Parent->Color = RedBlackTreeBlack; | |
RightNephew->Color = RedBlackTreeBlack; | |
RedBlackTreeRotateLeft (Parent, &NewRoot); | |
Child = NewRoot; | |
// | |
// This terminates the loop. | |
// | |
} | |
} else { | |
// | |
// Mirrors the other branch. | |
// | |
Sibling = Parent->Left; | |
ASSERT (Sibling != NULL); | |
if (Sibling->Color == RedBlackTreeRed) { | |
Sibling->Color = RedBlackTreeBlack; | |
Parent->Color = RedBlackTreeRed; | |
RedBlackTreeRotateRight (Parent, &NewRoot); | |
Sibling = Parent->Left; | |
ASSERT (Sibling != NULL); | |
} | |
ASSERT (Sibling->Color == RedBlackTreeBlack); | |
RightNephew = Sibling->Right; | |
LeftNephew = Sibling->Left; | |
if (NodeIsNullOrBlack (RightNephew) && | |
NodeIsNullOrBlack (LeftNephew)) { | |
Sibling->Color = RedBlackTreeRed; | |
Child = Parent; | |
Parent = Parent->Parent; | |
} else { | |
if (NodeIsNullOrBlack (LeftNephew)) { | |
RightNephew->Color = RedBlackTreeBlack; | |
Sibling->Color = RedBlackTreeRed; | |
RedBlackTreeRotateLeft (Sibling, &NewRoot); | |
Sibling = Parent->Left; | |
LeftNephew = Sibling->Left; | |
} | |
ASSERT (LeftNephew != NULL); | |
ASSERT (LeftNephew->Color == RedBlackTreeRed); | |
ASSERT (Sibling != NULL); | |
ASSERT (Sibling->Color == RedBlackTreeBlack); | |
Sibling->Color = Parent->Color; | |
Parent->Color = RedBlackTreeBlack; | |
LeftNephew->Color = RedBlackTreeBlack; | |
RedBlackTreeRotateRight (Parent, &NewRoot); | |
Child = NewRoot; | |
} | |
} | |
} | |
if (Child != NULL) { | |
Child->Color = RedBlackTreeBlack; | |
} | |
} | |
Tree->Root = NewRoot; | |
if (FeaturePcdGet (PcdValidateOrderedCollection)) { | |
RedBlackTreeValidate (Tree); | |
} | |
} | |
/** | |
Recursively check the red-black tree properties #1 to #4 on a node. | |
@param[in] Node The root of the subtree to validate. | |
@retval The black-height of Node's parent. | |
**/ | |
UINT32 | |
RedBlackTreeRecursiveCheck ( | |
IN CONST RED_BLACK_TREE_NODE *Node | |
) | |
{ | |
UINT32 LeftHeight; | |
UINT32 RightHeight; | |
// | |
// property #2 | |
// | |
if (Node == NULL) { | |
return 1; | |
} | |
// | |
// property #1 | |
// | |
ASSERT (Node->Color == RedBlackTreeRed || Node->Color == RedBlackTreeBlack); | |
// | |
// property #3 | |
// | |
if (Node->Color == RedBlackTreeRed) { | |
ASSERT (NodeIsNullOrBlack (Node->Left)); | |
ASSERT (NodeIsNullOrBlack (Node->Right)); | |
} | |
// | |
// property #4 | |
// | |
LeftHeight = RedBlackTreeRecursiveCheck (Node->Left); | |
RightHeight = RedBlackTreeRecursiveCheck (Node->Right); | |
ASSERT (LeftHeight == RightHeight); | |
return (Node->Color == RedBlackTreeBlack) + LeftHeight; | |
} | |
/** | |
A slow function that asserts that the tree is a valid red-black tree, and | |
that it orders user structures correctly. | |
Read-only operation. | |
This function uses the stack for recursion and is not recommended for | |
"production use". | |
@param[in] Tree The tree to validate. | |
**/ | |
VOID | |
RedBlackTreeValidate ( | |
IN CONST RED_BLACK_TREE *Tree | |
) | |
{ | |
UINT32 BlackHeight; | |
UINT32 ForwardCount; | |
UINT32 BackwardCount; | |
CONST RED_BLACK_TREE_NODE *Last; | |
CONST RED_BLACK_TREE_NODE *Node; | |
DEBUG ((DEBUG_VERBOSE, "%a: Tree=%p\n", __FUNCTION__, Tree)); | |
// | |
// property #5 | |
// | |
ASSERT (NodeIsNullOrBlack (Tree->Root)); | |
// | |
// check the other properties | |
// | |
BlackHeight = RedBlackTreeRecursiveCheck (Tree->Root) - 1; | |
// | |
// forward ordering | |
// | |
Last = OrderedCollectionMin (Tree); | |
ForwardCount = (Last != NULL); | |
for (Node = OrderedCollectionNext (Last); Node != NULL; | |
Node = OrderedCollectionNext (Last)) { | |
ASSERT (Tree->UserStructCompare (Last->UserStruct, Node->UserStruct) < 0); | |
Last = Node; | |
++ForwardCount; | |
} | |
// | |
// backward ordering | |
// | |
Last = OrderedCollectionMax (Tree); | |
BackwardCount = (Last != NULL); | |
for (Node = OrderedCollectionPrev (Last); Node != NULL; | |
Node = OrderedCollectionPrev (Last)) { | |
ASSERT (Tree->UserStructCompare (Last->UserStruct, Node->UserStruct) > 0); | |
Last = Node; | |
++BackwardCount; | |
} | |
ASSERT (ForwardCount == BackwardCount); | |
DEBUG ((DEBUG_VERBOSE, "%a: Tree=%p BlackHeight=%Ld Count=%Ld\n", | |
__FUNCTION__, Tree, (INT64)BlackHeight, (INT64)ForwardCount)); | |
} |