404 lines
14 KiB
C
404 lines
14 KiB
C
|
/*
|
||
|
* This file is part of the COMROGUE Operating System for Raspberry Pi
|
||
|
*
|
||
|
* Copyright (c) 2013, Eric J. Bowersox / Erbosoft Enterprises
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This program is free for commercial and non-commercial use as long as the following conditions are
|
||
|
* adhered to.
|
||
|
*
|
||
|
* Copyright in this file remains Eric J. Bowersox and/or Erbosoft, and as such any copyright notices
|
||
|
* in the code are not to be removed.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||
|
* provided that the following conditions are met:
|
||
|
*
|
||
|
* * Redistributions of source code must retain the above copyright notice, this list of conditions and
|
||
|
* the following disclaimer.
|
||
|
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
||
|
* the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*
|
||
|
* "Raspberry Pi" is a trademark of the Raspberry Pi Foundation.
|
||
|
*/
|
||
|
#define __COMROGUE_KERNEL_LIB__
|
||
|
#include <comrogue/types.h>
|
||
|
#include <comrogue/internals/rbtree.h>
|
||
|
#include <comrogue/internals/seg.h>
|
||
|
#include <comrogue/internals/trace.h>
|
||
|
|
||
|
#ifdef THIS_FILE
|
||
|
#undef THIS_FILE
|
||
|
DECLARE_THIS_FILE
|
||
|
#endif
|
||
|
|
||
|
/*------------------------------------------------------------------------------------------------------
|
||
|
* An implementation of left-leaning red-black 2-3 trees as detailed in "Left-leaning Red-Black Trees,"
|
||
|
* Robert Sedgwick, Princeton University, 2008 (http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf).
|
||
|
* See also Java source at https://gist.github.com/741080.
|
||
|
*
|
||
|
* Note that we store the node color as a single bit in the low-order bit of the "right" node pointer.
|
||
|
* This is do-able since, for all cases, pointers to instances of RBTREENODE will be 4-byte aligned.
|
||
|
*------------------------------------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* A standard compare-by-value function for tree keys, which compares the numeric values of the keys as
|
||
|
* unsigned integers.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - k1 = First key value to compare.
|
||
|
* - k2 = Second key value to compare.
|
||
|
*
|
||
|
* Returns:
|
||
|
* 0 if the keys are equal; an integer less than 0 if k1 is less than k2; an integer greater than 0 if
|
||
|
* k1 is greater than k2.
|
||
|
*/
|
||
|
SEG_LIB_CODE INT32 RbtStdCompareByValue(TREEKEY k1, TREEKEY k2)
|
||
|
{
|
||
|
if (k1 == k2)
|
||
|
return 0;
|
||
|
if (((UINT_PTR)k1) < ((UINT_PTR)k2))
|
||
|
return -1;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Rotates a subtree "leftward," so that the root node of the tree is the former root node's right child.
|
||
|
* The new root node inherits the former root node's color, and the former root node turns red.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to the root node of the subtree.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the new root node of the subtree after the rotation.
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE rotate_left(PRBTREENODE ptn)
|
||
|
{
|
||
|
register PRBTREENODE ptnNewRoot = rbtNodeRight(ptn);
|
||
|
ASSERT(ptnNewRoot);
|
||
|
rbtSetNodeRight(ptn, ptnNewRoot->ptnLeft);
|
||
|
ptnNewRoot->ptnLeft = ptn;
|
||
|
rbtSetNodeColor(ptnNewRoot, rbtNodeColor(ptn));
|
||
|
rbtSetNodeColor(ptn, RED);
|
||
|
return ptnNewRoot;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Rotates a subtree "rightward," so that the root node of the tree is the former root node's left child.
|
||
|
* The new root node inherits the former root node's color, and the former root node turns red.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to theroot node of the subtree.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the new root node of the subtree after the rotation.
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE rotate_right(PRBTREENODE ptn)
|
||
|
{
|
||
|
register PRBTREENODE ptnNewRoot = ptn->ptnLeft;
|
||
|
ASSERT(ptnNewRoot);
|
||
|
ptn->ptnLeft = rbtNodeRight(ptnNewRoot);
|
||
|
rbtSetNodeRight(ptnNewRoot, ptn);
|
||
|
rbtSetNodeColor(ptnNewRoot, rbtNodeColor(ptn));
|
||
|
rbtSetNodeColor(ptn, RED);
|
||
|
return ptnNewRoot;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Flips the color of the specified node and both its immediate children.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to the node to be color-flipped.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Nothing.
|
||
|
*/
|
||
|
SEG_LIB_CODE static void color_flip(PRBTREENODE ptn)
|
||
|
{
|
||
|
rbtToggleColor(ptn);
|
||
|
rbtToggleColor(ptn->ptnLeft);
|
||
|
rbtToggleColor(rbtNodeRight(ptn));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fixes up the given subtree after an insertion or deletion, to ensure that it maintains the invariants
|
||
|
* that no two consecutive links in the tree may be red, and that all red links must lean left.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to the root node of the subtree to be fixed up.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the new root node of the subtree after fixup is performed.
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE fix_up(PRBTREENODE ptn)
|
||
|
{
|
||
|
if (rbtIsRed(rbtNodeRight(ptn)) && !rbtIsRed(ptn->ptnLeft))
|
||
|
ptn = rotate_left(ptn);
|
||
|
if (rbtIsRed(ptn->ptnLeft) && rbtIsRed(ptn->ptnLeft->ptnLeft))
|
||
|
ptn = rotate_right(ptn);
|
||
|
if (rbtIsRed(ptn->ptnLeft) && rbtIsRed(rbtNodeRight(ptn)))
|
||
|
color_flip(ptn);
|
||
|
return ptn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Inserts a new node under the current subtree. An O(log n) operation.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptree = Pointer to the tree head structure, containing the compare function.
|
||
|
* - ptnCurrent = Pointer to the current subtree we're inserting into.
|
||
|
* - ptnNew = Pointer to the new tree node to be inserted. This node must have been initialized with
|
||
|
* the rbtInitNode macro to contain a key, NULL left and right pointers, and be red. It is
|
||
|
* assumed that the node's key does NOT already exist in the tree.
|
||
|
*
|
||
|
* Returns:
|
||
|
* The pointer to the new subtree after the insertion is performed.
|
||
|
*
|
||
|
* N.B.:
|
||
|
* This function is recursive; however, the nature of the tree guarantees that the stack space consumed
|
||
|
* by its stack frames will be O(log n).
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE insert_under(PRBTREE ptree, PRBTREENODE ptnCurrent, PRBTREENODE ptnNew)
|
||
|
{
|
||
|
register int cmp; /* compare result */
|
||
|
|
||
|
if (!ptnCurrent)
|
||
|
return ptnNew; /* degenerate case */
|
||
|
cmp = (*(ptree->pfnTreeCompare))(ptnNew->treekey, ptnCurrent->treekey);
|
||
|
ASSERT(cmp != 0);
|
||
|
if (cmp < 0)
|
||
|
ptnCurrent->ptnLeft = insert_under(ptree, ptnCurrent->ptnLeft, ptnNew);
|
||
|
else
|
||
|
rbtSetNodeRight(ptnCurrent, insert_under(ptree, rbtNodeRight(ptnCurrent), ptnNew));
|
||
|
return fix_up(ptnCurrent);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Inserts a new node into the tree. An O(log n) operation.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptree = Pointer to the tree head structure.
|
||
|
* - ptnNew = Pointer to the new tree node to be inserted. This node must have been initialized with
|
||
|
* the rbtInitNode macro to contain a key, NULL left and right pointers, and be red. It is
|
||
|
* assumed that the node's key does NOT already exist in the tree.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Nothing.
|
||
|
*/
|
||
|
SEG_LIB_CODE void RbtInsert(PRBTREE ptree, PRBTREENODE ptnNew)
|
||
|
{
|
||
|
ptree->ptnRoot = insert_under(ptree, ptree->ptnRoot, ptnNew);
|
||
|
rbtSetNodeColor(ptree->ptnRoot, BLACK);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Locates a node in the tree by key. An O(log n) operation.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptree = Pointer to the tree head structure.
|
||
|
* - key = Key value to be looked up.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the node where the key is found, or NULL if not found.
|
||
|
*/
|
||
|
SEG_LIB_CODE PRBTREENODE RbtFind(PRBTREE ptree, TREEKEY key)
|
||
|
{
|
||
|
register PRBTREENODE ptn = ptree->ptnRoot; /* current node */
|
||
|
register int cmp; /* compare result */
|
||
|
|
||
|
while (ptn)
|
||
|
{
|
||
|
cmp = (*(ptree->pfnTreeCompare))(key, ptn->treekey);
|
||
|
if (cmp == 0)
|
||
|
break; /* found */
|
||
|
else if (cmp < 0)
|
||
|
ptn = ptn->ptnLeft;
|
||
|
else
|
||
|
ptn = rbtNodeRight(ptn);
|
||
|
}
|
||
|
|
||
|
return ptn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Finds the "minimum" node in the subtree (the one at the bottom end of the left spine of the subtree).
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to the subtree to be searched.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the leftmost node in the subtree.
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE find_min(PRBTREENODE ptn)
|
||
|
{
|
||
|
while (ptn->ptnLeft)
|
||
|
ptn = ptn->ptnLeft;
|
||
|
return ptn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Finds the "minimum" node int he tree (the one at the bottom end of the left spine of the tree).
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptree = Pointer to the tree head structure.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the leftmost node in the tree. If the tree has no nodes, NULL is returned.
|
||
|
*/
|
||
|
SEG_LIB_CODE PRBTREENODE RbtFindMin(PRBTREE ptree)
|
||
|
{
|
||
|
return ptree->ptnRoot ? find_min(ptree->ptnRoot) : NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fixup required to delete the leftmost node; we maintain the invariant that either the current node
|
||
|
* or its left child is red. After a color flip, we resolve any successive reds on the right with rotations
|
||
|
* and another color flip.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to root of subtree to be fixed up.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to root of subtree after fixup.
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE move_red_left(PRBTREENODE ptn)
|
||
|
{
|
||
|
color_flip(ptn);
|
||
|
if (rbtNodeRight(ptn) && rbtIsRed(rbtNodeRight(ptn)->ptnLeft))
|
||
|
{
|
||
|
rbtSetNodeRight(ptn, rotate_right(rbtNodeRight(ptn)));
|
||
|
ptn = rotate_left(ptn);
|
||
|
color_flip(ptn);
|
||
|
}
|
||
|
return ptn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fixup required to delete an internal node, rotating left-leaning red links to the right.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to root of subtree to be fixed up.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to root of subtree after fixup.
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE move_red_right(PRBTREENODE ptn)
|
||
|
{
|
||
|
color_flip(ptn);
|
||
|
if (ptn->ptnLeft && rbtIsRed(ptn->ptnLeft->ptnLeft))
|
||
|
{
|
||
|
ptn = rotate_right(ptn);
|
||
|
color_flip(ptn);
|
||
|
}
|
||
|
return ptn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Deletes the leftmost node in the subtree. (Note that "deletes" means "removes from the tree." No memory
|
||
|
* delete operation is actually performed.)
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptn = Pointer to root of subtree to have its leftmost node deleted.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to root of subtree after having the leftmost node deleted.
|
||
|
*
|
||
|
* N.B.:
|
||
|
* This function is recursive; however, the nature of the tree guarantees that the stack space consumed
|
||
|
* by its stack frames will be O(log n).
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE delete_min(PRBTREENODE ptn)
|
||
|
{
|
||
|
if (!(ptn->ptnLeft))
|
||
|
return rbtNodeRight(ptn);
|
||
|
if (!rbtIsRed(ptn->ptnLeft) && !rbtIsRed(ptn->ptnLeft->ptnLeft))
|
||
|
ptn = move_red_left(ptn);
|
||
|
ptn->ptnLeft = delete_min(ptn->ptnLeft);
|
||
|
return fix_up(ptn);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Detetes the node in the subtree having an arbitrary key. An O(log n) operation.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptree = Pointer to the tree head structure, containing the compare function.
|
||
|
* - ptnCurrent = Pointer to the root of the current subtree we're deleting from.
|
||
|
* - key = Key value we're deleting from the tree. It is assumed that this key value exists in the subtree.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Pointer to the root of the subtree after the node has been deleted.
|
||
|
*
|
||
|
* N.B.:
|
||
|
* This function is recursive; however, the nature of the tree guarantees that the stack space consumed
|
||
|
* by its stack frames (and those of delete_min, where we call it) will be O(log n).
|
||
|
*/
|
||
|
SEG_LIB_CODE static PRBTREENODE delete_from_under(PRBTREE ptree, PRBTREENODE ptnCurrent, TREEKEY key)
|
||
|
{
|
||
|
register int cmp = (*(ptree->pfnTreeCompare))(key, ptnCurrent->treekey);
|
||
|
if (cmp < 0)
|
||
|
{
|
||
|
/* hunt down the left subtree */
|
||
|
if (!rbtIsRed(ptnCurrent->ptnLeft) && !rbtIsRed(ptnCurrent->ptnLeft->ptnLeft))
|
||
|
ptnCurrent = move_red_left(ptnCurrent);
|
||
|
ptnCurrent->ptnLeft = delete_from_under(ptree, ptnCurrent->ptnLeft, key);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (rbtIsRed(ptnCurrent->ptnLeft))
|
||
|
{
|
||
|
ptnCurrent = rotate_right(ptnCurrent);
|
||
|
cmp = (*(ptree->pfnTreeCompare))(key, ptnCurrent->treekey);
|
||
|
}
|
||
|
if ((cmp == 0) && !rbtNodeRight(ptnCurrent))
|
||
|
return ptnCurrent->ptnLeft; /* degenerate case */
|
||
|
if ( !rbtIsRed(rbtNodeRight(ptnCurrent))
|
||
|
&& (!rbtNodeRight(ptnCurrent) || !rbtIsRed(rbtNodeRight(ptnCurrent)->ptnLeft)))
|
||
|
{
|
||
|
ptnCurrent = move_red_right(ptnCurrent);
|
||
|
cmp = (*(ptree->pfnTreeCompare))(key, ptnCurrent->treekey);
|
||
|
}
|
||
|
if (cmp == 0)
|
||
|
{
|
||
|
/*
|
||
|
* Here we find the minimum node in the right subtree, unlink it, and link it into place in place of
|
||
|
* ptnCurrent (i.e. node pointed to by ptnCurrent should no longer be referenced). We inherit the
|
||
|
* child pointers and color of ptnCurrent (minus the reference from the right-hand tree where applicable).
|
||
|
*/
|
||
|
register PRBTREENODE ptnMin = find_min(rbtNodeRight(ptnCurrent));
|
||
|
rbtSetNodeRight(ptnMin, delete_min(rbtNodeRight(ptnCurrent)));
|
||
|
ptnMin->ptnLeft = ptnCurrent->ptnLeft;
|
||
|
rbtSetNodeColor(ptnMin, rbtNodeColor(ptnCurrent));
|
||
|
ptnCurrent = ptnMin;
|
||
|
}
|
||
|
else /* hunt down the right subtree */
|
||
|
rbtSetNodeRight(ptnCurrent, delete_from_under(ptree, rbtNodeRight(ptnCurrent), key));
|
||
|
}
|
||
|
return fix_up(ptnCurrent);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Detetes the node in the tree having an arbitrary key. An O(log n) operation.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* - ptree = Pointer to the tree head structure.
|
||
|
* - key = Key value we're deleting from the tree. It is assumed that this key value exists in the subtree.
|
||
|
*
|
||
|
* Returns:
|
||
|
* Nothing.
|
||
|
*/
|
||
|
SEG_LIB_CODE void RbtDelete(PRBTREE ptree, TREEKEY key)
|
||
|
{
|
||
|
ptree->ptnRoot = delete_from_under(ptree, ptree->ptnRoot, key);
|
||
|
if (ptree->ptnRoot)
|
||
|
rbtSetNodeColor(ptree->ptnRoot, BLACK);
|
||
|
}
|