/* splay.c: SPLAY TREE IMPLEMENTATION
*
* $Id: //info.ravenbrook.com/project/mps/custom/cet/branch/2016-09-13/job004020/code/splay.c#1 $
* Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license.
*
* .purpose: Splay trees are used to manage potentially unbounded
* collections of ordered things. In the MPS these are usually
* address-ordered memory blocks.
*
* .source: <design/splay>
*
* .note.stack: It's important that the MPS have a bounded stack size,
* and this is a problem for tree algorithms. Basically, we have to
* avoid recursion. See design.mps.sp.sol.depth.no-recursion.
*/
#include "splay.h"
#include "mpm.h"
SRCID(splay, "$Id: //info.ravenbrook.com/project/mps/custom/cet/branch/2016-09-13/job004020/code/splay.c#1 $");
/* SPLAY_DEBUG -- switch for extra debugging
*
* Define SPLAY_DEBUG to enable extra consistency checking when modifying
* splay tree algorithms, which can be tricky to get right. This will
* check the tree size and ordering frequently.
*/
/* #define SPLAY_DEBUG */
#define SplayTreeSetRoot(splay, tree) BEGIN ((splay)->root = (tree)); END
#define SplayCompare(tree, key, node) (((tree)->compare)(node, key))
#define SplayHasUpdate(splay) ((splay)->updateNode != SplayTrivUpdate)
/* SplayTreeCheck -- check consistency of SplayTree
*
* See guide.impl.c.adt.check and <design/check>.
*/
Bool SplayTreeCheck(SplayTree splay)
{
UNUSED(splay);
CHECKS(SplayTree, splay);
CHECKL(FUNCHECK(splay->compare));
CHECKL(FUNCHECK(splay->nodeKey));
CHECKL(FUNCHECK(splay->updateNode));
/* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */
CHECKL(TreeCheck(splay->root));
return TRUE;
}
/* SplayTreeInit -- initialise a splay tree
*
* ``compare`` must provide a total ordering on node keys.
*
* ``nodeKey`` extracts a key from a tree node for passing to ``compare``.
*
* ``updateNode`` will be applied to nodes from bottom to top when the
* tree is restructured in order to maintain client properties (see
* design.mps.splay.prop). If SplayTrivUpdate is be passed, faster
* algorithms are chosen for splaying. Compare SplaySplitDown with
* SplaySplitRev.
*/
void SplayTreeInit(SplayTree splay,
TreeCompareFunction compare,
TreeKeyFunction nodeKey,
SplayUpdateNodeFunction updateNode)
{
AVER(splay != NULL);
AVER(FUNCHECK(compare));
AVER(FUNCHECK(nodeKey));
AVER(FUNCHECK(updateNode));
splay->compare = compare;
splay->nodeKey = nodeKey;
splay->updateNode = updateNode;
SplayTreeSetRoot(splay, TreeEMPTY);
splay->sig = SplayTreeSig;
AVERT(SplayTree, splay);
}
/* SplayTreeFinish -- finish a splay tree
*
* Does not attempt to descend or finish any tree nodes.
*
* TODO: Should probably fail on non-empty tree, so that client code is
* forced to decide what to do about that.
*/
void SplayTreeFinish(SplayTree splay)
{
AVERT(SplayTree, splay);
splay->sig = SigInvalid;
SplayTreeSetRoot(splay, TreeEMPTY);
splay->compare = NULL;
splay->nodeKey = NULL;
splay->updateNode = NULL;
}
/* SplayTrivUpdate -- trivial update method
*
* This is passed to SplayTreeInit to indicate that no client property
* maintenance is required. It can also be called to do nothing.
*/
void SplayTrivUpdate(SplayTree splay, Tree tree)
{
AVERT(SplayTree, splay);
AVERT(Tree, tree);
}
/* compareLess, compareGreater -- trivial comparisons
*
* These comparisons can be passed to SplaySplay to find the leftmost
* or rightmost nodes in a tree quickly.
*
* NOTE: It's also possible to make specialised versions of SplaySplit
* that traverse left and right unconditionally. These weren't found
* to have a significant performance advantage when benchmarking.
* RB 2014-02-23
*/
static Compare compareLess(Tree tree, TreeKey key)
{
UNUSED(tree);
UNUSED(key);
return CompareLESS;
}
static Compare compareGreater(Tree tree, TreeKey key)
{
UNUSED(tree);
UNUSED(key);
return CompareGREATER;
}
/* SplayDebugUpdate -- force update of client property
*
* A debugging utility to recursively update the client property of
* a subtree. May not be used in production MPS because it has
* indefinite stack usage. See .note.stack.
*/
void SplayDebugUpdate(SplayTree splay, Tree tree)
{
AVERT(SplayTree, splay);
AVERT(Tree, tree);
if (tree == TreeEMPTY)
return;
SplayDebugUpdate(splay, TreeLeft(tree));
SplayDebugUpdate(splay, TreeRight(tree));
splay->updateNode(splay, tree);
}
/* SplayDebugCount -- count and check order of tree
*
* This function may be called from a debugger or temporarily inserted
* during development to check a tree's integrity. It may not be called
* from the production MPS because it uses indefinite stack depth.
* See <code/tree.c#.note.stack>.
*/
Count SplayDebugCount(SplayTree splay)
{
AVERT(SplayTree, splay);
return TreeDebugCount(SplayTreeRoot(splay), splay->compare, splay->nodeKey);
}
/* SplayZig -- move to left child, prepending to right tree
*
* Link the top node of the middle tree into the left child of the
* right tree, then step to the left child. Returns new middle.
*
* See <design/splay/#impl.link.right>.
*
* middle rightNext middle
* B E A E
* / \ / \ => / \
* A C D F rightNext D F
* rightFirst /
* rightFirst B
* \
* C
*/
static Tree SplayZig(Tree middle, Tree *rightFirstIO, Tree *rightNextReturn)
{
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(rightFirstIO != NULL);
AVERT_CRITICAL(Tree, *rightFirstIO);
TreeSetLeft(*rightFirstIO, middle);
*rightNextReturn = *rightFirstIO;
*rightFirstIO = middle;
return TreeLeft(middle);
}
/* SplayZigZig -- move to left child, rotating on on to right tree
*
* Rotate the top node of the middle tree over the left child of the
* right tree, then step to the left child, completing a splay "zig zig"
* after an initial SplayZig. Returns new middle.
*
* middle rightNext middle rightNext
* B E A E
* / \ / \ => / \
* A C D F rightFirst B F
* rightFirst \
* D
* /
* C
*/
static Tree SplayZigZig(Tree middle, Tree *rightFirstIO, Tree rightNext)
{
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(rightFirstIO != NULL);
AVERT_CRITICAL(Tree, *rightFirstIO);
TreeSetLeft(*rightFirstIO, TreeRight(middle));
TreeSetRight(middle, *rightFirstIO);
TreeSetLeft(rightNext, middle);
*rightFirstIO = middle;
return TreeLeft(middle);
}
/* SplayZag -- mirror image of SplayZig */
static Tree SplayZag(Tree middle, Tree *leftLastIO, Tree *leftPrevReturn)
{
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(leftLastIO != NULL);
AVERT_CRITICAL(Tree, *leftLastIO);
TreeSetRight(*leftLastIO, middle);
*leftPrevReturn = *leftLastIO;
*leftLastIO = middle;
return TreeRight(middle);
}
/* SplayZagZag -- mirror image of SplayZigZig */
static Tree SplayZagZag(Tree middle, Tree *leftLastIO, Tree leftPrev)
{
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(leftLastIO != NULL);
AVERT_CRITICAL(Tree, *leftLastIO);
TreeSetRight(*leftLastIO, TreeLeft(middle));
TreeSetLeft(middle, *leftLastIO);
TreeSetRight(leftPrev, middle);
*leftLastIO = middle;
return TreeRight(middle);
}
/* SplayState -- the state of splaying between "split" and "assemble"
*
* Splaying is divided into two phases: splitting the tree into three,
* and then assembling a final tree. This allows for optimisation of
* certain operations, the key one being SplayTreeNeighbours, which is
* critical for coalescing memory blocks (see CBSInsert).
*
* Note that SplaySplitDown and SplaySplitRev use the trees slightly
* differently. SplaySplitRev does not provide "left" and "right", and
* "leftLast" and "rightFirst" are pointer-reversed spines.
*/
typedef struct SplayStateStruct {
Tree middle; /* always non-empty, has the found node at the root */
Tree left; /* nodes less than search key during split */
Tree leftLast; /* rightmost node on right spine of "left" */
Tree right; /* nodes greater than search key during split */
Tree rightFirst; /* leftmost node on left spine of "right" */
} SplayStateStruct, *SplayState;
/* SplaySplitDown -- divide the tree around a key
*
* Split a tree into three according to a key and a comparison,
* splaying nested left and right nodes. Preserves tree ordering.
* This is a top-down splay procedure, and does not use any recursion
* or require any parent pointers (see design.mps.impl.top-down).
*
* Returns cmp, the relationship of the root of the middle tree to the key,
* and a SplayState.
*
* Does *not* call update to maintain client properties. See SplaySplitRev.
*/
static Compare SplaySplitDown(SplayStateStruct *stateReturn,
SplayTree splay, TreeKey key,
TreeCompareFunction compare)
{
TreeStruct sentinel;
Tree middle, leftLast, rightFirst, leftPrev, rightNext;
Compare cmp;
AVERT(SplayTree, splay);
AVER(FUNCHECK(compare));
AVER(!SplayTreeIsEmpty(splay));
AVER(!SplayHasUpdate(splay));
TreeInit(&sentinel);
leftLast = &sentinel;
rightFirst = &sentinel;
middle = SplayTreeRoot(splay);
for (;;) {
cmp = compare(middle, key);
switch(cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
goto stop;
case CompareLESS:
if (!TreeHasLeft(middle))
goto stop;
middle = SplayZig(middle, &rightFirst, &rightNext);
cmp = compare(middle, key);
switch(cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
goto stop;
case CompareLESS:
if (!TreeHasLeft(middle))
goto stop;
middle = SplayZigZig(middle, &rightFirst, rightNext);
break;
case CompareGREATER:
if (!TreeHasRight(middle))
goto stop;
middle = SplayZag(middle, &leftLast, &leftPrev);
break;
}
break;
case CompareGREATER:
if (!TreeHasRight(middle))
goto stop;
middle = SplayZag(middle, &leftLast, &leftPrev);
cmp = compare(middle, key);
switch(cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
goto stop;
case CompareGREATER:
if (!TreeHasRight(middle))
goto stop;
middle = SplayZagZag(middle, &leftLast, leftPrev);
break;
case CompareLESS:
if (!TreeHasLeft(middle))
goto stop;
middle = SplayZig(middle, &rightFirst, &rightNext);
break;
}
break;
}
}
stop:
stateReturn->middle = middle;
stateReturn->left = TreeRight(&sentinel);
stateReturn->leftLast = leftLast == &sentinel ? TreeEMPTY : leftLast;
stateReturn->right = TreeLeft(&sentinel);
stateReturn->rightFirst = rightFirst == &sentinel ? TreeEMPTY : rightFirst;
return cmp;
}
/* SplayAssembleDown -- assemble left right and middle trees into one
*
* Takes the result of a SplaySplit and forms a single tree with the
* root of the middle tree as the root.
*
* left middle right middle
* B P V P
* / \ / \ / \ => / \
* A C N Q U X B V
* leftLast rightFirst / \ / \
* A C U X
* \ /
* N Q
*
* The children of the middle tree are grafted onto the last and first
* nodes of the side trees, which become the children of the root.
*
* Does *not* maintain client properties. See SplayAssembleRev.
*
* See <design/splay/#impl.assemble>.
*/
static void SplayAssembleDown(SplayTree splay, SplayState state)
{
AVERT(SplayTree, splay);
AVER(state->middle != TreeEMPTY);
AVER(!SplayHasUpdate(splay));
if (state->left != TreeEMPTY) {
AVER_CRITICAL(state->leftLast != TreeEMPTY);
TreeSetRight(state->leftLast, TreeLeft(state->middle));
TreeSetLeft(state->middle, state->left);
}
if (state->right != TreeEMPTY) {
AVER_CRITICAL(state->rightFirst != TreeEMPTY);
TreeSetLeft(state->rightFirst, TreeRight(state->middle));
TreeSetRight(state->middle, state->right);
}
}
/* SplayZigRev -- move to left child, prepending to reversed right tree
*
* Same as SplayZig, except that the left spine of the right tree is
* pointer-reversed, so that its left children point at their parents
* instead of their children. This is fixed up in SplayAssembleRev.
*/
static Tree SplayZigRev(Tree middle, Tree *rightFirstIO)
{
Tree child;
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(rightFirstIO != NULL);
AVERT_CRITICAL(Tree, *rightFirstIO);
child = TreeLeft(middle);
TreeSetLeft(middle, *rightFirstIO);
*rightFirstIO = middle;
return child;
}
/* SplayZigZigRev -- move to left child, rotating onto reversed right tree
*
* Same as SplayZigZig, except that the right tree is pointer reversed
* (see SplayZigRev)
*/
static Tree SplayZigZigRev(Tree middle, Tree *rightFirstIO)
{
Tree child;
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(rightFirstIO != NULL);
AVERT_CRITICAL(Tree, *rightFirstIO);
child = TreeLeft(middle);
TreeSetLeft(middle, TreeLeft(*rightFirstIO));
TreeSetLeft(*rightFirstIO, TreeRight(middle));
TreeSetRight(middle, *rightFirstIO);
*rightFirstIO = middle;
return child;
}
/* SplayZagRev -- mirror image of SplayZigRev */
static Tree SplayZagRev(Tree middle, Tree *leftLastIO)
{
Tree child;
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(leftLastIO != NULL);
AVERT_CRITICAL(Tree, *leftLastIO);
child = TreeRight(middle);
TreeSetRight(middle, *leftLastIO);
*leftLastIO = middle;
return child;
}
/* SplayZagZagRev -- mirror image of SplayZigZigRev */
static Tree SplayZagZagRev(Tree middle, Tree *leftLastIO)
{
Tree child;
AVERT_CRITICAL(Tree, middle);
AVER_CRITICAL(leftLastIO != NULL);
AVERT_CRITICAL(Tree, *leftLastIO);
child = TreeRight(middle);
TreeSetRight(middle, TreeRight(*leftLastIO));
TreeSetRight(*leftLastIO, TreeLeft(middle));
TreeSetLeft(middle, *leftLastIO);
*leftLastIO = middle;
return child;
}
/* SplaySplitRev -- divide the tree around a key
*
* This is the same as SplaySplit, except that:
* - the left and right trees are pointer reversed on their spines
* - client properties for rotated nodes (not on the spines) are
* updated
*/
static Compare SplaySplitRev(SplayStateStruct *stateReturn,
SplayTree splay, TreeKey key,
TreeCompareFunction compare)
{
Tree middle, leftLast, rightFirst;
Compare cmp;
AVERT_CRITICAL(SplayTree, splay);
AVER_CRITICAL(FUNCHECK(compare));
AVER_CRITICAL(!SplayTreeIsEmpty(splay));
leftLast = TreeEMPTY;
rightFirst = TreeEMPTY;
middle = SplayTreeRoot(splay);
for (;;) {
cmp = compare(middle, key);
switch(cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
goto stop;
case CompareLESS:
if (!TreeHasLeft(middle))
goto stop;
middle = SplayZigRev(middle, &rightFirst);
cmp = compare(middle, key);
switch(cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
goto stop;
case CompareLESS:
if (!TreeHasLeft(middle))
goto stop;
middle = SplayZigZigRev(middle, &rightFirst);
splay->updateNode(splay, TreeRight(rightFirst));
break;
case CompareGREATER:
if (!TreeHasRight(middle))
goto stop;
middle = SplayZagRev(middle, &leftLast);
break;
}
break;
case CompareGREATER:
if (!TreeHasRight(middle))
goto stop;
middle = SplayZagRev(middle, &leftLast);
cmp = compare(middle, key);
switch(cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
goto stop;
case CompareGREATER:
if (!TreeHasRight(middle))
goto stop;
middle = SplayZagZagRev(middle, &leftLast);
splay->updateNode(splay, TreeLeft(leftLast));
break;
case CompareLESS:
if (!TreeHasLeft(middle))
goto stop;
middle = SplayZigRev(middle, &rightFirst);
break;
}
break;
}
}
stop:
stateReturn->middle = middle;
stateReturn->leftLast = leftLast;
stateReturn->rightFirst = rightFirst;
return cmp;
}
/* SplayUpdateLeftSpine -- undo pointer reversal, updating client property */
static Tree SplayUpdateLeftSpine(SplayTree splay, Tree node, Tree child)
{
AVERT_CRITICAL(SplayTree, splay);
AVERT_CRITICAL(Tree, node);
AVERT_CRITICAL(Tree, child);
while(node != TreeEMPTY) {
Tree parent = TreeLeft(node);
TreeSetLeft(node, child); /* un-reverse pointer */
splay->updateNode(splay, node);
child = node;
node = parent;
}
return child;
}
/* SplayUpdateRightSpine -- mirror of SplayUpdateLeftSpine */
static Tree SplayUpdateRightSpine(SplayTree splay, Tree node, Tree child)
{
AVERT_CRITICAL(SplayTree, splay);
AVERT_CRITICAL(Tree, node);
AVERT_CRITICAL(Tree, child);
while (node != TreeEMPTY) {
Tree parent = TreeRight(node);
TreeSetRight(node, child); /* un-reverse pointer */
splay->updateNode(splay, node);
child = node;
node = parent;
}
return child;
}
/* SplayAssembleRev -- pointer reversed SplayAssemble
*
* Does the same job as SplayAssemble, but operates on pointer-reversed
* left and right trees, updating client properties. When we reach
* this function, the nodes on the spines of the left and right trees
* will have out of date client properties because their children have
* been changed by SplaySplitRev.
*/
static void SplayAssembleRev(SplayTree splay, SplayState state)
{
Tree left, right;
AVERT_CRITICAL(SplayTree, splay);
AVER_CRITICAL(state->middle != TreeEMPTY);
left = TreeLeft(state->middle);
left = SplayUpdateRightSpine(splay, state->leftLast, left);
TreeSetLeft(state->middle, left);
right = TreeRight(state->middle);
right = SplayUpdateLeftSpine(splay, state->rightFirst, right);
TreeSetRight(state->middle, right);
splay->updateNode(splay, state->middle);
}
/* SplaySplit -- call SplaySplitDown or SplaySplitRev as appropriate */
static Compare SplaySplit(SplayStateStruct *stateReturn,
SplayTree splay, TreeKey key,
TreeCompareFunction compare)
{
if (SplayHasUpdate(splay))
return SplaySplitRev(stateReturn, splay, key, compare);
else
return SplaySplitDown(stateReturn, splay, key, compare);
}
/* SplayAssemble -- call SplayAssembleDown or SplayAssembleRev as appropriate */
static void SplayAssemble(SplayTree splay, SplayState state)
{
if (SplayHasUpdate(splay))
SplayAssembleRev(splay, state);
else
SplayAssembleDown(splay, state);
}
/* SplaySplay -- splay the tree around a given key
*
* Uses SplaySplitRev/SplayAssembleRev or SplaySplitDown/SplayAssembleDown
* as appropriate, but also catches the empty tree case and shortcuts
* the common case where the wanted node is already at the root (due
* to a previous splay). The latter shortcut has a significant effect
* on run time.
*
* If a matching node is found, it is splayed to the root and the function
* returns CompareEQUAL, or if the tree is empty, will also return
* CompareEQUAL. Otherwise, CompareGREATER or CompareLESS is returned
* meaning either the key is greater or less than the new root. In this
* case the new root is the last node visited which is either the closest
* node left or the closest node right of the key.
*
* See <design/splay/#impl.splay>.
*/
static Compare SplaySplay(SplayTree splay, TreeKey key,
TreeCompareFunction compare)
{
Compare cmp;
SplayStateStruct stateStruct;
#ifdef SPLAY_DEBUG
Count count = SplayDebugCount(splay);
#endif
/* Short-circuit common cases. Splay trees often bring recently
acccessed nodes to the root. */
if (SplayTreeIsEmpty(splay) ||
compare(SplayTreeRoot(splay), key) == CompareEQUAL)
return CompareEQUAL;
if (SplayHasUpdate(splay)) {
cmp = SplaySplitRev(&stateStruct, splay, key, compare);
SplayAssembleRev(splay, &stateStruct);
} else {
cmp = SplaySplitDown(&stateStruct, splay, key, compare);
SplayAssembleDown(splay, &stateStruct);
}
SplayTreeSetRoot(splay, stateStruct.middle);
#ifdef SPLAY_DEBUG
AVER(count == SplayDebugCount(splay));
#endif
return cmp;
}
/* SplayTreeInsert -- insert a node into a splay tree
*
*
* This function is used to insert a node into the tree. Splays the
* tree at the node's key. If an attempt is made to insert a node that
* compares ``CompareEQUAL`` to an existing node in the tree, then
* ``FALSE`` will be returned and the node will not be inserted.
*
* NOTE: It would be possible to use split here, then assemble around
* the new node, leaving the neighbour where it was, but it's probably
* a good thing for key neighbours to be tree neighbours.
*/
Bool SplayTreeInsert(SplayTree splay, Tree node) {
Tree neighbour;
AVERT(SplayTree, splay);
AVERT(Tree, node);
AVER(TreeLeft(node) == TreeEMPTY);
AVER(TreeRight(node) == TreeEMPTY);
if (SplayTreeIsEmpty(splay)) {
SplayTreeSetRoot(splay, node);
return TRUE;
}
switch (SplaySplay(splay, splay->nodeKey(node), splay->compare)) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL: /* duplicate node */
return FALSE;
case CompareGREATER: /* left neighbour is at root */
neighbour = SplayTreeRoot(splay);
SplayTreeSetRoot(splay, node);
TreeSetRight(node, TreeRight(neighbour));
TreeSetLeft(node, neighbour);
TreeSetRight(neighbour, TreeEMPTY);
break;
case CompareLESS: /* right neighbour is at root */
neighbour = SplayTreeRoot(splay);
SplayTreeSetRoot(splay, node);
TreeSetLeft(node, TreeLeft(neighbour));
TreeSetRight(node, neighbour);
TreeSetLeft(neighbour, TreeEMPTY);
break;
}
splay->updateNode(splay, neighbour);
splay->updateNode(splay, node);
return TRUE;
}
/* SplayTreeDelete -- delete a node from a splay tree
*
* Delete a node from the tree. If the tree does not contain the given
* node then ``FALSE`` will be returned. The client must not pass a
* node whose key compares equal to a different node in the tree.
*
* The function first splays the tree at the given key.
*
* TODO: If the node has zero or one children, then the replacement
* would be the leftLast or rightFirst after a SplaySplit, and would
* avoid a search for a replacement in more cases.
*/
Bool SplayTreeDelete(SplayTree splay, Tree node) {
Tree leftLast;
Compare cmp;
AVERT(SplayTree, splay);
AVERT(Tree, node);
if (SplayTreeIsEmpty(splay))
return FALSE;
cmp = SplaySplay(splay, splay->nodeKey(node), splay->compare);
AVER(cmp != CompareEQUAL || SplayTreeRoot(splay) == node);
if (cmp != CompareEQUAL) {
return FALSE;
} else if (!TreeHasLeft(node)) {
SplayTreeSetRoot(splay, TreeRight(node));
TreeClearRight(node);
} else if (!TreeHasRight(node)) {
SplayTreeSetRoot(splay, TreeLeft(node));
TreeClearLeft(node);
} else {
Tree rightHalf = TreeRight(node);
TreeClearRight(node);
SplayTreeSetRoot(splay, TreeLeft(node));
TreeClearLeft(node);
(void)SplaySplay(splay, NULL, compareGreater);
leftLast = SplayTreeRoot(splay);
AVER(leftLast != TreeEMPTY);
AVER(!TreeHasRight(leftLast));
TreeSetRight(leftLast, rightHalf);
splay->updateNode(splay, leftLast);
}
TreeFinish(node);
return TRUE;
}
/* SplayTreeFind -- search for a node in a splay tree matching a key
*
* Search the tree for a node that compares ``CompareEQUAL`` to a key
* Splays the tree at the key. Returns ``FALSE`` if there is no such
* node in the tree, otherwise ``*nodeReturn`` will be set to the node.
*/
Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) {
AVERT(SplayTree, splay);
AVER(nodeReturn != NULL);
if (SplayTreeIsEmpty(splay))
return FALSE;
if (SplaySplay(splay, key, splay->compare) != CompareEQUAL)
return FALSE;
*nodeReturn = SplayTreeRoot(splay);
return TRUE;
}
/* SplayTreeSuccessor -- splays a tree at the root's successor
*
* Must not be called on en empty tree. Successor need not exist,
* in which case TreeEMPTY is returned, and the tree is unchanged.
*/
static Tree SplayTreeSuccessor(SplayTree splay) {
Tree oldRoot, newRoot;
AVERT(SplayTree, splay);
AVER(!SplayTreeIsEmpty(splay));
oldRoot = SplayTreeRoot(splay);
if (!TreeHasRight(oldRoot))
return TreeEMPTY; /* No successor */
/* temporarily chop off the left half-tree, inclusive of root */
SplayTreeSetRoot(splay, TreeRight(oldRoot));
TreeSetRight(oldRoot, TreeEMPTY);
(void)SplaySplay(splay, NULL, compareLess);
newRoot = SplayTreeRoot(splay);
AVER(newRoot != TreeEMPTY);
AVER(TreeLeft(newRoot) == TreeEMPTY);
TreeSetLeft(newRoot, oldRoot);
splay->updateNode(splay, oldRoot);
splay->updateNode(splay, newRoot);
return newRoot;
}
/* SplayTreeNeighbours
*
* Search for the two nodes in a splay tree neighbouring a key.
* Splays the tree at the key. ``*leftReturn`` will be the neighbour
* which compares less than the key if such a neighbour exists; otherwise
* it will be ``TreeEMPTY``. ``*rightReturn`` will be the neighbour which
* compares greater than the key if such a neighbour exists; otherwise
* it will be ``TreeEMPTY``. The function returns ``FALSE`` if any node
* in the tree compares ``CompareEQUAL`` with the given key.
*
* TODO: Change to SplayTreeCoalesce that takes a function that can
* direct the deletion of one of the neighbours, since this is a
* good moment to do it, avoiding another search and splay.
*
* This implementation uses SplaySplit to find both neighbours in a
* single splay (see design.mps.splay.impl.neighbours).
*/
Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn,
SplayTree splay, TreeKey key)
{
SplayStateStruct stateStruct;
Bool found;
Compare cmp;
#ifdef SPLAY_DEBUG
Count count = SplayDebugCount(splay);
#endif
AVERT(SplayTree, splay);
AVER(leftReturn != NULL);
AVER(rightReturn != NULL);
if (SplayTreeIsEmpty(splay)) {
*leftReturn = *rightReturn = TreeEMPTY;
return TRUE;
}
cmp = SplaySplit(&stateStruct, splay, key, splay->compare);
switch (cmp) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareEQUAL:
found = FALSE;
break;
case CompareLESS:
AVER(!TreeHasLeft(stateStruct.middle));
*rightReturn = stateStruct.middle;
*leftReturn = stateStruct.leftLast;
found = TRUE;
break;
case CompareGREATER:
AVER(!TreeHasRight(stateStruct.middle));
*leftReturn = stateStruct.middle;
*rightReturn = stateStruct.rightFirst;
found = TRUE;
break;
}
SplayAssemble(splay, &stateStruct);
SplayTreeSetRoot(splay, stateStruct.middle);
#ifdef SPLAY_DEBUG
AVER(count == SplayDebugCount(splay));
#endif
return found;
}
/* SplayTreeFirst, SplayTreeNext -- iterators
*
* SplayTreeFirst returns TreeEMPTY if the tree is empty. Otherwise,
* it splays the tree to the first node, and returns the new root.
*
* SplayTreeNext takes a tree and splays it to the successor of a key
* and returns the new root. Returns TreeEMPTY is there are no
* successors.
*
* SplayTreeFirst and SplayTreeNext do not require the tree to remain
* unmodified.
*
* IMPORTANT: Iterating over the tree using these functions will leave
* the tree totally unbalanced, throwing away optimisations of the tree
* shape caused by previous splays. Consider using TreeTraverse instead.
*/
Tree SplayTreeFirst(SplayTree splay) {
Tree node;
AVERT(SplayTree, splay);
if (SplayTreeIsEmpty(splay))
return TreeEMPTY;
(void)SplaySplay(splay, NULL, compareLess);
node = SplayTreeRoot(splay);
AVER(node != TreeEMPTY);
AVER(TreeLeft(node) == TreeEMPTY);
return node;
}
Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) {
AVERT(SplayTree, splay);
if (SplayTreeIsEmpty(splay))
return TreeEMPTY;
/* Make old node the root. Probably already is. We don't mind if the
node has been deleted, or replaced by a node with the same key. */
switch (SplaySplay(splay, oldKey, splay->compare)) {
default:
NOTREACHED;
/* defensive fall-through */
case CompareLESS:
return SplayTreeRoot(splay);
case CompareGREATER:
case CompareEQUAL:
return SplayTreeSuccessor(splay);
}
}
/* SplayNodeDescribe -- Describe a node in the splay tree
*
* Note that this breaks the restriction of .note.stack.
* This is alright as the function is debug only.
*/
static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream,
TreeDescribeFunction nodeDescribe)
{
Res res;
if (!TreeCheck(node))
return ResFAIL;
if (stream == NULL)
return ResFAIL;
if (nodeDescribe == NULL)
return ResFAIL;
res = WriteF(stream, 0, "( ", NULL);
if (res != ResOK)
return res;
if (TreeHasLeft(node)) {
res = SplayNodeDescribe(TreeLeft(node), stream, nodeDescribe);
if (res != ResOK)
return res;
res = WriteF(stream, 0, " / ", NULL);
if (res != ResOK)
return res;
}
res = (*nodeDescribe)(node, stream);
if (res != ResOK)
return res;
if (TreeHasRight(node)) {
res = WriteF(stream, 0, " \\ ", NULL);
if (res != ResOK)
return res;
res = SplayNodeDescribe(TreeRight(node), stream, nodeDescribe);
if (res != ResOK)
return res;
}
res = WriteF(stream, 0, " )", NULL);
if (res != ResOK)
return res;
return ResOK;
}
/* SplayFindFirstCompare, SplayFindLastCompare -- filtering searches
*
* These are used by SplayFindFirst and SplayFindLast as comparison
* functions to SplaySplit in order to home in on a node using client
* tests. The way to understand them is that the comparison values
* they return have nothing to do with the tree ordering, but are instead
* like commands that tell SplaySplit whether to "go left", "stop", or
* "go right" according to the results of testNode and testTree.
* Since splaying preserves the order of the tree, any tests can be
* applied to navigate to a destination.
*
* In the MPS these are mainly used by the CBS to search for memory
* blocks above a certain size. Their performance is quite critical.
*/
typedef struct SplayFindClosureStruct {
SplayTestNodeFunction testNode;
SplayTestTreeFunction testTree;
void *testClosure;
SplayTree splay;
Bool found;
} SplayFindClosureStruct, *SplayFindClosure;
static Compare SplayFindFirstCompare(Tree node, TreeKey key)
{
SplayFindClosure my;
SplayTestNodeFunction testNode;
SplayTestTreeFunction testTree;
void *testClosure;
SplayTree splay;
AVERT(Tree, node);
AVER(key != NULL);
/* Lift closure values into variables so that they aren't aliased by
calls to the test functions. */
my = (SplayFindClosure)key;
testClosure = my->testClosure;
testNode = my->testNode;
testTree = my->testTree;
splay = my->splay;
if (TreeHasLeft(node) &&
(*testTree)(splay, TreeLeft(node), testClosure)) {
return CompareLESS;
} else if ((*testNode)(splay, node, testClosure)) {
my->found = TRUE;
return CompareEQUAL;
} else {
/* If there's a right subtree but it doesn't satisfy the tree test
then we want to terminate the splay right now. SplaySplay will
return TRUE, so the caller must check closure->found to find out
whether the result node actually satisfies testNode. */
if (TreeHasRight(node) &&
!(*testTree)(splay, TreeRight(node), testClosure)) {
my->found = FALSE;
return CompareEQUAL;
}
return CompareGREATER;
}
}
static Compare SplayFindLastCompare(Tree node, TreeKey key)
{
SplayFindClosure my;
SplayTestNodeFunction testNode;
SplayTestTreeFunction testTree;
void *testClosure;
SplayTree splay;
AVERT(Tree, node);
AVER(key != NULL);
/* Lift closure values into variables so that they aren't aliased by
calls to the test functions. */
my = (SplayFindClosure)key;
testClosure = my->testClosure;
testNode = my->testNode;
testTree = my->testTree;
splay = my->splay;
if (TreeHasRight(node) &&
(*testTree)(splay, TreeRight(node), testClosure)) {
return CompareGREATER;
} else if ((*testNode)(splay, node, testClosure)) {
my->found = TRUE;
return CompareEQUAL;
} else {
/* See SplayFindFirstCompare. */
if (TreeHasLeft(node) &&
!(*testTree)(splay, TreeLeft(node), testClosure)) {
my->found = FALSE;
return CompareEQUAL;
}
return CompareLESS;
}
}
/* SplayFindFirst -- Find first node that satisfies client property
*
* This function finds the first node (in address order) in the given
* tree that satisfies some property defined by the client. The
* property is such that the client can detect, given a sub-tree,
* whether that sub-tree contains any nodes satisfying the property.
* If there is no satisfactory node, ``FALSE`` is returned, otherwise
* ``*nodeReturn`` is set to the node.
*
* The given callbacks testNode and testTree detect this property in
* a single node or a sub-tree rooted at a node, and both receive an
* arbitrary closure.
*
* TODO: This repeatedly splays failed matches to the root and rotates
* them, so it could have quite an unbalancing effect if size is small.
* Think about a better search, perhaps using TreeTraverse?
*/
Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay,
SplayTestNodeFunction testNode,
SplayTestTreeFunction testTree,
void *testClosure)
{
SplayFindClosureStruct closureStruct;
Bool found;
AVER(nodeReturn != NULL);
AVERT(SplayTree, splay);
AVER(FUNCHECK(testNode));
AVER(FUNCHECK(testTree));
if (SplayTreeIsEmpty(splay) ||
!testTree(splay, SplayTreeRoot(splay), testClosure))
return FALSE; /* no suitable nodes in tree */
closureStruct.testClosure = testClosure;
closureStruct.testNode = testNode;
closureStruct.testTree = testTree;
closureStruct.splay = splay;
closureStruct.found = FALSE;
found = SplaySplay(splay, &closureStruct,
SplayFindFirstCompare) == CompareEQUAL &&
closureStruct.found;
while (!found) {
Tree oldRoot, newRoot;
/* FIXME: Rename to "seen" and "not yet seen" or something. */
oldRoot = SplayTreeRoot(splay);
newRoot = TreeRight(oldRoot);
if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, testClosure))
return FALSE; /* no suitable nodes in the rest of the tree */
/* Temporarily chop off the left half-tree, inclusive of root,
so that the search excludes any nodes we've seen already. */
SplayTreeSetRoot(splay, newRoot);
TreeSetRight(oldRoot, TreeEMPTY);
found = SplaySplay(splay, &closureStruct,
SplayFindFirstCompare) == CompareEQUAL &&
closureStruct.found;
/* Restore the left tree, then rotate left so that the node we
just splayed is at the root. Update both. */
newRoot = SplayTreeRoot(splay);
TreeSetRight(oldRoot, newRoot);
SplayTreeSetRoot(splay, oldRoot);
TreeRotateLeft(&splay->root);
splay->updateNode(splay, oldRoot);
splay->updateNode(splay, newRoot);
}
*nodeReturn = SplayTreeRoot(splay);
return TRUE;
}
/* SplayFindLast -- As SplayFindFirst but in reverse address order */
Bool SplayFindLast(Tree *nodeReturn, SplayTree splay,
SplayTestNodeFunction testNode,
SplayTestTreeFunction testTree,
void *testClosure)
{
SplayFindClosureStruct closureStruct;
Bool found;
AVER(nodeReturn != NULL);
AVERT(SplayTree, splay);
AVER(FUNCHECK(testNode));
AVER(FUNCHECK(testTree));
if (SplayTreeIsEmpty(splay) ||
!testTree(splay, SplayTreeRoot(splay), testClosure))
return FALSE; /* no suitable nodes in tree */
closureStruct.testClosure = testClosure;
closureStruct.testNode = testNode;
closureStruct.testTree = testTree;
closureStruct.splay = splay;
found = SplaySplay(splay, &closureStruct,
SplayFindLastCompare) == CompareEQUAL &&
closureStruct.found;
while (!found) {
Tree oldRoot, newRoot;
oldRoot = SplayTreeRoot(splay);
newRoot = TreeLeft(oldRoot);
if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, testClosure))
return FALSE; /* no suitable nodes in the rest of the tree */
/* Temporarily chop off the right half-tree, inclusive of root,
so that the search excludes any nodes we've seen already. */
SplayTreeSetRoot(splay, newRoot);
TreeSetLeft(oldRoot, TreeEMPTY);
found = SplaySplay(splay, &closureStruct,
SplayFindLastCompare) == CompareEQUAL &&
closureStruct.found;
/* Restore the right tree, then rotate right so that the node we
just splayed is at the root. Update both. */
newRoot = SplayTreeRoot(splay);
TreeSetLeft(oldRoot, newRoot);
SplayTreeSetRoot(splay, oldRoot);
TreeRotateRight(&splay->root);
splay->updateNode(splay, oldRoot);
splay->updateNode(splay, newRoot);
}
*nodeReturn = SplayTreeRoot(splay);
return TRUE;
}
/* SplayNodeRefresh -- updates the client property that has changed at a node
*
* This function undertakes to call the client updateNode callback for each
* node affected by the change in properties at the given node (which has
* the given key) in an appropriate order.
*
* The function fullfils its job by first splaying at the given node, and
* updating the single node. In the MPS it is used by the CBS during
* coalescing, when the node is likely to be at (or adjacent to) the top
* of the tree anyway.
*/
void SplayNodeRefresh(SplayTree splay, Tree node)
{
Compare cmp;
AVERT(SplayTree, splay);
AVERT(Tree, node);
AVER(!SplayTreeIsEmpty(splay)); /* must contain node, at least */
AVER(SplayHasUpdate(splay)); /* otherwise, why call? */
cmp = SplaySplay(splay, splay->nodeKey(node), splay->compare);
AVER(cmp == CompareEQUAL);
AVER(SplayTreeRoot(splay) == node);
splay->updateNode(splay, node);
}
/* SplayNodeInit -- initialize client property without splaying */
void SplayNodeInit(SplayTree splay, Tree node)
{
AVERT(SplayTree, splay);
AVERT(Tree, node);
AVER(!TreeHasLeft(node)); /* otherwise, call SplayNodeRefresh */
AVER(!TreeHasRight(node)); /* otherwise, call SplayNodeRefresh */
AVER(SplayHasUpdate(splay)); /* otherwise, why call? */
splay->updateNode(splay, node);
}
/* SplayTreeDescribe -- Describe a splay tree
*
* See <design/splay/#function.splay.tree.describe>.
*/
Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, Count depth,
TreeDescribeFunction nodeDescribe)
{
Res res;
if (!TESTT(SplayTree, splay))
return ResFAIL;
if (stream == NULL)
return ResFAIL;
if (nodeDescribe == NULL)
return ResFAIL;
res = WriteF(stream, depth,
"Splay $P {\n", (WriteFP)splay,
" compare $F\n", (WriteFF)splay->compare,
" nodeKey $F\n", (WriteFF)splay->nodeKey,
" updateNode $F\n", (WriteFF)splay->updateNode,
NULL);
if (res != ResOK)
return res;
if (SplayTreeRoot(splay) != TreeEMPTY) {
res = WriteF(stream, depth, " tree ", NULL);
if (res != ResOK)
return res;
res = SplayNodeDescribe(SplayTreeRoot(splay), stream, nodeDescribe);
if (res != ResOK)
return res;
}
res = WriteF(stream, depth, "\n} Splay $P\n", (WriteFP)splay, NULL);
return res;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2016 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* 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, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND 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.
*/