diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 84913dd9eab..096eaa075a5 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -23,7 +23,7 @@ SRCID(cbs, "$Id$"); typedef struct CBSBlockStruct *CBSBlock; typedef struct CBSBlockStruct { - SplayNodeStruct splayNode; + TreeStruct node; Addr base; Addr limit; Size maxSize; /* accurate maximum block size of sub-tree */ @@ -34,11 +34,11 @@ typedef struct CBSBlockStruct { #define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit) -#define cbsOfSplayTree(tree) PARENT(CBSStruct, splayTree, (tree)) -#define cbsBlockOfSplayNode(node) PARENT(CBSBlockStruct, splayNode, (node)) -#define splayTreeOfCBS(tree) (&((cbs)->splayTree)) -#define splayNodeOfCBSBlock(block) (&((block)->splayNode)) -#define keyOfCBSBlock(block) ((void *)&((block)->base)) +#define cbsOfTree(_tree) TREE_ELT(CBS, tree, _tree) +#define cbsBlockOfNode(_node) PARENT(CBSBlockStruct, node, _node) +#define treeOfCBS(cbs) (&((cbs)->tree)) +#define nodeOfCBSBlock(block) (&((block)->node)) +#define keyOfCBSBlock(block) (&((block)->base)) /* cbsEnter, cbsLeave -- Avoid re-entrance @@ -73,8 +73,8 @@ Bool CBSCheck(CBS cbs) /* See .enter-leave.simple. */ CHECKS(CBS, cbs); CHECKL(cbs != NULL); - CHECKL(SplayTreeCheck(splayTreeOfCBS(cbs))); - /* nothing to check about splayTreeSize */ + CHECKD(SplayTree, treeOfCBS(cbs)); + /* nothing to check about treeSize */ CHECKD(Pool, cbs->blockPool); CHECKL(BoolCheck(cbs->fastFind)); CHECKL(BoolCheck(cbs->inCBS)); @@ -89,7 +89,7 @@ static Bool CBSBlockCheck(CBSBlock block) /* See .enter-leave.simple. */ UNUSED(block); /* Required because there is no signature */ CHECKL(block != NULL); - CHECKL(SplayNodeCheck(splayNodeOfCBSBlock(block))); + CHECKL(TreeCheck(nodeOfCBSBlock(block))); /* If the block is in the middle of being deleted, */ /* the pointers will be equal. */ @@ -99,24 +99,21 @@ static Bool CBSBlockCheck(CBSBlock block) } -/* cbsSplayCompare -- Compare key to [base,limit) +/* cbsCompare -- Compare key to [base,limit) * * See */ -static Compare cbsSplayCompare(void *key, SplayNode node) +static Compare cbsCompare(Tree node, TreeKey key) { Addr base1, base2, limit2; CBSBlock cbsBlock; - /* NULL key compares less than everything. */ - if (key == NULL) - return CompareLESS; - AVER(node != NULL); + AVER(node != TreeEMPTY); base1 = *(Addr *)key; - cbsBlock = cbsBlockOfSplayNode(node); + cbsBlock = cbsBlockOfNode(node); base2 = cbsBlock->base; limit2 = cbsBlock->limit; @@ -128,37 +125,42 @@ static Compare cbsSplayCompare(void *key, SplayNode node) return CompareEQUAL; } +static TreeKey cbsKey(Tree node) +{ + return keyOfCBSBlock(cbsBlockOfNode(node)); +} + /* cbsTestNode, cbsTestTree -- test for nodes larger than the S parameter */ -static Bool cbsTestNode(SplayTree tree, SplayNode node, +static Bool cbsTestNode(SplayTree tree, Tree node, void *closureP, Size size) { CBSBlock block; AVERT(SplayTree, tree); - AVERT(SplayNode, node); + AVERT(Tree, node); AVER(closureP == NULL); AVER(size > 0); - AVER(cbsOfSplayTree(tree)->fastFind); + AVER(cbsOfTree(tree)->fastFind); - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfNode(node); return CBSBlockSize(block) >= size; } -static Bool cbsTestTree(SplayTree tree, SplayNode node, +static Bool cbsTestTree(SplayTree tree, Tree node, void *closureP, Size size) { CBSBlock block; AVERT(SplayTree, tree); - AVERT(SplayNode, node); + AVERT(Tree, node); AVER(closureP == NULL); AVER(size > 0); - AVER(cbsOfSplayTree(tree)->fastFind); + AVER(cbsOfTree(tree)->fastFind); - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfNode(node); return block->maxSize >= size; } @@ -166,31 +168,26 @@ static Bool cbsTestTree(SplayTree tree, SplayNode node, /* cbsUpdateNode -- update size info after restructuring */ -static void cbsUpdateNode(SplayTree tree, SplayNode node, - SplayNode leftChild, SplayNode rightChild) +static void cbsUpdateNode(SplayTree tree, Tree node) { Size maxSize; CBSBlock block; AVERT(SplayTree, tree); - AVERT(SplayNode, node); - if (leftChild != NULL) - AVERT(SplayNode, leftChild); - if (rightChild != NULL) - AVERT(SplayNode, rightChild); - AVER(cbsOfSplayTree(tree)->fastFind); + AVERT(Tree, node); + AVER(cbsOfTree(tree)->fastFind); - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfNode(node); maxSize = CBSBlockSize(block); - if (leftChild != NULL) { - Size size = cbsBlockOfSplayNode(leftChild)->maxSize; + if (TreeHasLeft(node)) { + Size size = cbsBlockOfNode(TreeLeft(node))->maxSize; if (size > maxSize) maxSize = size; } - if (rightChild != NULL) { - Size size = cbsBlockOfSplayNode(rightChild)->maxSize; + if (TreeHasRight(node)) { + Size size = cbsBlockOfNode(TreeRight(node))->maxSize; if (size > maxSize) maxSize = size; } @@ -218,8 +215,10 @@ Res CBSInit(Arena arena, CBS cbs, void *owner, Align alignment, if (ArgPick(&arg, args, MPS_KEY_CBS_EXTEND_BY)) extendBy = arg.val.size; - SplayTreeInit(splayTreeOfCBS(cbs), &cbsSplayCompare, - fastFind ? &cbsUpdateNode : NULL); + SplayTreeInit(treeOfCBS(cbs), + cbsCompare, + cbsKey, + fastFind ? cbsUpdateNode : SplayTrivUpdate); MPS_ARGS_BEGIN(pcArgs) { MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSBlockStruct)); MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, extendBy); @@ -227,13 +226,13 @@ Res CBSInit(Arena arena, CBS cbs, void *owner, Align alignment, } MPS_ARGS_END(pcArgs); if (res != ResOK) return res; - cbs->splayTreeSize = 0; + cbs->treeSize = 0; cbs->fastFind = fastFind; cbs->alignment = alignment; cbs->inCBS = TRUE; - METER_INIT(cbs->splaySearch, "size of splay tree", (void *)cbs); + METER_INIT(cbs->treeSearch, "size of tree", (void *)cbs); cbs->sig = CBSSig; @@ -254,11 +253,11 @@ void CBSFinish(CBS cbs) AVERT(CBS, cbs); cbsEnter(cbs); - METER_EMIT(&cbs->splaySearch); + METER_EMIT(&cbs->treeSearch); cbs->sig = SigInvalid; - SplayTreeFinish(splayTreeOfCBS(cbs)); + SplayTreeFinish(treeOfCBS(cbs)); PoolDestroy(cbs->blockPool); } @@ -272,16 +271,15 @@ void CBSFinish(CBS cbs) static void cbsBlockDelete(CBS cbs, CBSBlock block) { - Res res; + Bool b; AVERT(CBS, cbs); AVERT(CBSBlock, block); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeDelete(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); - AVER(res == ResOK); /* Must be possible to delete node */ - STATISTIC(--cbs->splayTreeSize); + METER_ACC(cbs->treeSearch, cbs->treeSize); + b = SplayTreeDelete(treeOfCBS(cbs), nodeOfCBSBlock(block)); + AVER(b); /* expect block to be in the tree */ + STATISTIC(--cbs->treeSize); /* make invalid */ block->limit = block->base; @@ -302,8 +300,7 @@ static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize) AVER(oldSize > newSize); if (cbs->fastFind) { - SplayNodeRefresh(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); + SplayNodeRefresh(treeOfCBS(cbs), nodeOfCBSBlock(block)); AVER(CBSBlockSize(block) <= block->maxSize); } } @@ -319,14 +316,13 @@ static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize) AVER(oldSize < newSize); if (cbs->fastFind) { - SplayNodeRefresh(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); + SplayNodeRefresh(treeOfCBS(cbs), nodeOfCBSBlock(block)); AVER(CBSBlockSize(block) <= block->maxSize); } } /* cbsBlockAlloc -- allocate a new block and set its base and limit, - but do not insert it into the splay tree yet */ + but do not insert it into the tree yet */ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) { @@ -344,7 +340,7 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) goto failPoolAlloc; block = (CBSBlock)p; - SplayNodeInit(splayNodeOfCBSBlock(block)); + TreeInit(nodeOfCBSBlock(block)); block->base = RangeBase(range); block->limit = RangeLimit(range); block->maxSize = CBSBlockSize(block); @@ -358,30 +354,30 @@ failPoolAlloc: return res; } -/* cbsBlockInsert -- insert a block into the splay tree */ +/* cbsBlockInsert -- insert a block into the tree */ static void cbsBlockInsert(CBS cbs, CBSBlock block) { - Res res; + Bool b; AVERT(CBS, cbs); AVERT(CBSBlock, block); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeInsert(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); - AVER(res == ResOK); - STATISTIC(++cbs->splayTreeSize); + METER_ACC(cbs->treeSearch, cbs->treeSize); + b = SplayTreeInsert(treeOfCBS(cbs), nodeOfCBSBlock(block)); + AVER(b); + STATISTIC(++cbs->treeSize); } -/* cbsInsertIntoTree -- Insert a range into the splay tree */ +/* cbsInsertIntoTree -- Insert a range into the tree */ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) { + Bool b; Res res; Addr base, limit, newBase, newLimit; - SplayNode leftSplay, rightSplay; + Tree leftSplay, rightSplay; CBSBlock leftCBS, rightCBS; Bool leftMerge, rightMerge; Size oldSize; @@ -394,35 +390,36 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) base = RangeBase(range); limit = RangeLimit(range); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeNeighbours(&leftSplay, &rightSplay, - splayTreeOfCBS(cbs), (void *)&base); - if (res != ResOK) + METER_ACC(cbs->treeSearch, cbs->treeSize); + b = SplayTreeNeighbours(&leftSplay, &rightSplay, treeOfCBS(cbs), &base); + if (!b) { + res = ResFAIL; goto fail; + } /* The two cases below are not quite symmetrical, because base was * passed into the call to SplayTreeNeighbours(), but limit was not. * So we know that if there is a left neighbour, then leftCBS->limit - * <= base (this is ensured by cbsSplayCompare, which is the - * comparison method on the splay tree). But if there is a right + * <= base (this is ensured by cbsCompare, which is the + * comparison method on the tree). But if there is a right * neighbour, all we know is that base < rightCBS->base. But for the * range to fit, we need limit <= rightCBS->base too. Hence the extra * check and the possibility of failure in the second case. */ - if (leftSplay == NULL) { + if (leftSplay == TreeEMPTY) { leftCBS = NULL; leftMerge = FALSE; } else { - leftCBS = cbsBlockOfSplayNode(leftSplay); + leftCBS = cbsBlockOfNode(leftSplay); AVER(leftCBS->limit <= base); leftMerge = leftCBS->limit == base; } - if (rightSplay == NULL) { + if (rightSplay == TreeEMPTY) { rightCBS = NULL; rightMerge = FALSE; } else { - rightCBS = cbsBlockOfSplayNode(rightSplay); + rightCBS = cbsBlockOfNode(rightSplay); if (rightCBS != NULL && limit > CBSBlockLimit(rightCBS)) { res = ResFAIL; goto fail; @@ -493,13 +490,13 @@ Res CBSInsert(Range rangeReturn, CBS cbs, Range range) } -/* cbsDeleteFromTree -- delete blocks from the splay tree */ +/* cbsDeleteFromTree -- delete blocks from the tree */ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) { Res res; CBSBlock cbsBlock; - SplayNode splayNode; + Tree node; Addr base, limit, oldBase, oldLimit; Size oldSize; @@ -511,11 +508,12 @@ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) base = RangeBase(range); limit = RangeLimit(range); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeSearch(&splayNode, splayTreeOfCBS(cbs), (void *)&base); - if (res != ResOK) + METER_ACC(cbs->treeSearch, cbs->treeSize); + if (!SplayTreeFind(&node, treeOfCBS(cbs), (void *)&base)) { + res = ResFAIL; goto failSplayTreeSearch; - cbsBlock = cbsBlockOfSplayNode(splayNode); + } + cbsBlock = cbsBlockOfNode(node); if (limit > cbsBlock->limit) { res = ResFAIL; @@ -597,7 +595,8 @@ static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) { Res res; - if (stream == NULL) return ResFAIL; + if (stream == NULL) + return ResFAIL; res = WriteF(stream, "[$P,$P) {$U}", @@ -608,49 +607,80 @@ static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) return res; } -static Res cbsSplayNodeDescribe(SplayNode splayNode, mps_lib_FILE *stream) +static Res cbsSplayNodeDescribe(Tree node, mps_lib_FILE *stream) { Res res; - if (splayNode == NULL) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (node == TreeEMPTY) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = cbsBlockDescribe(cbsBlockOfSplayNode(splayNode), stream); + res = cbsBlockDescribe(cbsBlockOfNode(node), stream); return res; } -/* CBSIterate -- Iterate all blocks in CBS +/* CBSIterate -- iterate over all blocks in CBS + * + * Applies a visitor to all isolated contiguous ranges in a CBS. + * It receives a pointer, ``Size`` closure pair to pass on to the + * visitor function, and an visitor function to invoke on every range + * in address order. If the visitor returns ``FALSE``, then the iteration + * is terminated. + * + * The visitor function may not modify the CBS during the iteration. + * This is because CBSIterate uses TreeTraverse, which does not permit + * modification, for speed and to avoid perturbing the splay tree balance. * - * This is not necessarily efficient. * See . */ -void CBSIterate(CBS cbs, CBSIterateMethod iterate, +typedef struct CBSIterateClosure { + CBS cbs; + CBSVisitor iterate; + void *closureP; + Size closureS; +} CBSIterateClosure; + +static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) +{ + CBSIterateClosure *closure = closureP; + RangeStruct range; + CBSBlock cbsBlock; + CBS cbs = closure->cbs; + + UNUSED(closureS); + + cbsBlock = cbsBlockOfNode(tree); + RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); + if (!closure->iterate(cbs, &range, closure->closureP, closure->closureS)) + return FALSE; + METER_ACC(cbs->treeSearch, cbs->treeSize); + return TRUE; +} + +void CBSIterate(CBS cbs, CBSVisitor visitor, void *closureP, Size closureS) { - SplayNode splayNode; - SplayTree splayTree; - CBSBlock cbsBlock; + SplayTree tree; + CBSIterateClosure closure; AVERT(CBS, cbs); cbsEnter(cbs); - AVER(FUNCHECK(iterate)); + AVER(FUNCHECK(visitor)); - splayTree = splayTreeOfCBS(cbs); + tree = treeOfCBS(cbs); /* .splay-iterate.slow: We assume that splay tree iteration does */ /* searches and meter it. */ - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - splayNode = SplayTreeFirst(splayTree, NULL); - while(splayNode != NULL) { - RangeStruct range; - cbsBlock = cbsBlockOfSplayNode(splayNode); - RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); - if (!(*iterate)(cbs, &range, closureP, closureS)) - break; - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - splayNode = SplayTreeNext(splayTree, splayNode, keyOfCBSBlock(cbsBlock)); - } + METER_ACC(cbs->treeSearch, cbs->treeSize); + + closure.cbs = cbs; + closure.iterate = visitor; + closure.closureP = closureP; + closure.closureS = closureS; + (void)TreeTraverse(SplayTreeRoot(tree), tree->compare, tree->nodeKey, + cbsIterateVisit, &closure, 0); cbsLeave(cbs); return; @@ -722,7 +752,7 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, Res res; res = cbsDeleteFromTree(oldRangeReturn, cbs, rangeReturn); /* Can't have run out of memory, because all our callers pass in - blocks that were just found in the splay tree, and we only + blocks that were just found in the tree, and we only deleted from one end of the block, so cbsDeleteFromTree did not need to allocate a new block. */ AVER(res == ResOK); @@ -736,7 +766,7 @@ Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete) { Bool found; - SplayNode node; + Tree node; AVERT(CBS, cbs); cbsEnter(cbs); @@ -748,13 +778,13 @@ Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, AVER(cbs->fastFind); AVERT(FindDelete, findDelete); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - found = SplayFindFirst(&node, splayTreeOfCBS(cbs), &cbsTestNode, + METER_ACC(cbs->treeSearch, cbs->treeSize); + found = SplayFindFirst(&node, treeOfCBS(cbs), &cbsTestNode, &cbsTestTree, NULL, size); if (found) { CBSBlock block; RangeStruct range; - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfNode(node); AVER(CBSBlockSize(block) >= size); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= size); @@ -773,7 +803,7 @@ Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete) { Bool found; - SplayNode node; + Tree node; AVERT(CBS, cbs); cbsEnter(cbs); @@ -785,13 +815,13 @@ Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, AVER(cbs->fastFind); AVERT(FindDelete, findDelete); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - found = SplayFindLast(&node, splayTreeOfCBS(cbs), &cbsTestNode, + METER_ACC(cbs->treeSearch, cbs->treeSize); + found = SplayFindLast(&node, treeOfCBS(cbs), &cbsTestNode, &cbsTestTree, NULL, size); if (found) { CBSBlock block; RangeStruct range; - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfNode(node); AVER(CBSBlockSize(block) >= size); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= size); @@ -810,8 +840,6 @@ Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete) { Bool found = FALSE; - SplayNode root; - Bool notEmpty; AVERT(CBS, cbs); cbsEnter(cbs); @@ -821,20 +849,19 @@ Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, AVER(cbs->fastFind); AVERT(FindDelete, findDelete); - notEmpty = SplayRoot(&root, splayTreeOfCBS(cbs)); - if (notEmpty) { + if (!SplayTreeIsEmpty(treeOfCBS(cbs))) { RangeStruct range; CBSBlock block; - SplayNode node = NULL; /* suppress "may be used uninitialized" */ + Tree node = TreeEMPTY; /* suppress "may be used uninitialized" */ Size maxSize; - maxSize = cbsBlockOfSplayNode(root)->maxSize; + maxSize = cbsBlockOfNode(SplayTreeRoot(treeOfCBS(cbs)))->maxSize; if (maxSize >= size) { - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - found = SplayFindFirst(&node, splayTreeOfCBS(cbs), &cbsTestNode, + METER_ACC(cbs->treeSearch, cbs->treeSize); + found = SplayFindFirst(&node, treeOfCBS(cbs), &cbsTestNode, &cbsTestTree, NULL, maxSize); AVER(found); /* maxSize is exact, so we will find it. */ - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfNode(node); AVER(CBSBlockSize(block) >= maxSize); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= maxSize); @@ -857,8 +884,10 @@ Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) { Res res; - if (!TESTT(CBS, cbs)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(CBS, cbs)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; res = WriteF(stream, "CBS $P {\n", (WriteFP)cbs, @@ -866,14 +895,14 @@ Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) " blockPool: $P\n", (WriteFP)cbs->blockPool, " fastFind: $U\n", (WriteFU)cbs->fastFind, " inCBS: $U\n", (WriteFU)cbs->inCBS, - " splayTreeSize: $U\n", (WriteFU)cbs->splayTreeSize, + " treeSize: $U\n", (WriteFU)cbs->treeSize, NULL); if (res != ResOK) return res; - res = SplayTreeDescribe(splayTreeOfCBS(cbs), stream, &cbsSplayNodeDescribe); + res = SplayTreeDescribe(treeOfCBS(cbs), stream, &cbsSplayNodeDescribe); if (res != ResOK) return res; - res = METER_WRITE(cbs->splaySearch, stream); + res = METER_WRITE(cbs->treeSearch, stream); if (res != ResOK) return res; res = WriteF(stream, "}\n", NULL); diff --git a/mps/code/cbs.h b/mps/code/cbs.h index aba66cf5c19..f49081a258e 100644 --- a/mps/code/cbs.h +++ b/mps/code/cbs.h @@ -17,21 +17,21 @@ typedef struct CBSStruct *CBS; -typedef Bool (*CBSIterateMethod)(CBS cbs, Range range, - void *closureP, Size closureS); +typedef Bool (*CBSVisitor)(CBS cbs, Range range, + void *closureP, Size closureS); #define CBSSig ((Sig)0x519CB599) /* SIGnature CBS */ typedef struct CBSStruct { - SplayTreeStruct splayTree; - Count splayTreeSize; + SplayTreeStruct tree; + Count treeSize; Pool blockPool; Align alignment; Bool fastFind; Bool inCBS; /* prevent reentrance */ /* meters for sizes of search structures at each op */ - METER_DECL(splaySearch); + METER_DECL(treeSearch); Sig sig; /* sig at end because embeded */ } CBSStruct; @@ -43,7 +43,7 @@ extern void CBSFinish(CBS cbs); extern Res CBSInsert(Range rangeReturn, CBS cbs, Range range); extern Res CBSDelete(Range rangeReturn, CBS cbs, Range range); -extern void CBSIterate(CBS cbs, CBSIterateMethod iterate, +extern void CBSIterate(CBS cbs, CBSVisitor visitor, void *closureP, Size closureS); extern Res CBSDescribe(CBS cbs, mps_lib_FILE *stream); diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index abc9c5a3025..c412ac476e3 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -169,7 +169,7 @@ MPMCOMMON = abq.c arena.c arenacl.c arenavm.c arg.c boot.c bt.c \ freelist.c global.c ld.c locus.c message.c meter.c mpm.c mpsi.c \ pool.c poolabs.c poolmfs.c poolmrg.c poolmv.c protocol.c range.c \ ref.c reserv.c ring.c root.c sa.c sac.c seg.c shield.c splay.c ss.c \ - table.c trace.c traceanc.c tract.c walk.c + table.c trace.c traceanc.c tract.c tree.c walk.c MPM = $(MPMCOMMON) $(MPMPF) diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index de9052303a1..11273ed3966 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -96,7 +96,7 @@ MPMCOMMON = \ \ \ \ - + PLINTH = AMC = AMS = diff --git a/mps/code/dbgpool.c b/mps/code/dbgpool.c index db4fab371c3..ab0bc7d3cfd 100644 --- a/mps/code/dbgpool.c +++ b/mps/code/dbgpool.c @@ -22,11 +22,12 @@ typedef struct tagStruct { /* We don't want to pay the expense of a sig in every tag */ Addr addr; Size size; - SplayNodeStruct splayNode; + TreeStruct treeStruct; char userdata[1 /* actually variable length */]; } tagStruct; -#define SplayNode2Tag(node) PARENT(tagStruct, splayNode, (node)) +#define TagTree(tag) (&(tag)->treeStruct) +#define TagOfTree(tree) TREE_ELT(tag, treeStruct, tree) typedef tagStruct *Tag; @@ -43,22 +44,27 @@ static void TagTrivInit(void* tag, va_list args) /* TagComp -- splay comparison function for address ordering of tags */ -static Compare TagComp(void *key, SplayNode node) +static Compare TagCompare(Tree node, TreeKey key) { Addr addr1, addr2; addr1 = *(Addr *)key; - addr2 = SplayNode2Tag(node)->addr; + addr2 = TagOfTree(node)->addr; if (addr1 < addr2) return CompareLESS; else if (addr1 > addr2) { /* Check key is not inside the object of this tag */ - AVER_CRITICAL(AddrAdd(addr2, SplayNode2Tag(node)->size) <= addr1); + AVER_CRITICAL(AddrAdd(addr2, TagOfTree(node)->size) <= addr1); return CompareGREATER; } else return CompareEQUAL; } +static TreeKey TagKey(Tree node) +{ + return &TagOfTree(node)->addr; +} + /* PoolDebugMixinCheck -- check a PoolDebugMixin */ @@ -74,7 +80,7 @@ Bool PoolDebugMixinCheck(PoolDebugMixin debug) CHECKD(Pool, debug->tagPool); CHECKL(COMPATTYPE(Addr, void*)); /* tagPool relies on this */ /* Nothing to check about missingTags */ - CHECKL(SplayTreeCheck(&debug->index)); + CHECKD(SplayTree, &debug->index); } UNUSED(debug); /* see */ return TRUE; @@ -193,7 +199,7 @@ static Res DebugPoolInit(Pool pool, ArgList args) if (res != ResOK) goto tagFail; debug->missingTags = 0; - SplayTreeInit(&debug->index, TagComp, NULL); + SplayTreeInit(&debug->index, TagCompare, TagKey, SplayTrivUpdate); } debug->sig = PoolDebugMixinSig; @@ -429,6 +435,7 @@ static Res tagAlloc(PoolDebugMixin debug, { Tag tag; Res res; + Bool b; Addr addr; UNUSED(pool); @@ -443,10 +450,10 @@ static Res tagAlloc(PoolDebugMixin debug, } tag = (Tag)addr; tag->addr = new; tag->size = size; - SplayNodeInit(&tag->splayNode); + TreeInit(TagTree(tag)); /* In the future, we might call debug->tagInit here. */ - res = SplayTreeInsert(&debug->index, &tag->splayNode, (void *)&new); - AVER(res == ResOK); + b = SplayTreeInsert(&debug->index, TagTree(tag)); + AVER(b); return ResOK; } @@ -455,25 +462,25 @@ static Res tagAlloc(PoolDebugMixin debug, static void tagFree(PoolDebugMixin debug, Pool pool, Addr old, Size size) { - SplayNode node; + Tree node; Tag tag; - Res res; + Bool b; AVERT(PoolDebugMixin, debug); AVERT(Pool, pool); AVER(size > 0); - res = SplayTreeSearch(&node, &debug->index, (void *)&old); - if (res != ResOK) { + if (!SplayTreeFind(&node, &debug->index, &old)) { AVER(debug->missingTags > 0); debug->missingTags--; return; } - tag = SplayNode2Tag(node); + tag = TagOfTree(node); AVER(tag->size == size); - res = SplayTreeDelete(&debug->index, node, (void *)&old); - AVER(res == ResOK); - SplayNodeFinish(node); + AVER(tag->addr == old); + b = SplayTreeDelete(&debug->index, node); + AVER(b); /* expect tag to be in the tree */ + TreeFinish(node); PoolFree(debug->tagPool, (Addr)tag, debug->tagSize); } @@ -554,10 +561,8 @@ typedef void (*ObjectsStepMethod)(Addr addr, Size size, Format fmt, static void TagWalk(Pool pool, ObjectsStepMethod step, void *p) { - SplayNode node; + Tree node; PoolDebugMixin debug; - Addr dummy = NULL; /* Breaks , but it's */ - /* only temporary until SplayTreeFirst is fixed. */ AVERT(Pool, pool); AVERT(ObjectsStepMethod, step); @@ -567,12 +572,12 @@ static void TagWalk(Pool pool, ObjectsStepMethod step, void *p) AVER(debug != NULL); AVERT(PoolDebugMixin, debug); - node = SplayTreeFirst(&debug->index, (void *)&dummy); + node = SplayTreeFirst(&debug->index); while (node != NULL) { - Tag tag = SplayNode2Tag(node); + Tag tag = TagOfTree(node); step(tag->addr, tag->size, NULL, pool, &tag->userdata, p); - node = SplayTreeNext(&debug->index, node, (void *)&tag->addr); + node = SplayTreeNext(&debug->index, &tag->addr); } } diff --git a/mps/code/misc.h b/mps/code/misc.h index 0557c9af6fc..f419fdd0455 100644 --- a/mps/code/misc.h +++ b/mps/code/misc.h @@ -23,6 +23,14 @@ enum BoolEnum { }; +typedef int Compare; +enum { + CompareLESS = -1, + CompareEQUAL = 0, + CompareGREATER = 1 +}; + + /* SrcId -- source identification * * Every C source file should start with a SRCID declaration to diff --git a/mps/code/mps.c b/mps/code/mps.c index 329c67de9a4..ef2484eabdf 100644 --- a/mps/code/mps.c +++ b/mps/code/mps.c @@ -64,6 +64,7 @@ #include "dbgpooli.c" #include "boot.c" #include "meter.c" +#include "tree.c" #include "splay.c" #include "cbs.c" #include "ss.c" diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index 0f5a83f6f99..d546be9ad6e 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj @@ -1278,6 +1278,8 @@ 3104B02F156D39F2000A585A /* amssshe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amssshe.c; sourceTree = ""; }; 3104B03D156D3AD7000A585A /* segsmss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = segsmss; sourceTree = BUILT_PRODUCTS_DIR; }; 3107DC4E173B03D100F705C8 /* arg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arg.h; sourceTree = ""; }; + 310F5D7118B6675F007EFCBC /* tree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tree.c; sourceTree = ""; }; + 310F5D7218B6675F007EFCBC /* tree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tree.h; sourceTree = ""; }; 3112ED3A18ABC57F00CC531A /* sa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sa.h; sourceTree = ""; }; 3112ED3B18ABC75200CC531A /* sa.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sa.c; sourceTree = ""; }; 3114A590156E913C001E0AA3 /* locv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locv; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2195,6 +2197,8 @@ 31EEAC30156AB2F200714D05 /* ring.c */, 311F2F7317398B7100C15B6A /* ring.h */, 31EEAC1C156AB2B200714D05 /* root.c */, + 3112ED3B18ABC75200CC531A /* sa.c */, + 3112ED3A18ABC57F00CC531A /* sa.h */, 31EEAC31156AB2F200714D05 /* sac.c */, 311F2F7417398B7100C15B6A /* sac.h */, 311F2F7517398B8E00C15B6A /* sc.h */, @@ -2209,10 +2213,10 @@ 31EEAC1F156AB2B200714D05 /* traceanc.c */, 31EEAC0D156AB27B00714D05 /* tract.c */, 311F2F7A17398B8E00C15B6A /* tract.h */, + 310F5D7118B6675F007EFCBC /* tree.c */, + 310F5D7218B6675F007EFCBC /* tree.h */, 31EEAC44156AB32500714D05 /* version.c */, 31EEAC0E156AB27B00714D05 /* walk.c */, - 3112ED3A18ABC57F00CC531A /* sa.h */, - 3112ED3B18ABC75200CC531A /* sa.c */, ); name = "MPM Core"; sourceTree = ""; diff --git a/mps/code/splay.c b/mps/code/splay.c index 4042f2c0e07..549451c9a95 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -1,16 +1,18 @@ /* splay.c: SPLAY TREE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: Splay trees are used to manage potentially unbounded - * collections of ordered things. + * collections of ordered things. In the MPS these are usually + * address-ordered memory blocks. * - * .source: + * .source: * * .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. + * we have to avoid recursion. TODO: Design documentation for this + * requirement, meanwhile see job003651 and job003640. */ @@ -20,646 +22,847 @@ SRCID(splay, "$Id$"); -/* Basic getter and setter methods */ +/* 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 SplayTreeRoot(t) RVALUE((t)->root) -#define SplayTreeSetRoot(t, r) BEGIN ((t)->root = (r)); END -#define SplayNodeLeftChild(n) RVALUE((n)->left) -#define SplayNodeSetLeftChild(n, child) \ - BEGIN ((n)->left = (child)); END -#define SplayNodeRightChild(n) RVALUE((n)->right) -#define SplayNodeSetRightChild(n, child) \ - BEGIN ((n)->right = (child)); END +/* #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) -#define SplayCompare(tree, key, node) \ - (((tree)->compare)((key), (node))) +/* SplayTreeCheck -- check consistency of SplayTree + * + * See guide.impl.c.adt.check and . + */ - -Bool SplayTreeCheck(SplayTree tree) +Bool SplayTreeCheck(SplayTree splay) { - UNUSED(tree); - CHECKL(tree != NULL); - CHECKL(FUNCHECK(tree->compare)); - CHECKL(tree->updateNode == NULL || FUNCHECK(tree->updateNode)); + UNUSED(splay); + CHECKS(SplayTree, splay); + CHECKL(FUNCHECK(splay->compare)); + CHECKL(FUNCHECK(splay->nodeKey)); + CHECKL(FUNCHECK(splay->updateNode)); + CHECKL(TreeCheck(splay->root)); return TRUE; } -Bool SplayNodeCheck(SplayNode node) -{ - UNUSED(node); - CHECKL(node != NULL); - 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 tree, SplayCompareMethod compare, +void SplayTreeInit(SplayTree splay, + TreeCompare compare, + TreeKeyMethod nodeKey, SplayUpdateNodeMethod updateNode) { - AVER(tree != NULL); + AVER(splay != NULL); AVER(FUNCHECK(compare)); - AVER(updateNode == NULL || FUNCHECK(updateNode)); + AVER(FUNCHECK(nodeKey)); + AVER(FUNCHECK(updateNode)); - tree->compare = compare; - tree->updateNode = updateNode; - SplayTreeSetRoot(tree, NULL); + splay->compare = compare; + splay->nodeKey = nodeKey; + splay->updateNode = updateNode; + SplayTreeSetRoot(splay, TreeEMPTY); + splay->sig = SplayTreeSig; - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); } -void SplayNodeInit(SplayNode node) -{ - AVER(node != NULL); - - /* We don't try to finish the attached nodes. See .note.stack. */ - SplayNodeSetLeftChild(node, NULL); - SplayNodeSetRightChild(node, NULL); - - AVERT(SplayNode, node); -} - - -void SplayNodeFinish(SplayNode node) -{ - AVERT(SplayNode, node); - - /* we don't try to do a recursive finish. See .note.stack. */ - SplayNodeSetLeftChild(node, NULL); - SplayNodeSetRightChild(node, NULL); -} - - -void SplayTreeFinish(SplayTree tree) -{ - AVERT(SplayTree, tree); - SplayTreeSetRoot(tree, NULL); - tree->compare = NULL; -} - - -static void SplayNodeUpdate(SplayTree tree, SplayNode node) -{ - AVERT(SplayTree, tree); - AVERT(SplayNode, node); - AVER(tree->updateNode != NULL); - - (*tree->updateNode)(tree, node, SplayNodeLeftChild(node), - SplayNodeRightChild(node)); - return; -} - - -/* SplayLinkRight -- Move top to left child of top +/* SplayTreeFinish -- finish a splay tree * - * Link the current top node into the left child of the right tree, - * leaving the top node as the left child of the old top node. + * 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); +} + + +/* 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 . + * + * middle rightNext middle + * B E A E + * / \ / \ => / \ + * A C D F rightNext D F + * rightFirst / + * rightFirst B + * \ + * C */ -static void SplayLinkRight(SplayNode *topIO, SplayNode *rightIO) +static Tree SplayZig(Tree middle, Tree *rightFirstIO, Tree *rightNextReturn) { - AVERT(SplayNode, *topIO); - AVERT(SplayNode, *rightIO); + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(rightFirstIO != NULL); + AVERT_CRITICAL(Tree, *rightFirstIO); + TreeSetLeft(*rightFirstIO, middle); + *rightNextReturn = *rightFirstIO; + *rightFirstIO = middle; + return TreeLeft(middle); +} - /* Don't fix client properties yet. */ +/* 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 + */ - /* .link.right.first: *rightIO is always the first node in the */ - /* right tree, so its left child must be null. */ - AVER(SplayNodeLeftChild(*rightIO) == NULL); +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 */ - SplayNodeSetLeftChild(*rightIO, *topIO); - *rightIO = *topIO; - *topIO = SplayNodeLeftChild(*topIO); +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); +} - /* The following line is only required for .link.right.first. */ - SplayNodeSetLeftChild(*rightIO, NULL); +/* 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); } -/* SplayLinkLeft -- Move top to right child of top +/* SplayState -- the state of splaying between "split" and "assemble" * - * Link the current top node into the right child of the left tree, - * leaving the top node as the right child of the old top node. + * 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). * - * See . + * 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. */ -static void SplayLinkLeft(SplayNode *topIO, SplayNode *leftIO) { - AVERT(SplayNode, *topIO); - AVERT(SplayNode, *leftIO); - - /* Don't fix client properties yet. */ - - /* .link.left.first: *leftIO is always the last node in the */ - /* left tree, so its right child must be null. */ - AVER(SplayNodeRightChild(*leftIO) == NULL); - - SplayNodeSetRightChild(*leftIO, *topIO); - *leftIO = *topIO; - *topIO = SplayNodeRightChild(*topIO); - - /* The following line is only required for .link.left.first. */ - SplayNodeSetRightChild(*leftIO, NULL); -} +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; -/* SplayRotateLeft -- Rotate right child edge of node +/* SplaySplitDown -- divide the tree around a key * - * Rotates node, right child of node, and left child of right - * child of node, leftwards in the order stated. + * 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). * - * See . + * 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 void SplayRotateLeft(SplayNode *nodeIO, SplayTree tree) { - SplayNode nodeRight; +static Compare SplaySplitDown(SplayStateStruct *stateReturn, + SplayTree splay, TreeKey key, TreeCompare compare) +{ + TreeStruct sentinel; + Tree middle, leftLast, rightFirst, leftPrev, rightNext; + Compare cmp; - AVER(nodeIO != NULL); - AVERT(SplayNode, *nodeIO); - AVERT(SplayNode, SplayNodeRightChild(*nodeIO)); - AVERT(SplayTree, tree); + 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; - nodeRight = SplayNodeRightChild(*nodeIO); - SplayNodeSetRightChild(*nodeIO, SplayNodeLeftChild(nodeRight)); - SplayNodeSetLeftChild(nodeRight, *nodeIO); - *nodeIO = nodeRight; + 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; - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, SplayNodeLeftChild(nodeRight)); - /* Don't need to update new root because we know that we will */ - /* do either a link or an assemble next, and that will sort it */ - /* out. */ + 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; + } } - return; +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; } -/* SplayRotateRight -- Rotate left child edge of node +/* SplayAssembleDown -- assemble left right and middle trees into one * - * Rotates node, left child of node, and right child of left - * child of node, leftwards in the order stated. + * Takes the result of a SplaySplit and forms a single tree with the + * root of the middle tree as the root. * - * See . - */ - -static void SplayRotateRight(SplayNode *nodeIO, SplayTree tree) { - SplayNode nodeLeft; - - AVER(nodeIO != NULL); - AVERT(SplayNode, *nodeIO); - AVERT(SplayNode, SplayNodeLeftChild(*nodeIO)); - AVERT(SplayTree, tree); - - nodeLeft = SplayNodeLeftChild(*nodeIO); - SplayNodeSetLeftChild(*nodeIO, SplayNodeRightChild(nodeLeft)); - SplayNodeSetRightChild(nodeLeft, *nodeIO); - *nodeIO = nodeLeft; - - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, SplayNodeRightChild(nodeLeft)); - /* Don't need to update new root because we know that we will */ - /* do either a link or an assemble next, and that will sort it */ - /* out. */ - } - - return; -} - - -/* SplayAssemble -- Assemble left right and top trees into one + * left middle right middle + * B P V P + * / \ / \ / \ => / \ + * A C N Q U X B V + * leftLast rightFirst / \ / \ + * A C U X + * \ / + * N Q * - * We do this by moving the children of the top tree to the last and - * first nodes in the left and right trees, and then moving the tops - * of the left and right trees to the children of the top tree. + * 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. * - * When we reach this function, the nodes between the roots of the - * left and right trees and their last and first nodes respectively - * will have out of date client properties. + * Does *not* maintain client properties. See SplayAssembleRev. * * See . */ -static void SplayAssemble(SplayTree tree, SplayNode top, - SplayNode leftTop, SplayNode leftLast, - SplayNode rightTop, SplayNode rightFirst) { - AVERT(SplayTree, tree); - AVERT(SplayNode, top); - AVER(leftTop == NULL || - (SplayNodeCheck(leftTop) && SplayNodeCheck(leftLast))); - AVER(rightTop == NULL || - (SplayNodeCheck(rightTop) && SplayNodeCheck(rightFirst))); +static void SplayAssembleDown(SplayTree splay, SplayState state) +{ + AVERT(SplayTree, splay); + AVER(state->middle != TreeEMPTY); + AVER(!SplayHasUpdate(splay)); - if (leftTop != NULL) { - SplayNodeSetRightChild(leftLast, SplayNodeLeftChild(top)); - SplayNodeSetLeftChild(top, leftTop); - - if (tree->updateNode != NULL) { - /* Update client property using pointer reversal (Ugh!). */ - SplayNode node, parent, rightChild; - - /* Reverse the pointers between leftTop and leftLast */ - /* leftLast is not reversed. */ - node = leftTop; - parent = NULL; - while(node != leftLast) { - rightChild = SplayNodeRightChild(node); - SplayNodeSetRightChild(node, parent); /* pointer reversal */ - parent = node; - node = rightChild; - } - - /* Now restore the pointers, updating the client property. */ - /* node is leftLast, parent is the last parent (or NULL). */ - SplayNodeUpdate(tree, node); - while(node != leftTop) { - rightChild = node; - node = parent; - parent = SplayNodeRightChild(node); - SplayNodeSetRightChild(node, rightChild); /* un-reverse pointer */ - SplayNodeUpdate(tree, node); - } - } + if (state->left != TreeEMPTY) { + AVER_CRITICAL(state->leftLast != TreeEMPTY); + TreeSetRight(state->leftLast, TreeLeft(state->middle)); + TreeSetLeft(state->middle, state->left); } - /* otherwise leave top->left alone */ - if (rightTop != NULL) { - SplayNodeSetLeftChild(rightFirst, SplayNodeRightChild(top)); - SplayNodeSetRightChild(top, rightTop); - - if (tree->updateNode != NULL) { - /* Update client property using pointer reversal (Ugh!). */ - SplayNode node, parent, leftChild; - - /* Reverse the pointers between rightTop and rightFirst */ - /* ightFirst is not reversed. */ - node = rightTop; - parent = NULL; - while(node != rightFirst) { - leftChild = SplayNodeLeftChild(node); - SplayNodeSetLeftChild(node, parent); /* pointer reversal */ - parent = node; - node = leftChild; - } - - /* Now restore the pointers, updating the client property. */ - /* node is rightFirst, parent is the last parent (or NULL). */ - SplayNodeUpdate(tree, node); - while(node != rightTop) { - leftChild = node; - node = parent; - parent = SplayNodeLeftChild(node); - SplayNodeSetLeftChild(node, leftChild); /* un-reverse pointer */ - SplayNodeUpdate(tree, node); - } - } + if (state->right != TreeEMPTY) { + AVER_CRITICAL(state->rightFirst != TreeEMPTY); + TreeSetLeft(state->rightFirst, TreeRight(state->middle)); + TreeSetRight(state->middle, state->right); } - /* otherwise leave top->right alone */ - - if (tree->updateNode != NULL) - SplayNodeUpdate(tree, top); } -/* SplaySplay -- Splay the tree (top-down) around a given key +/* SplayZigRev -- move to left child, prepending to reversed right tree * - * If the key is not found, splays around an arbitrary neighbour. - * Returns whether key was found. This is the real logic behind - * splay trees. + * 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, TreeCompare compare) +{ + Tree middle, leftLast, rightFirst; + Compare cmp; + + AVERT(SplayTree, splay); + AVER(FUNCHECK(compare)); + AVER(!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(SplayTree, splay); + AVER(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, TreeCompare 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 . */ -static Bool SplaySplay(SplayNode *nodeReturn, SplayTree tree, - void *key, SplayCompareMethod compareMethod) { - /* The sides structure avoids a boundary case in SplayLink* */ - SplayNodeStruct sides; /* rightTop and leftTop */ - SplayNode top, leftLast, rightFirst; - Bool found; - Compare compareTop; +static Compare SplaySplay(SplayTree splay, TreeKey key, TreeCompare compare) +{ + Compare cmp; + SplayStateStruct stateStruct; - AVERT(SplayTree, tree); - AVER(nodeReturn != NULL); - AVER(FUNCHECK(compareMethod)); +#ifdef SPLAY_DEBUG + Count count = TreeDebugCount(SplayTreeRoot(tree), tree->compare, tree->nodeKey); +#endif - top = SplayTreeRoot(tree); /* will be copied back at end */ + /* 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 (top == NULL) { - *nodeReturn = NULL; - return FALSE; + if (SplayHasUpdate(splay)) { + cmp = SplaySplitRev(&stateStruct, splay, key, compare); + SplayAssembleRev(splay, &stateStruct); + } else { + cmp = SplaySplitDown(&stateStruct, splay, key, compare); + SplayAssembleDown(splay, &stateStruct); } - /* short-circuit case where node is already top */ - compareTop = (*compareMethod)(key, top); - if (compareTop == CompareEQUAL) { - *nodeReturn = top; + SplayTreeSetRoot(splay, stateStruct.middle); + +#ifdef SPLAY_DEBUG + AVER(count == TreeDebugCount(SplayTreeRoot(tree), tree->compare, tree->nodeKey)); +#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; - SplayNodeInit(&sides); /* left and right trees now NULL */ - leftLast = &sides; - rightFirst = &sides; - - while(TRUE) { - /* compareTop is already initialised above. */ - switch(compareTop) { - - case CompareLESS: { - SplayNode topLeft = SplayNodeLeftChild(top); - if (topLeft == NULL) { - found = FALSE; - goto assemble; - } else { - Compare compareTopLeft = (*compareMethod)(key, topLeft); - - switch(compareTopLeft) { - - case CompareEQUAL: { /* zig */ - SplayLinkRight(&top, &rightFirst); - found = TRUE; - goto assemble; - } /* break; */ - - case CompareLESS: { /* zig-zig */ - if (SplayNodeLeftChild(topLeft) == NULL) - goto terminalZig; - SplayRotateRight(&top, tree); - SplayLinkRight(&top, &rightFirst); - } break; - - case CompareGREATER: { /* zig-zag */ - if (SplayNodeRightChild(topLeft) == NULL) - goto terminalZig; - SplayLinkRight(&top, &rightFirst); - SplayLinkLeft(&top, &leftLast); - } break; - - default: { - NOTREACHED; - } break; - } - } - } break; - - case CompareGREATER: { - SplayNode topRight = SplayNodeRightChild(top); - if (topRight == NULL) { - found = FALSE; - goto assemble; - } else { - Compare compareTopRight = (*compareMethod)(key, topRight); - - switch(compareTopRight) { - - case CompareEQUAL: { /* zag */ - SplayLinkLeft(&top, &leftLast); - found = TRUE; - goto assemble; - } /* break; */ - - case CompareGREATER: { /* zag-zag */ - if (SplayNodeRightChild(topRight) == NULL) - goto terminalZag; - SplayRotateLeft(&top, tree); - SplayLinkLeft(&top, &leftLast); - } break; - - case CompareLESS: { /* zag-zig */ - if (SplayNodeLeftChild(topRight) == NULL) - goto terminalZag; - SplayLinkLeft(&top, &leftLast); - SplayLinkRight(&top, &rightFirst); - } break; - - default: { - NOTREACHED; - } break; - } - } - } break; - - case CompareEQUAL: { - found = TRUE; - goto assemble; - } /* break; */ - - default: { - NOTREACHED; - } break; - } - compareTop = (*compareMethod)(key, top); - } /* end while(TRUE) */ - -terminalZig: - SplayLinkRight(&top, &rightFirst); - found = FALSE; - goto assemble; - -terminalZag: - SplayLinkLeft(&top, &leftLast); - found = FALSE; - goto assemble; - -assemble: - SplayAssemble(tree, top, - SplayNodeRightChild(&sides), leftLast, - SplayNodeLeftChild(&sides), rightFirst); - - SplayTreeSetRoot(tree, top); - *nodeReturn = top; - - return found; -} - - -/* SplayTreeInsert -- Insert a node into a splay tree - * - * See and - * . - */ - -Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key) { - SplayNode neighbour; - - AVERT(SplayTree, tree); - AVERT(SplayNode, node); - AVER(SplayNodeLeftChild(node) == NULL); - AVER(SplayNodeRightChild(node) == NULL); - - if (SplayTreeRoot(tree) == NULL) { - SplayTreeSetRoot(tree, node); - } else if (SplaySplay(&neighbour, tree, key, tree->compare)) { - return ResFAIL; - } else { - AVER(SplayTreeRoot(tree) == neighbour); - switch(SplayCompare(tree, key, neighbour)) { - - case CompareGREATER: { /* left neighbour */ - SplayTreeSetRoot(tree, node); - SplayNodeSetRightChild(node, SplayNodeRightChild(neighbour)); - SplayNodeSetLeftChild(node, neighbour); - SplayNodeSetRightChild(neighbour, NULL); - } break; - - case CompareLESS: { /* right neighbour */ - SplayTreeSetRoot(tree, node); - SplayNodeSetLeftChild(node, SplayNodeLeftChild(neighbour)); - SplayNodeSetRightChild(node, neighbour); - SplayNodeSetLeftChild(neighbour, NULL); - } break; - - case CompareEQUAL: - default: { - NOTREACHED; - } break; - } - - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, neighbour); - SplayNodeUpdate(tree, node); - } + case CompareLESS: /* right neighbour is at root */ + neighbour = SplayTreeRoot(splay); + SplayTreeSetRoot(splay, node); + TreeSetLeft(node, TreeLeft(neighbour)); + TreeSetRight(node, neighbour); + TreeSetLeft(neighbour, TreeEMPTY); + break; } - return ResOK; + splay->updateNode(splay, neighbour); + splay->updateNode(splay, node); + return TRUE; } -/* SplayTreeDelete -- Delete a node from a splay tree +/* SplayTreeDelete -- delete a node from a splay tree * - * See and - * . + * 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. */ -Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key) { - SplayNode rightHalf, del, leftLast; - Bool found; +Bool SplayTreeDelete(SplayTree splay, Tree node) { + Tree leftLast; + Compare cmp; - AVERT(SplayTree, tree); - AVERT(SplayNode, node); + AVERT(SplayTree, splay); + AVERT(Tree, node); - found = SplaySplay(&del, tree, key, tree->compare); - AVER(!found || del == node); + if (SplayTreeIsEmpty(splay)) + return FALSE; - if (!found) { - return ResFAIL; - } else if (SplayNodeLeftChild(node) == NULL) { - SplayTreeSetRoot(tree, SplayNodeRightChild(node)); - } else if (SplayNodeRightChild(node) == NULL) { - SplayTreeSetRoot(tree, SplayNodeLeftChild(node)); + 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 { - rightHalf = SplayNodeRightChild(node); - SplayTreeSetRoot(tree, SplayNodeLeftChild(node)); - if (SplaySplay(&leftLast, tree, key, tree->compare)) { - return ResFAIL; - } else { - AVER(SplayNodeRightChild(leftLast) == NULL); - SplayNodeSetRightChild(leftLast, rightHalf); - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, leftLast); - } - } + Tree rightHalf = TreeRight(node); + TreeClearRight(node); + SplayTreeSetRoot(splay, TreeLeft(node)); + TreeClearLeft(node); + SplaySplay(splay, NULL, compareGreater); + leftLast = SplayTreeRoot(splay); + AVER(leftLast != TreeEMPTY); + AVER(!TreeHasRight(leftLast)); + TreeSetRight(leftLast, rightHalf); + splay->updateNode(splay, leftLast); } - SplayNodeFinish(node); + TreeFinish(node); - return ResOK; + return TRUE; } -/* SplayTreeSearch -- Search for a node in a splay tree matching a key +/* SplayTreeFind -- search for a node in a splay tree matching a key * - * See and - * . + * 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. */ -Res SplayTreeSearch(SplayNode *nodeReturn, SplayTree tree, void *key) { - SplayNode node; - - AVERT(SplayTree, tree); +Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) { + AVERT(SplayTree, splay); AVER(nodeReturn != NULL); - if (SplaySplay(&node, tree, key, tree->compare)) { - *nodeReturn = node; - } else { - return ResFAIL; - } + if (SplayTreeIsEmpty(splay)) + return FALSE; - return ResOK; + if (SplaySplay(splay, key, splay->compare) != CompareEQUAL) + return FALSE; + + *nodeReturn = SplayTreeRoot(splay); + return TRUE; } -/* SplayTreePredecessor -- Splays a tree at the root's predecessor - * - * Must not be called on en empty tree. Predecessor need not exist, - * in which case NULL is returned, and the tree is unchanged. - */ - -static SplayNode SplayTreePredecessor(SplayTree tree, void *key) { - SplayNode oldRoot, newRoot; - - AVERT(SplayTree, tree); - - oldRoot = SplayTreeRoot(tree); - AVERT(SplayNode, oldRoot); - - if (SplayNodeLeftChild(oldRoot) == NULL) { - newRoot = NULL; /* No predecessor */ - } else { - /* temporarily chop off the right half-tree, inclusive of root */ - SplayTreeSetRoot(tree, SplayNodeLeftChild(oldRoot)); - SplayNodeSetLeftChild(oldRoot, NULL); - if (SplaySplay(&newRoot, tree, key, tree->compare)) { - NOTREACHED; /* Another matching node found */ - } else { - AVER(SplayNodeRightChild(newRoot) == NULL); - SplayNodeSetRightChild(newRoot, oldRoot); - } - - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, oldRoot); - SplayNodeUpdate(tree, newRoot); - } - } - - return newRoot; -} - - -/* SplayTreeSuccessor -- Splays a tree at the root's successor +/* SplayTreeSuccessor -- splays a tree at the root's successor * * Must not be called on en empty tree. Successor need not exist, - * in which case NULL is returned, and the tree is unchanged. + * in which case TreeEMPTY is returned, and the tree is unchanged. */ -static SplayNode SplayTreeSuccessor(SplayTree tree, void *key) { - SplayNode oldRoot, newRoot; +static Tree SplayTreeSuccessor(SplayTree splay) { + Tree oldRoot, newRoot; - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); + AVER(!SplayTreeIsEmpty(splay)); - oldRoot = SplayTreeRoot(tree); - AVERT(SplayNode, oldRoot); + oldRoot = SplayTreeRoot(splay); - if (SplayNodeRightChild(oldRoot) == NULL) { - newRoot = NULL; /* No successor */ - } else { - /* temporarily chop off the left half-tree, inclusive of root */ - SplayTreeSetRoot(tree, SplayNodeRightChild(oldRoot)); - SplayNodeSetRightChild(oldRoot, NULL); - if (SplaySplay(&newRoot, tree, key, tree->compare)) { - NOTREACHED; /* Another matching node found */ - } else { - AVER(SplayNodeLeftChild(newRoot) == NULL); - SplayNodeSetLeftChild(newRoot, oldRoot); - } + if (!TreeHasRight(oldRoot)) + return TreeEMPTY; /* No successor */ - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, oldRoot); - SplayNodeUpdate(tree, newRoot); - } - } + /* temporarily chop off the left half-tree, inclusive of root */ + SplayTreeSetRoot(splay, TreeRight(oldRoot)); + TreeSetRight(oldRoot, TreeEMPTY); + 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; } @@ -668,88 +871,130 @@ static SplayNode SplayTreeSuccessor(SplayTree tree, void *key) { /* 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. * - * See and - * . + * 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 = TreeDebugCount(SplayTreeRoot(tree), tree->compare, tree->nodeKey); +#endif -Res SplayTreeNeighbours(SplayNode *leftReturn, SplayNode *rightReturn, - SplayTree tree, void *key) { - SplayNode neighbour; - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); AVER(leftReturn != NULL); AVER(rightReturn != NULL); - if (SplaySplay(&neighbour, tree, key, tree->compare)) { - return ResFAIL; - } else if (neighbour == NULL) { - *leftReturn = *rightReturn = NULL; - } else { - switch(SplayCompare(tree, key, neighbour)) { - - case CompareLESS: { - *rightReturn = neighbour; - *leftReturn = SplayTreePredecessor(tree, key); - } break; - - case CompareGREATER: { - *leftReturn = neighbour; - *rightReturn = SplayTreeSuccessor(tree, key); - } break; - - case CompareEQUAL: - default: { - NOTREACHED; - } break; - } + if (SplayTreeIsEmpty(splay)) { + *leftReturn = *rightReturn = TreeEMPTY; + return TRUE; } - return ResOK; + + 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 == TreeDebugCount(SplayTreeRoot(tree), tree->compare, tree->nodeKey)); +#endif + + return found; } -/* SplayTreeFirst, SplayTreeNext -- Iterators +/* SplayTreeFirst, SplayTreeNext -- iterators * * SplayTreeFirst receives a key that must precede all - * nodes in the tree. It returns NULL if the tree is empty. + * nodes in the tree. It returns TreeEMPTY if the tree is empty. * Otherwise, it splays the tree to the first node, and returns the - * new root. See . + * new root. * - * SplayTreeNext takes a tree and splays it to the successor of the - * old root, and returns the new root. Returns NULL is there are - * no successors. It takes a key for the old root. See - * . + * 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. */ -SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey) { - SplayNode node; - AVERT(SplayTree, tree); +Tree SplayTreeFirst(SplayTree splay) { + Tree node; - if (SplayTreeRoot(tree) == NULL) { - node = NULL; - } else if (SplaySplay(&node, tree, zeroKey, tree->compare)) { - NOTREACHED; - } else { - AVER(SplayNodeLeftChild(node) == NULL); - } + AVERT(SplayTree, splay); + + if (SplayTreeIsEmpty(splay)) + return TreeEMPTY; + + SplaySplay(splay, NULL, compareLess); + node = SplayTreeRoot(splay); + AVER(node != TreeEMPTY); + AVER(TreeLeft(node) == TreeEMPTY); return node; } -SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey) { - Bool b; - SplayNode node; +Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) { + AVERT(SplayTree, splay); - AVERT(SplayTree, tree); - AVERT(SplayNode, oldNode); + 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 CompareGREATER: + return SplayTreeRoot(splay); - /* Make old node the root. Probably already is. */ - b = SplaySplay(&node, tree, oldKey, tree->compare); - AVER(b); - AVER(node == oldNode); - - return SplayTreeSuccessor(tree, oldKey); + case CompareLESS: + case CompareEQUAL: + return SplayTreeSuccessor(splay); + } } @@ -759,20 +1004,20 @@ SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey) { * This is alright as the function is debug only. */ -static Res SplayNodeDescribe(SplayNode node, mps_lib_FILE *stream, +static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe) { Res res; #if defined(AVER_AND_CHECK) - if (!SplayNodeCheck(node)) return ResFAIL; + if (!TreeCheck(node)) return ResFAIL; /* stream and nodeDescribe checked by SplayTreeDescribe */ #endif res = WriteF(stream, "( ", NULL); if (res != ResOK) return res; - if (SplayNodeLeftChild(node) != NULL) { - res = SplayNodeDescribe(SplayNodeLeftChild(node), stream, nodeDescribe); + if (TreeHasLeft(node)) { + res = SplayNodeDescribe(TreeLeft(node), stream, nodeDescribe); if (res != ResOK) return res; res = WriteF(stream, " / ", NULL); @@ -782,11 +1027,11 @@ static Res SplayNodeDescribe(SplayNode node, mps_lib_FILE *stream, res = (*nodeDescribe)(node, stream); if (res != ResOK) return res; - if (SplayNodeRightChild(node) != NULL) { + if (TreeHasRight(node)) { res = WriteF(stream, " \\ ", NULL); if (res != ResOK) return res; - res = SplayNodeDescribe(SplayNodeRightChild(node), stream, nodeDescribe); + res = SplayNodeDescribe(TreeRight(node), stream, nodeDescribe); if (res != ResOK) return res; } @@ -797,72 +1042,91 @@ static Res SplayNodeDescribe(SplayNode node, mps_lib_FILE *stream, } -typedef struct { +/* 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 { SplayTestNodeMethod testNode; SplayTestTreeMethod testTree; void *p; Size s; - SplayTree tree; + SplayTree splay; } SplayFindClosureStruct, *SplayFindClosure; -static Compare SplayFindFirstCompare(void *key, SplayNode node) +static Compare SplayFindFirstCompare(Tree node, TreeKey key) { SplayFindClosure closure; void *closureP; Size closureS; SplayTestNodeMethod testNode; SplayTestTreeMethod testTree; - SplayTree tree; + SplayTree splay; - AVERT(SplayNode, node); + AVERT(Tree, node); AVER(key != NULL); + /* Lift closure values into variables so that they aren't aliased by + calls to the test functions. */ closure = (SplayFindClosure)key; closureP = closure->p; closureS = closure->s; testNode = closure->testNode; testTree = closure->testTree; - tree = closure->tree; + splay = closure->splay; - if (SplayNodeLeftChild(node) != NULL && - (*testTree)(tree, SplayNodeLeftChild(node), closureP, closureS)) { + if (TreeHasLeft(node) && + (*testTree)(splay, TreeLeft(node), closureP, closureS)) { return CompareLESS; - } else if ((*testNode)(tree, node, closureP, closureS)) { + } else if ((*testNode)(splay, node, closureP, closureS)) { return CompareEQUAL; } else { - AVER(SplayNodeRightChild(node) != NULL); - AVER((*testTree)(tree, SplayNodeRightChild(node), closureP, closureS)); + AVER(TreeHasRight(node)); + AVER((*testTree)(splay, TreeRight(node), closureP, closureS)); return CompareGREATER; } } -static Compare SplayFindLastCompare(void *key, SplayNode node) +static Compare SplayFindLastCompare(Tree node, TreeKey key) { SplayFindClosure closure; void *closureP; Size closureS; SplayTestNodeMethod testNode; SplayTestTreeMethod testTree; - SplayTree tree; + SplayTree splay; - AVERT(SplayNode, node); + AVERT(Tree, node); AVER(key != NULL); + /* Lift closure values into variables so that they aren't aliased by + calls to the test functions. */ closure = (SplayFindClosure)key; closureP = closure->p; closureS = closure->s; testNode = closure->testNode; testTree = closure->testTree; - tree = closure->tree; + splay = closure->splay; - if (SplayNodeRightChild(node) != NULL && - (*testTree)(tree, SplayNodeRightChild(node), closureP, closureS)) { + if (TreeHasRight(node) && + (*testTree)(splay, TreeRight(node), closureP, closureS)) { return CompareGREATER; - } else if ((*testNode)(tree, node, closureP, closureS)) { + } else if ((*testNode)(splay, node, closureP, closureS)) { return CompareEQUAL; } else { - AVER(SplayNodeLeftChild(node) != NULL); - AVER((*testTree)(tree, SplayNodeLeftChild(node), closureP, closureS)); + AVER(TreeHasLeft(node)); + AVER((*testTree)(splay, TreeLeft(node), closureP, closureS)); return CompareLESS; } } @@ -874,125 +1138,104 @@ static Compare SplayFindLastCompare(void *key, SplayNode node) * 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 the * arbitrary closures closureP and closureS. */ -Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, - SplayTestNodeMethod testNode, - SplayTestTreeMethod testTree, - void *closureP, Size closureS) +Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, + SplayTestNodeMethod testNode, + SplayTestTreeMethod testTree, + void *closureP, Size closureS) { - SplayNode node; SplayFindClosureStruct closureStruct; + Compare cmp; AVER(nodeReturn != NULL); - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); AVER(FUNCHECK(testNode)); AVER(FUNCHECK(testTree)); - node = SplayTreeRoot(tree); - - if (node == NULL || !(*testTree)(tree, node, closureP, closureS)) + if (SplayTreeIsEmpty(splay) || + !testTree(splay, SplayTreeRoot(splay), closureP, closureS)) return FALSE; /* no suitable nodes in tree */ closureStruct.p = closureP; closureStruct.s = closureS; closureStruct.testNode = testNode; closureStruct.testTree = testTree; - closureStruct.tree = tree; + closureStruct.splay = splay; - if (SplaySplay(&node, tree, (void *)&closureStruct, - &SplayFindFirstCompare)) { - *nodeReturn = node; - return TRUE; - } else { - return FALSE; - } + cmp = SplaySplay(splay, &closureStruct, SplayFindFirstCompare); + AVER(cmp == CompareEQUAL); + + *nodeReturn = SplayTreeRoot(splay); + return TRUE; } /* SplayFindLast -- As SplayFindFirst but in reverse address order */ -Bool SplayFindLast(SplayNode *nodeReturn, SplayTree tree, - SplayTestNodeMethod testNode, - SplayTestTreeMethod testTree, - void *closureP, Size closureS) +Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, + SplayTestNodeMethod testNode, + SplayTestTreeMethod testTree, + void *closureP, Size closureS) { - SplayNode node; SplayFindClosureStruct closureStruct; + Compare cmp; AVER(nodeReturn != NULL); - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); AVER(FUNCHECK(testNode)); AVER(FUNCHECK(testTree)); - node = SplayTreeRoot(tree); - - if (node == NULL || !(*testTree)(tree, node, closureP, closureS)) + if (SplayTreeIsEmpty(splay) || + !testTree(splay, SplayTreeRoot(splay), closureP, closureS)) return FALSE; /* no suitable nodes in tree */ closureStruct.p = closureP; closureStruct.s = closureS; closureStruct.testNode = testNode; closureStruct.testTree = testTree; - closureStruct.tree = tree; + closureStruct.splay = splay; - if (SplaySplay(&node, tree, (void *)&closureStruct, - &SplayFindLastCompare)) { - *nodeReturn = node; - return TRUE; - } else { - return FALSE; - } + cmp = SplaySplay(splay, &closureStruct, SplayFindLastCompare); + AVER(cmp == CompareEQUAL); + + *nodeReturn = SplayTreeRoot(splay); + return TRUE; } -/* SplayRoot -- return the root node of the tree */ - -Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree) -{ - SplayNode node; - - AVER(nodeReturn != NULL); - AVERT(SplayTree, tree); - - node = SplayTreeRoot(tree); - if (node == NULL) - return FALSE; - else { - *nodeReturn = node; - return TRUE; - } -} - - -/* SplayNodeRefresh -- Updates the client property that has changed at a node +/* 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. This may change. + * 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 tree, SplayNode node, void *key) +void SplayNodeRefresh(SplayTree splay, Tree node) { - Bool b; - SplayNode node2; + Compare cmp; - AVERT(SplayTree, tree); - AVERT(SplayNode, node); + AVERT(SplayTree, splay); + AVERT(Tree, node); + AVER(!SplayTreeIsEmpty(splay)); /* must contain node, at least */ + AVER(SplayHasUpdate(splay)); /* otherwise, why call? */ - b = SplaySplay(&node2, tree, key, tree->compare); - AVER(b); - AVER(node == node2); + cmp = SplaySplay(splay, splay->nodeKey(node), splay->compare); + AVER(cmp == CompareEQUAL); + AVER(SplayTreeRoot(splay) == node); - (*tree->updateNode)(tree, node, SplayNodeLeftChild(node), - SplayNodeRightChild(node)); + splay->updateNode(splay, node); } @@ -1001,24 +1244,24 @@ void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key) * See . */ -Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, +Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe) { Res res; #if defined(AVER_AND_CHECK) - if (!SplayTreeCheck(tree)) return ResFAIL; + if (!SplayTreeCheck(splay)) return ResFAIL; if (stream == NULL) return ResFAIL; if (!FUNCHECK(nodeDescribe)) return ResFAIL; #endif res = WriteF(stream, - "Splay $P {\n", (WriteFP)tree, - " compare $F\n", (WriteFF)tree->compare, + "Splay $P {\n", (WriteFP)splay, + " compare $F\n", (WriteFF)splay->compare, NULL); if (res != ResOK) return res; - if (SplayTreeRoot(tree) != NULL) { - res = SplayNodeDescribe(SplayTreeRoot(tree), stream, nodeDescribe); + if (SplayTreeRoot(splay) != TreeEMPTY) { + res = SplayNodeDescribe(SplayTreeRoot(splay), stream, nodeDescribe); if (res != ResOK) return res; } @@ -1029,7 +1272,7 @@ Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/splay.h b/mps/code/splay.h index 8fa31af7fd3..7d1eaff8999 100644 --- a/mps/code/splay.h +++ b/mps/code/splay.h @@ -10,75 +10,67 @@ #define splay_h #include "mpmtypes.h" /* for Res, etc. */ +#include "tree.h" typedef struct SplayTreeStruct *SplayTree; -typedef struct SplayNodeStruct *SplayNode; -typedef unsigned Compare; -typedef Compare (*SplayCompareMethod)(void *key, SplayNode node); -typedef Bool (*SplayTestNodeMethod)(SplayTree tree, SplayNode node, - void *closureP, Size closureS); -typedef Bool (*SplayTestTreeMethod)(SplayTree tree, SplayNode node, - void *closureP, Size closureS); -typedef void (*SplayUpdateNodeMethod)(SplayTree tree, SplayNode node, - SplayNode leftChild, - SplayNode rightChild); -typedef Res (*SplayNodeDescribeMethod)(SplayNode node, mps_lib_FILE *stream); -enum { - CompareLESS = 1, - CompareEQUAL, - CompareGREATER -}; +typedef Bool (*SplayTestNodeMethod)(SplayTree splay, Tree node, + void *closureP, Size closureS); +typedef Bool (*SplayTestTreeMethod)(SplayTree splay, Tree node, + void *closureP, Size closureS); +typedef Res (*SplayNodeDescribeMethod)(Tree node, mps_lib_FILE *stream); + +typedef void (*SplayUpdateNodeMethod)(SplayTree splay, Tree node); +extern void SplayTrivUpdate(SplayTree splay, Tree node); + +#define SplayTreeSig ((Sig)0x5195B1A1) /* SIGnature SPLAY */ typedef struct SplayTreeStruct { - SplayCompareMethod compare; + Sig sig; + TreeCompare compare; + TreeKeyMethod nodeKey; SplayUpdateNodeMethod updateNode; - SplayNode root; + Tree root; } SplayTreeStruct; -typedef struct SplayNodeStruct { - SplayNode left; - SplayNode right; -} SplayNodeStruct; +#define SplayTreeRoot(splay) RVALUE((splay)->root) +#define SplayTreeIsEmpty(splay) (SplayTreeRoot(splay) == TreeEMPTY) - -extern Bool SplayTreeCheck(SplayTree tree); -extern Bool SplayNodeCheck(SplayNode node); -extern void SplayTreeInit(SplayTree tree, SplayCompareMethod compare, +extern Bool SplayTreeCheck(SplayTree splay); +extern void SplayTreeInit(SplayTree splay, + TreeCompare compare, + TreeKeyMethod nodeKey, SplayUpdateNodeMethod updateNode); -extern void SplayNodeInit(SplayNode node); -extern void SplayNodeFinish(SplayNode node); -extern void SplayTreeFinish(SplayTree tree); +extern void SplayTreeFinish(SplayTree splay); -extern Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key); -extern Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key); +extern Bool SplayTreeInsert(SplayTree splay, Tree node); +extern Bool SplayTreeDelete(SplayTree splay, Tree node); -extern Res SplayTreeSearch(SplayNode *nodeReturn, - SplayTree tree, void *key ); -extern Res SplayTreeNeighbours(SplayNode *leftReturn, - SplayNode *rightReturn, - SplayTree tree, void *key); +extern Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key); -extern SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey); -extern SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, - void *oldKey); +extern Bool SplayTreeNeighbours(Tree *leftReturn, + Tree *rightReturn, + SplayTree splay, TreeKey key); -extern Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, +extern Tree SplayTreeFirst(SplayTree splay); +extern Tree SplayTreeNext(SplayTree splay, TreeKey oldKey); + +extern Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, Size closureS); -extern Bool SplayFindLast(SplayNode *nodeReturn, SplayTree tree, +extern Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, Size closureS); -extern void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key); +extern void SplayNodeRefresh(SplayTree splay, Tree node); -extern Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, +extern Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe); -extern Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree); +extern void SplayDebugUpdate(SplayTree splay, Tree tree); #endif /* splay_h */ diff --git a/mps/code/tree.c b/mps/code/tree.c new file mode 100644 index 00000000000..9e19ef6edf9 --- /dev/null +++ b/mps/code/tree.c @@ -0,0 +1,533 @@ +/* tree.c: BINARY TREE IMPLEMENTATION + * + * $Id$ + * Copyright (C) 2014 Ravenbrook Limited. See end of file for license. + * + * Simple binary trees with utilities, for use as building blocks. + * Keep it simple, like Rings (see ring.h). + * + * The performance requirements on tree implementation will depend on + * how each individual function is applied in the MPS. + * + * .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. TODO: Design documentation for this + * requirement, meanwhile see job003651 and job003640. + */ + +#include "tree.h" +#include "mpm.h" + +SRCID(tree, "$Id$"); + + +Bool TreeCheck(Tree tree) +{ + if (tree != TreeEMPTY) { + CHECKL(tree != NULL); + CHECKL(tree->left == TreeEMPTY || tree->left != NULL); + CHECKL(tree->right == TreeEMPTY || tree->right != NULL); + } + return TRUE; +} + +Bool TreeCheckLeaf(Tree tree) +{ + CHECKL(TreeCheck(tree)); + CHECKL(tree != TreeEMPTY); + CHECKL(tree->left == TreeEMPTY); + CHECKL(tree->right == TreeEMPTY); + return TRUE; +} + + +/* TreeDebugCount -- 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 .note.stack. + */ + +static Count TreeDebugCountBetween(Tree node, + TreeCompare compare, TreeKeyMethod key, + TreeKey min, TreeKey max) +{ + if (node == TreeEMPTY) + return 0; + AVERT(Tree, node); + AVER(min == NULL || compare(node, min) != CompareGREATER); + AVER(max == NULL || compare(node, max) != CompareLESS); + return TreeDebugCountBetween(TreeLeft(node), compare, key, min, key(node)) + + 1 + + TreeDebugCountBetween(TreeRight(node), compare, key, key(node), max); +} + +Count TreeDebugCount(Tree tree, TreeCompare compare, TreeKeyMethod key) +{ + AVERT(Tree, tree); + return TreeDebugCountBetween(tree, compare, key, NULL, NULL); +} + + +#if 0 /* This code is not currently in use in the MPS */ + +/* TreeFind -- search for a node matching the key + * + * If a matching node is found, sets *treeReturn to that node and returns + * CompareEQUAL. Otherwise returns values useful for inserting a node with + * the key. If the tree is empty, returns CompareEQUAL and sets *treeReturn + * to NULL. Otherwise, sets *treeReturn to a potential parent for the new + * node and returns CompareLESS if the new node should be its left child, + * or CompareGREATER for its right. + */ + +Compare TreeFind(Tree *treeReturn, Tree root, TreeKey key, TreeCompare compare) +{ + Tree node, parent; + Compare cmp = CompareEQUAL; + + AVERT(Tree, root); + AVER(treeReturn != NULL); + AVER(FUNCHECK(compare)); + /* key is arbitrary */ + + parent = NULL; + node = root; + while (node != TreeEMPTY) { + parent = node; + cmp = compare(node, key); + switch (cmp) { + case CompareLESS: + node = node->left; + break; + case CompareEQUAL: + *treeReturn = node; + return cmp; + case CompareGREATER: + node = node->right; + break; + default: + NOTREACHED; + *treeReturn = NULL; + return cmp; + } + } + + *treeReturn = parent; + return cmp; +} + + +/* TreeInsert -- insert a node into a tree + * + * If the key doesn't exist in the tree, inserts a node as a leaf of the + * tree, returning the resulting tree in *treeReturn, and returns TRUE. + * Otherwise, *treeReturn points to the existing matching node, the tree + * is not modified, and returns FALSE. + */ + +Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, + TreeKey key, TreeCompare compare) +{ + Tree parent; + Compare cmp; + + AVER(treeReturn != NULL); + AVER(Tree, root); + AVER(TreeCheckLeaf(node)); + AVER(FUNCHECK(compare)); + /* key is arbitrary */ + + cmp = TreeFind(&parent, root, key, compare); + switch (cmp) { + case CompareLESS: + parent->left = node; + break; + case CompareEQUAL: + if (parent != NULL) { + *treeReturn = parent; + return FALSE; + } + AVER(root == TreeEMPTY); + root = node; + break; + case CompareGREATER: + parent->right = node; + break; + default: + NOTREACHED; + *treeReturn = NULL; + return FALSE; + } + + *treeReturn = root; + return TRUE; +} + + +/* TreeTraverseMorris -- traverse tree inorder in constant space + * + * The tree may not be accessed or modified during the traversal, and + * the traversal must complete in order to repair the tree. + * + * The visitor should return FALSE to terminate the traversal early, + * in which case FALSE is returned. + * + * TreeTraverse is generally superior if comparisons are cheap, but + * TreeTraverseMorris does not require any comparison function. + * + * + * + * Joseph M. Morris (1979). "Traversing Binary Trees Simply and Cheaply". + * Information Processing Letters 9:5 pp. 197–200. + */ + +Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, + void *closureP, Size closureS) +{ + Tree node; + Bool visiting = TRUE; + + AVERT(Tree, tree); + AVER(FUNCHECK(visit)); + /* closureP, closureS arbitrary */ + + node = tree; + while (node != TreeEMPTY) { + if (node->left == TreeEMPTY) { + if (visiting) + visiting = visit(node, closureP, closureS); + node = node->right; + } else { + Tree pre = node->left; + for (;;) { + if (pre->right == TreeEMPTY) { + pre->right = node; + node = node->left; + break; + } + if (pre->right == node) { + pre->right = TreeEMPTY; + if (visiting) + visiting = visit(node, closureP, closureS); + else if (node == tree) + return FALSE; + node = node->right; + break; + } + pre = pre->right; + } + } + } + + return visiting; +} + +#endif /* not currently in use */ + + +/* TreeTraverse -- traverse tree in-order using pointer reversal + * + * The tree may not be accessed or modified during the traversal, and + * the traversal must complete in order to repair the tree. + * + * The visitor should return FALSE to terminate the traversal early, + * in which case FALSE is returned. + * + * TreeTraverseMorris is an alternative when no cheap comparison is available. + */ + +static Tree stepDownLeft(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree child = TreeLeft(node); + TreeSetLeft(node, parent); + *parentIO = node; + return child; +} + +static Tree stepDownRight(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree child = TreeRight(node); + TreeSetRight(node, parent); + *parentIO = node; + return child; +} + +static Tree stepUpRight(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree grandparent = TreeLeft(parent); + TreeSetLeft(parent, node); + *parentIO = grandparent; + return parent; +} + +static Tree stepUpLeft(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree grandparent = TreeRight(parent); + TreeSetRight(parent, node); + *parentIO = grandparent; + return parent; +} + +Bool TreeTraverse(Tree tree, + TreeCompare compare, + TreeKeyMethod key, + TreeVisitor visit, void *closureP, Size closureS) +{ + Tree parent, node; + + AVERT(Tree, tree); + AVER(FUNCHECK(visit)); + /* closureP, closureS arbitrary */ + + parent = TreeEMPTY; + node = tree; + + if (node == TreeEMPTY) + return TRUE; + +down: + if (TreeHasLeft(node)) { + node = stepDownLeft(node, &parent); + AVER(compare(parent, key(node)) == CompareLESS); + goto down; + } + if (!visit(node, closureP, closureS)) + goto abort; + if (TreeHasRight(node)) { + node = stepDownRight(node, &parent); + AVER(compare(parent, key(node)) != CompareLESS); + goto down; + } + +up: + if (parent == TreeEMPTY) + return TRUE; + if (compare(parent, key(node)) != CompareLESS) { + node = stepUpLeft(node, &parent); + goto up; + } + node = stepUpRight(node, &parent); + if (!visit(node, closureP, closureS)) + goto abort; + if (!TreeHasRight(node)) + goto up; + node = stepDownRight(node, &parent); + goto down; + +abort: + if (parent == TreeEMPTY) + return FALSE; + if (compare(parent, key(node)) != CompareLESS) + node = stepUpLeft(node, &parent); + else + node = stepUpRight(node, &parent); + goto abort; +} + + +/* TreeRotateLeft -- Rotate right child edge of node + * + * Rotates node, right child of node, and left child of right + * child of node, leftwards in the order stated. Preserves tree + * ordering. + */ + +void TreeRotateLeft(Tree *treeIO) +{ + Tree tree, right; + + AVER(treeIO != NULL); + tree = *treeIO; + AVERT(Tree, tree); + right = TreeRight(tree); + AVERT(Tree, right); + + TreeSetRight(tree, TreeLeft(right)); + TreeSetLeft(right, tree); + + *treeIO = right; +} + + +/* TreeRotateRight -- Rotate left child edge of node + * + * Rotates node, left child of node, and right child of left + * child of node, leftwards in the order stated. Preserves tree + * ordering. + */ + +void TreeRotateRight(Tree *treeIO) { + Tree tree, left; + + AVER(treeIO != NULL); + tree = *treeIO; + AVERT(Tree, tree); + left = TreeLeft(tree); + AVERT(Tree, left); + + TreeSetLeft(*treeIO, TreeRight(left)); + TreeSetRight(left, *treeIO); + + *treeIO = left; +} + + +/* TreeReverseLeftSpine -- reverse the pointers on the right spine + * + * Descends the left spine of a tree, updating each node's left child + * to point to its parent instead. The root's left child is set to + * TreeEMPTY. Returns the leftmost child, or TreeEMPTY if the tree + * was empty. + */ + +Tree TreeReverseLeftSpine(Tree tree) +{ + Tree node, parent; + + AVERT(Tree, tree); + + parent = TreeEMPTY; + node = tree; + while (node != TreeEMPTY) { + Tree child = TreeLeft(node); + TreeSetLeft(node, parent); + parent = node; + node = child; + } + + return parent; +} + + +/* TreeReverseRightSpine -- reverse the pointers on the right spine + * + * Descends the right spine of a tree, updating each node's right child + * to point to its parent instead. The root's right child is set to + * TreeEMPTY. Returns the rightmost child or TreeEMPTY if the tree + * was empty. + */ + +Tree TreeReverseRightSpine(Tree tree) +{ + Tree node, parent; + + AVERT(Tree, tree); + + parent = TreeEMPTY; + node = tree; + while (node != TreeEMPTY) { + Tree child = TreeRight(node); + TreeSetRight(node, parent); + parent = node; + node = child; + } + + return parent; +} + + +#if 0 /* This code is currently not in use in the MPS */ + + +/* TreeToVine -- unbalance a tree into a single right spine */ + +Count TreeToVine(Tree *link) +{ + Count count = 0; + + AVER(link != NULL); + AVERT(Tree, *link); + + while (*link != TreeEMPTY) { + while (TreeHasLeft(*link)) + TreeRotateRight(link); + link = &((*link)->right); + ++count; + } + + return count; +} + + +/* TreeBalance -- rebalance a tree + * + * Linear time, constant space rebalance. + * + * Quentin F. Stout and Bette L. Warren, + * "Tree Rebalancing in Optimal Time and Space", + * Communications of the ACM, Vol. 29, No. 9 (September 1986), p. 902-908 + */ + +void TreeBalance(Tree *treeIO) +{ + Count depth; + + AVER(treeIO != NULL); + AVERT(Tree, *treeIO); + + depth = TreeToVine(treeIO); + + if (depth > 2) { + Count n = depth - 1; + do { + Count m = n / 2, i; + Tree *link = treeIO; + for (i = 0; i < m; ++i) { + TreeRotateLeft(link); + link = &((*link)->right); + } + n = n - m - 1; + } while (n > 1); + } +} + + +#endif /* not currently in use in the MPS */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * 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. + */ diff --git a/mps/code/tree.h b/mps/code/tree.h new file mode 100644 index 00000000000..69ee841d3c3 --- /dev/null +++ b/mps/code/tree.h @@ -0,0 +1,166 @@ +/* tree.h: BINARY TREE HEADER + * + * $Id$ + * Copyright (C) 2014 Ravenbrook Limited. See end of file for license. + * + * Simple binary trees with utilities, for use as building blocks. + * Keep it simple, like Rings (see ring.h). + */ + +#ifndef tree_h +#define tree_h + +#include "check.h" +#include "mpmtypes.h" + + +/* TreeStruct -- binary tree structure + * + * The tree structure is used in a field in other structures in order + * to link them together in a binary tree. + */ + +typedef struct TreeStruct *Tree; +typedef struct TreeStruct { + Tree left, right; +} TreeStruct; + + +/* TreeKey and TreeCompare -- ordered binary trees + * + * Binary trees are almost always ordered, and these types provide the + * abstraction for ordering. A TreeCompare method returns whether a key + * is less than, equal to, or greater than the key in a tree node. A + * TreeKeyMethod extracts a key from a node, depending on how TreeStruct + * is embedded within its parent structure. + */ + +typedef void *TreeKey; +typedef Compare (*TreeCompare)(Tree tree, TreeKey key); +typedef TreeKey (*TreeKeyMethod)(Tree tree); + + +/* TreeEMPTY -- the empty tree + * + * TreeEMPTY is the tree with no nodes, and hence unable to satisfy its + * olfactory senses. Empty trees should not be represented with NULL, + * as this is ambiguous. However, TreeEMPTY is in fact a null pointer for + * performance. To check whether you have it right, try temporarily + * defining TreeEMPTY to (Tree)1 or similar. + */ + +#define TreeEMPTY ((Tree)0) + +extern Bool TreeCheck(Tree tree); +extern Bool TreeCheckLeaf(Tree tree); +extern Count TreeDebugCount(Tree tree, TreeCompare compare, TreeKeyMethod key); + +#define TreeInit(tree) \ + BEGIN \ + Tree _tree = (tree); \ + AVER(_tree != NULL); \ + _tree->left = TreeEMPTY; \ + _tree->right = TreeEMPTY; \ + AVERT(Tree, _tree); \ + END + +#define TreeFinish(tree) \ + BEGIN \ + Tree _tree = (tree); \ + AVERT(Tree, _tree); \ + END + +#define TREE_ELT(type, field, node) \ + PARENT(type ## Struct, field, node) + +#define TreeLeft(tree) RVALUE((tree)->left) + +#define TreeRight(tree) RVALUE((tree)->right) + +#define TreeSetLeft(tree, child) \ + BEGIN \ + (tree)->left = (child); \ + END + +#define TreeSetRight(tree, child) \ + BEGIN \ + (tree)->right = (child); \ + END + +#define TreeClearLeft(tree) \ + BEGIN \ + (tree)->left = TreeEMPTY; \ + END + +#define TreeClearRight(tree) \ + BEGIN \ + (tree)->right = TreeEMPTY; \ + END + +#define TreeHasLeft(tree) (TreeLeft(tree) != TreeEMPTY) +#define TreeHasRight(tree) (TreeRight(tree) != TreeEMPTY) + +extern Compare TreeFind(Tree *treeReturn, Tree root, + TreeKey key, TreeCompare compare); + +extern Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, + TreeKey key, TreeCompare compare); + +typedef Bool TreeVisitor(Tree tree, void *closureP, Size closureS); +extern Bool TreeTraverse(Tree tree, + TreeCompare compare, + TreeKeyMethod key, + TreeVisitor visit, void *closureP, Size closureS); +extern Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, + void *closureP, Size closureS); + +extern void TreeRotateLeft(Tree *nodeIO); +extern void TreeRotateRight(Tree *nodeIO); +extern Tree TreeReverseLeftSpine(Tree tree); +extern Tree TreeReverseRightSpine(Tree tree); +extern Count TreeToVine(Tree *treeIO); +extern void TreeBalance(Tree *treeIO); + + +#endif /* tree_h */ + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * 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. + */ diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 8f91115a09f..8eb1a91c2a8 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -21,8 +21,8 @@ implementation. _`.readership`: This document is intended for any MM developer. -_`.source`: The primary sources for this design are paper.st85(0) and -paper.sleator96(0). Also as CBS is a client, design.mps.cbs. As +_`.source`: The primary sources for this design are [ST85]_ and +[Sleator96]_. Also as CBS is a client, design.mps.cbs. As PoolMVFF is an indirect client, design.mps.poolmvff(1). Also, as PoolMV2 is an (obsolescent?) indirect client, design.mps.poolmv2. @@ -153,22 +153,23 @@ the root of the splay tree. It is intended that the `.usage.client-tree`_ for an example). No convenience functions are provided for allocation or deallocation. -``typedef struct SplayNodeStruct SplayNodeStruct`` -``typedef struct SplayNodeStruct *SplayNode`` +``typedef struct TreeStruct TreeStruct`` +``typedef struct TreeStruct *Tree`` -_`.type.splay.node`: ``SplayNode`` is the type of a node of the splay -tree. ``SplayNodeStruct`` contains no fields to store the key +_`.type.splay.node`: ``Tree`` is the type of a binary tree, used as the +representation of the nodes of the splay tree. +``Tree`` contains no fields to store the key associated with the node, or the client property. Again, it is -intended that the ``SplayNodeStruct`` can be embedded in another +intended that the ``TreeStruct`` can be embedded in another structure, and that this is how the association will be made (see `.usage.client-node`_ for an example). No convenience functions are provided for allocation or deallocation. -``typedef Compare (*SplayCompareMethod)(void *key, SplayNode node)`` +``typedef Compare (*TreeCompare)(Tree tree, TreeKey key)`` _`.type.splay.compare.method`: A function of type -``SplayCompareMethod`` is required to compare ``key`` with the key the -client associates with that splay tree node ``node``, and return the +``TreeCompare`` is required to compare ``key`` with the key the +client associates with that splay tree node ``tree``, and return the appropriate Compare value (see `.usage.compare`_ for an example). The function compares a key with a node, rather than a pair of keys or nodes as might seem more obvious. This is because the details of the @@ -176,7 +177,7 @@ mapping between nodes and keys is left to the client (see `.type.splay.node`_), and the splaying operations compare keys with nodes (see `.impl.splay`_). -``typedef Res (*SplayNodeDescribeMethod)(SplayNode node, mps_lib_FILE *stream)`` +``typedef Res (*SplayNodeDescribeMethod)(Tree tree, mps_lib_FILE *stream)`` _`.type.splay.node.describe.method`: A function of type ``SplayNodeDescribeMethod`` is required to write (via ``WriteF()``) a @@ -184,7 +185,7 @@ client-oriented representation of the splay node. The output should be non-empty, short, and without return characters. This is provided for debugging purposes only. -``typedef Bool (*SplayTestNodeMethod)(SplayTree tree, SplayNode node, void *closureP, unsigned long closureS)`` +``typedef Bool (*SplayTestNodeMethod)(SplayTree splay, Tree tree, void *closureP, unsigned long closureS)`` _`.type.splay.test.node.method`: A function of type ``SplayTestNodeMethod`` required to determine whether the node itself @@ -193,7 +194,7 @@ meets some client determined property (see `.prop`_ and ``closureS`` describe the environment for the function (see `.function.splay.find.first`_ and `.function.splay.find.last`_). -``typedef Bool (*SplayTestTreeMethod)(SplayTree tree, SplayNode node, void *closureP, unsigned long closureS)`` +``typedef Bool (*SplayTestTreeMethod)(SplayTree splay, Tree tree, void *closureP, unsigned long closureS)`` _`.type.splay.test.tree.method`: A function of type ``SplayTestTreeMethod`` is required to determine whether any of the @@ -206,14 +207,13 @@ return ``TRUE``. Parameters ``closureP`` and ``closureS`` describe the environment for the function (see `.function.splay.find.first`_ and `.function.splay.find.last`_). -``typedef void (*SplayUpdateNodeMethod)(SplayTree tree, SplayNode node, SplayNode leftChild, SplayNode rightChild)`` +``typedef void (*SplayUpdateNodeMethod)(SplayTree splay, Tree tree)`` _`.type.splay.update.node.method`: A function of type ``SplayUpdateNodeMethod`` is required to update any client datastructures associated with a node to maintain some client determined property (see `.prop`_) given that the children of the node -have changed. If the node does not have one or both children, then -``NULL`` will be passed as the relevant parameter. (See +have changed. (See `.usage.callback`_ for an example) @@ -228,19 +228,19 @@ methods invoked by the splay module (`.type.splay.compare.method`_, `.type.splay.update.node.method`_) do not call functions of the splay module. -``Bool SplayTreeCheck(SplayTree tree)`` +``Bool SplayTreeCheck(SplayTree splay)`` _`.function.splay.tree.check`: This is a check function for the SplayTree type (see guide.impl.c.adt.method.check & design.mps.check(0)). -``Bool SplayNodeCheck(SplayNode node)`` +``Bool SplayNodeCheck(Tree tree)`` _`.function.splay.node.check`: This is a check function for the -``SplayNode`` type (see guide.impl.c.adt.method.check & +``Tree`` type (see guide.impl.c.adt.method.check & design.mps.check(0)). -``void SplayTreeInit(SplayTree tree, SplayCompareMethod compare, SplayUpdateNodeMethod updateNode)`` +``void SplayTreeInit(SplayTree splay, SplayCompareMethod compare, SplayUpdateNodeMethod updateNode)`` _`.function.splay.tree.init`: This function initialises a ``SplayTree`` (see guide.impl.c.adt.method.init). It requires a @@ -249,85 +249,66 @@ _`.function.splay.tree.init`: This function initialises a implement a total ordering is undefined. It also requires an ``updateNode`` method, which will be used to keep client properties up to date when the tree structure changes; the value -``SplayTrivUpdateNode`` may be used for this method if there is no +``SplayTrivUpdate`` may be used for this method if there is no need to maintain client properties. (See `.usage.initialization`_ for an example use). -``void SplayTreeFinish(SplayTree tree)`` +``void SplayTreeFinish(SplayTree splay)`` _`.function.splay.tree.finish`: This function clears the fields of a ``SplayTree`` (see guide.impl.c.adt.method.finish). Note that it does -not attempt to finish or deallocate any associated ``SplayNode`` +not attempt to finish or deallocate any associated ``Tree`` objects; clients wishing to destroy a non-empty ``SplayTree`` must first explicitly descend the tree and call ``SplayNodeFinish()`` on each node from the bottom up. -``void SplayNodeInit(SplayNode node)`` - -_`.function.splay.node.init`: This function initialises a -``SplayNode`` (see guide.impl.c.adt.method.init). - -``void SplayNodeFinish(SplayNode node)`` - -_`.function.splay.node.finish`: This function clears the fields of a -``SplayNode`` (see guide.impl.c.adt.method.finish). Note that it does -not attempt to finish or deallocate any referenced ``SplayNode`` -objects (see.function.splay.tree.finish). - -``Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree)`` - -_`.function.splay.root`: This function returns the root node of the -tree, if any (see `.req.root`_). If the tree is empty, ``FALSE`` is -returned and ``*nodeReturn`` is not changed. Otherwise, ``TRUE`` is -returned and ``*nodeReturn`` is set to the root. - -``Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key)`` +``Bool SplayTreeInsert(SplayTree splay, Tree tree, void *key)`` _`.function.splay.tree.insert`: This function is used to insert into a splay tree a new node which is associated with the supplied key (see `.req.add`_). It first splays the tree at the key. If an attempt is made to insert a node that compares ``CompareEQUAL`` to an existing -node in the tree, then ``ResFAIL`` will be returned and the node will +node in the tree, then ``FALSE`` will be returned and the node will not be inserted. (See `.usage.insert`_ for an example use). -``Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key)`` +``Bool SplayTreeDelete(SplayTree splay, Tree tree, void *key)`` _`.function.splay.tree.delete`: This function is used to delete from a splay tree a node which is associated with the supplied key (see `.req.remove`_). If the tree does not contain the given node, or the given node does not compare ``CompareEQUAL`` with the given key, then -``ResFAIL`` will be returned, and the node will not be deleted. The +``FALSE`` will be returned, and the node will not be deleted. The function first splays the tree at the given key. (See `.usage.delete`_ for an example use). -``Res SplayTreeSearch(SplayNode *nodeReturn, SplayTree tree, void *key)`` +``Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, void *key)`` _`.function.splay.tree.search`: This function searches the splay tree for a node that compares ``CompareEQUAL`` to the given key (see -`.req.locate`_). It splays the tree at the key. It returns ``ResFAIL`` +`.req.locate`_). It splays the tree at the key. It returns ``FALSE`` if there is no such node in the tree, otherwise ``*nodeReturn`` will be set to the node. -``Res SplayTreeNeighbours(SplayNode *leftReturn, SplayNode *rightReturn, SplayTree tree, void *key)`` +``Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, SplayTree splay, void *key)`` _`.function.splay.tree.neighbours`: This function searches a splay tree for the two nodes that are the neighbours of the given key (see `.req.neighbours`_). It 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 ``NULL``. ``*rightReturn`` will +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 ``NULL``. The function returns -``ResFAIL`` if any node in the tree compares ``CompareEQUAL`` with the +neighbour exists; otherwise it will be ``TreeEMPTY``. The function returns +``FALSE`` if any node in the tree compares ``CompareEQUAL`` with the given key. (See `.usage.insert`_ for an example use). -``SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey)`` +``Tree SplayTreeFirst(SplayTree splay, void *zeroKey)`` _`.function.splay.tree.first`: This function splays the tree at the first node, and returns that node (see `.req.iterate`_). The supplied key should compare ``CompareLESS`` with all nodes in the tree. It will -return ``NULL`` if the tree has no nodes. +return ``TreeEMPTY`` if the tree has no nodes. -``SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey)`` +``Tree SplayTreeNext(SplayTree splay, Tree oldNode, void *oldKey)`` _`.function.splay.tree.next`: This function receives a node and key and returns the successor node to that node (see `.req.iterate`_). @@ -342,14 +323,14 @@ tree was previously beneficially balanced for a small working set of accesses, then this local optimization will be lost. (see `.future.parent`_). -``Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe)`` +``Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe)`` _`.function.splay.tree.describe`: This function prints (using ``WriteF``) to the stream a textual representation of the given splay tree, using ``nodeDescribe`` to print client-oriented representations of the nodes (see `.req.debug`_). -``Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)`` +``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)`` _`.function.splay.find.first`: ``SplayFindFirst()`` finds the first node in the tree that satisfies some client property (as determined by the @@ -360,7 +341,7 @@ closure environments. If there is no satisfactory node, then ``FALSE`` is returned, otherwise ``*nodeReturn`` is set to the node. (See `.usage.delete`_ for an example use). -``Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)`` +``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)`` _`.function.splay.find.last`: ``SplayFindLast()`` finds the last node in the tree that satisfies some client property (as determined by the @@ -370,7 +351,7 @@ the ``testNode`` and ``testTree`` methods which may use the values as closure environments. If there is no satisfactory node, then ``FALSE`` is returned, otherwise ``*nodeReturn`` is set to the node. -``void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key)`` +``void SplayNodeRefresh(SplayTree splay, Tree tree, void *key)`` _`.function.splay.node.refresh`: ``SplayNodeRefresh()`` must be called whenever the client property (see `.prop`_) at a node changes (see @@ -462,10 +443,10 @@ _`.usage.client-tree`: Tree structure to embed a ``SplayTree`` (see /* no obvious client fields for this simple example */ } FreeTreeStruct; -_`.usage.client-node`: Node structure to embed a SplayNode (see `.type.splay.node`_):: +_`.usage.client-node`: Node structure to embed a Tree (see `.type.splay.node`_):: typedef struct FreeBlockStruct { - SplayNodeStruct splayNode; /* embedded splay node */ + TreeStruct treeStruct; /* embedded splay node */ Addr base; /* base address of block is also the key */ Size size; /* size of block is also the client property */ Size maxSize; /* cached value for maximum size in subtree */ @@ -474,26 +455,25 @@ _`.usage.client-node`: Node structure to embed a SplayNode (see `.type.splay.nod _`.usage.callback`: updateNode callback method (see `.type.splay.update.node.method`_):: - void FreeBlockUpdateNode(SplayTree tree, SplayNode node, - SplayNode leftChild, SplayNode rightChild) + void FreeBlockUpdateNode(SplayTree splay, Tree tree) { /* Compute the maximum size of any block in this subtree. */ /* The value to cache is the maximum of the size of this block, */ /* the cached value for the left subtree (if any) and the cached */ /* value of the right subtree (if any) */ - FreeBlock freeNode = FreeBlockOfSplayNode(node); + FreeBlock freeNode = FreeBlockOfSplayNode(tree); Size maxSize = freeNode.size; - if(leftChild != NULL) { - FreeBlock leftNode = FreeBlockOfSplayNode(leftChild); + if (TreeHasLeft(tree)) { + FreeBlock leftNode = FreeBlockOfSplayNode(TreeLeft(tree)); if(leftNode.maxSize > maxSize) maxSize = leftNode->maxSize; } - if(rightChild != NULL) { - FreeBlock rightNode = FreeBlockOfSplayNode(rightChild); + if (TreeHasRight(tree)) { + FreeBlock rightNode = FreeBlockOfSplayNode(TreeRight(tree)); if(rightNode.maxSize > maxSize) maxSize = rightNode->maxSize; } @@ -503,9 +483,9 @@ _`.usage.callback`: updateNode callback method (see _`.usage.compare`: Comparison function (see `.type.splay.compare.method`_):: - Compare FreeBlockCompare(void *key, SplayNode node) { + Compare FreeBlockCompare(Tree tree, TreeKey key) { Addr base1, base2, limit2; - FreeBlock freeNode = FreeBlockOfSplayNode(node); + FreeBlock freeNode = FreeBlockOfSplayNode(tree); base1 = (Addr *)key; base2 = freeNode->base; @@ -522,27 +502,27 @@ _`.usage.compare`: Comparison function (see `.type.splay.compare.method`_):: _`.usage.test.tree`: Test tree function (see `.type.splay.test.tree.method`_):: - Bool FreeBlockTestTree(SplayTree tree, SplayNode node + Bool FreeBlockTestTree(SplayTree splay, Tree tree void *closureP, unsigned long closureS) { /* Closure environment has wanted size as value of closureS. */ /* Look at the cached value for the node to see if any */ /* blocks in the subtree are big enough. */ Size size = (Size)closureS; - FreeBlock freeNode = FreeBlockOfSplayNode(node); + FreeBlock freeNode = FreeBlockOfSplayNode(tree); return freeNode->maxSize >= size; } _`.usage.test.node`: Test node function (see `.type.splay.test.node.method`_):: - Bool FreeBlockTestNode(SplayTree tree, SplayNode node + Bool FreeBlockTestNode(SplayTree splay, Tree tree void *closureP, unsigned long closureS) { /* Closure environment has wanted size as value of closureS. */ /* Look at the size of the node to see if is big enough. */ Size size = (Size)closureS; - FreeBlock freeNode = FreeBlockOfSplayNode(node); + FreeBlock freeNode = FreeBlockOfSplayNode(tree); return freeNode->size >= size; } @@ -559,7 +539,7 @@ tree, merging it with an existing block if possible:: void FreeTreeInsert(FreeTree tree, Addr base, Addr limit) { SplayTree splayTree = &tree->splayTree; - SplayNode leftNeighbour, rightNeighbour; + Tree leftNeighbour, rightNeighbour; void *key = (void *)base; /* use the base of the block as the key */ Res res; @@ -570,7 +550,7 @@ tree, merging it with an existing block if possible:: /* Look to see if the neighbours are contiguous. */ - if (leftNeighbour != NULL && + if (leftNeighbour != TreeEMPTY && FreeBlockLimitOfSplayNode(leftNeighbour) == base) { /* Inserted block is contiguous with left neighbour, so merge it. */ /* The client housekeeping is left as an exercise to the reader. */ @@ -578,7 +558,7 @@ tree, merging it with an existing block if possible:: /* property of the splay node. See `.function.splay.node.refresh`_ */ SplayNodeRefresh(tree, leftNeighbour, key); - } else if (rightNeighbour != NULL && + } else if (rightNeighbour != TreeEMPTY && FreeBlockBaseOfSplayNode(rightNeighbour) == limit) { /* Inserted block is contiguous with right neighbour, so merge it. */ /* The client housekeeping is left as an exercise to the reader. */ @@ -607,7 +587,7 @@ block:: Bool FreeTreeAllocate(Addr *baseReturn, Size *sizeReturn, FreeTree tree, Size size) { SplayTree splayTree = &tree->splayTree; - SplayNode splayNode; + Tree splayNode; Bool found; /* look for the first node of at least the given size. */ @@ -663,7 +643,7 @@ descended, whilst looking for the node. _`.impl.splay`: The key to the operation of the splay tree is the internal function ``SplaySplay()``. It searches the tree for a node -with a given key and returns whether it suceeded. In the process, it +with a given key. In the process, it brings the found node, or an arbitrary neighbour if not found, to the root of the tree. This "bring-to-root" operation is performed top-down during the search, and it is not the simplest possible bring-to-root @@ -878,7 +858,7 @@ protocol error. The cases it doesn't handle will result in undefined behaviour and probably cause an ``AVER`` to fire. These are: _`.error.bad-pointer`: Passing an invalid pointer in place of a -``SplayTree`` or ``SplayNode``. +``SplayTree`` or ``Tree``. _`.error.bad-compare`: Initialising a ``SplayTree`` with a compare function that is not a valid compare function, or which doesn't @@ -893,10 +873,6 @@ _`.error.out-of-stack`: Stack exhaustion under ``SplayTreeDescribe()``. Future ------ -_`.future.tree`: It would be possible to split the splay tree module -into two: one that implements binary trees; and one that implements -splay trees on top of a binary tree. - _`.future.parent`: The iterator could be made more efficient (in an amortized sense) if it didn't splay at each node. To implement this (whilst meeting `.req.stack`_) we really need parent pointers from the @@ -908,10 +884,17 @@ and right-child, and the right-sibling/parent between right-sibling and parent. One could either use the comparator to make these distinctions, or steal some bits from the pointers. -_`.future.reverse`: The assembly phase could be made more efficient if -the link left and link right operations were modified to add to the -left and right trees with pointers reversed. This would remove the -need for the assembly phase to reverse them. + +References +---------- + +.. [ST85] "Self-Adjusting Binary Search Trees"; Daniel Dominic Sleator, + Robert Endre Tarjan; AT&T Bell Laboratories, Murray Hill, NJ; 1985-07; + Journal of the ACM, Vol. 32, Num. 3, pp. 652-686, July 1985; + . + +.. [Sleator96] "Splay Trees"; Daniel Dominic Sleator; CMU, 22/02/96; + CMU 15-211; . Document History @@ -928,6 +911,11 @@ Document History - 2002-06-07 RB_ Converted from MMInfo database design document. +- 2014-02-22 RB_ Fixing abuses of Res and ResFAIL. + +- 2014-03-11 RB_ Updating in response to code review. Removing + .future.tree and .future.reverse, both now implemented. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/