From 59334b5e4fac75a343ec7df2af7d176518fa56c9 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Sun, 23 Feb 2014 13:54:07 +0000 Subject: [PATCH 1/6] Updating copyright dates. Copied from Perforce Change: 184510 ServerID: perforce.ravenbrook.com --- mps/code/splay.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/code/splay.c b/mps/code/splay.c index 6002d0a1702..2779228c9e6 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -1,7 +1,7 @@ /* 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. @@ -1175,7 +1175,7 @@ Res SplayTreeDescribe(SplayTree splay, 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. * From f47100fd09ccd0ca6615dbc29a5b21df25e3b21c Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Sun, 23 Feb 2014 14:42:23 +0000 Subject: [PATCH 2/6] Permitting tree traversals to abort early. Copied from Perforce Change: 184511 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 4 ++-- mps/code/tree.c | 25 +++++++++++++++++-------- mps/code/tree.h | 6 ++++-- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index d4363f44475..45fd4aaa665 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -667,8 +667,8 @@ void CBSIterate(CBS cbs, CBSIterateMethod iterate, closure.iterate = iterate; closure.closureP = closureP; closure.closureS = closureS; - TreeTraverse(SplayTreeRoot(tree), tree->compare, tree->nodeKey, - CBSIterateVisit, &closure, 0); + (void)TreeTraverse(SplayTreeRoot(tree), tree->compare, tree->nodeKey, + CBSIterateVisit, &closure, 0); cbsLeave(cbs); return; diff --git a/mps/code/tree.c b/mps/code/tree.c index 277dbaf3772..59d492987ac 100644 --- a/mps/code/tree.c +++ b/mps/code/tree.c @@ -155,13 +155,16 @@ Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, * * * The tree may not be modified during the traversal, and the traversal - * must complete. TODO: Is there a way to abort early? + * must complete. + * + * TreeTraverse is generally superior if comparisons are cheap. */ -void TreeTraverseMorris(Tree tree, TreeVisitor visit, +Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, void *closureP, Size closureS) { Tree node; + Bool visiting = TRUE; AVER(TreeCheck(tree)); AVER(FUNCHECK(visit)); @@ -170,7 +173,8 @@ void TreeTraverseMorris(Tree tree, TreeVisitor visit, node = tree; while (node != TreeEMPTY) { if (node->left == TreeEMPTY) { - (void)visit(node, closureP, closureS); + if (visiting) + visiting = visit(node, closureP, closureS); node = node->right; } else { Tree pre = node->left; @@ -182,7 +186,10 @@ void TreeTraverseMorris(Tree tree, TreeVisitor visit, } if (pre->right == node) { pre->right = TreeEMPTY; - (void)visit(node, closureP, closureS); + if (visiting) + visiting = visit(node, closureP, closureS); + else if (node == tree) + return FALSE; node = node->right; break; } @@ -190,6 +197,8 @@ void TreeTraverseMorris(Tree tree, TreeVisitor visit, } } } + + return visiting; } @@ -231,7 +240,7 @@ static Tree stepUpLeft(Tree node, Tree *parentIO) return parent; } -void TreeTraverse(Tree tree, +Bool TreeTraverse(Tree tree, TreeCompare compare, TreeKeyMethod key, TreeVisitor visit, void *closureP, Size closureS) @@ -246,7 +255,7 @@ void TreeTraverse(Tree tree, node = tree; if (node == TreeEMPTY) - return; + return TRUE; down: if (TreeHasLeft(node)) { @@ -264,7 +273,7 @@ down: up: if (parent == TreeEMPTY) - return; + return TRUE; if (compare(parent, key(node)) != CompareLESS) { node = stepUpLeft(node, &parent); goto up; @@ -279,7 +288,7 @@ up: abort: if (parent == TreeEMPTY) - return; + return FALSE; if (compare(parent, key(node)) != CompareLESS) node = stepUpLeft(node, &parent); else diff --git a/mps/code/tree.h b/mps/code/tree.h index eaa917dca67..42aa135054c 100644 --- a/mps/code/tree.h +++ b/mps/code/tree.h @@ -2,6 +2,8 @@ * * $Id$ * Copyright (C) 2014 Ravenbrook Limited. See end of file for license. + * + * Simple binary trees, for use as building blocks. */ #ifndef tree_h @@ -77,11 +79,11 @@ extern Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, TreeKey key, TreeCompare compare); typedef Bool TreeVisitor(Tree tree, void *closureP, Size closureS); -extern void TreeTraverse(Tree tree, +extern Bool TreeTraverse(Tree tree, TreeCompare compare, TreeKeyMethod key, TreeVisitor visit, void *closureP, Size closureS); -extern void TreeTraverseMorris(Tree tree, TreeVisitor visit, +extern Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, void *closureP, Size closureS); extern void TreeRotateLeft(Tree *nodeIO); From 4ef9c96411f484e9705c16a3257f6ef3213a733b Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 26 Feb 2014 01:39:10 +0000 Subject: [PATCH 3/6] Moving and updating most of the usage documentation from the design document to the source code. design documentation should contain justification of design decisions, not a user manual. Copied from Perforce Change: 184547 ServerID: perforce.ravenbrook.com --- mps/code/splay.c | 165 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 41 deletions(-) diff --git a/mps/code/splay.c b/mps/code/splay.c index 2779228c9e6..a7ed45d2fe5 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -4,7 +4,8 @@ * 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: * @@ -34,6 +35,11 @@ SRCID(splay, "$Id$"); #define SplayHasUpdate(splay) ((splay)->updateNode != SplayTrivUpdate) +/* SplayTreeCheck -- check consistency of SplayTree + * + * See guide.impl.c.adt.check and design.mps.check. + */ + Bool SplayTreeCheck(SplayTree splay) { UNUSED(splay); @@ -46,6 +52,18 @@ Bool SplayTreeCheck(SplayTree splay) } +/* 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 may be passed, faster + * algorithms are chosen for splaying (FIXME: xref design). + */ + void SplayTreeInit(SplayTree splay, TreeCompare compare, TreeKeyMethod nodeKey, @@ -66,6 +84,14 @@ void SplayTreeInit(SplayTree splay, } +/* SplayTreeFinish -- finish a splay tree + * + * Does not attempt to descend or finish any tree nodes. + * + * TODO: Should probably fail on non-empty tree, so that client code is + * forced to decide what to do about that. + */ + void SplayTreeFinish(SplayTree splay) { AVERT(SplayTree, splay); @@ -218,30 +244,40 @@ static Tree SplayZagZag(Tree middle, Tree *leftLastIO, Tree leftPrev) } +/* SplayState -- the state of splaying between "split" and "assemble" + * + * Splaying is divided into two phases: splitting the tree into three, + * and then assembling a final tree. This allows for optimisation of + * certain operations, the key one being SplayTreeNeighbours, which is + * critical for coalescing memory blocks (see CBSInsert). + * + * Note that SplaySplitDown and SplaySplitRev use the trees slightly + * differently. SplaySplitRev does not provide "left" and "right", and + * "leftLast" and "rightFirst" are pointer-reversed spines. + */ + +typedef struct SplayStateStruct { + Tree middle; /* always non-empty, has the found node at the root */ + Tree left; /* nodes less than search key during split */ + Tree leftLast; /* rightmost node on right spine of "left" */ + Tree right; /* nodes greater than search key during split */ + Tree rightFirst; /* leftmost node on left spine of "right" */ +} SplayStateStruct, *SplayState; + + /* SplaySplitDown -- divide the tree around a key * * Split a tree into three according to a key and a comparison, * splaying nested left and right nodes. Preserves tree ordering. + * This is a top-down splay procedure, and does not use any recursion + * or require any parent pointers (see design.mps.impl.top-down). * * Returns cmp, the relationship of the root of the middle tree to the key, - * and a SplayState: - * middle -- the middle tree, with the nearest match for the key at the top - * leftTop -- a tree of nodes that were less than node - * leftLast -- the greatest node in leftTop (rightmost child) - * rightTop -- a tree of nodes that were greater than node - * rightFirst -- the least node in rightTop (leftmost child) + * and a SplayState. * - * Does *not* maintain client properties. See SplaySplitRev. + * Does *not* call update to maintain client properties. See SplaySplitRev. */ -typedef struct SplayStateStruct { - Tree middle; - Tree left; - Tree leftLast; - Tree right; - Tree rightFirst; -} SplayStateStruct, *SplayState; - static Compare SplaySplitDown(SplayStateStruct *stateReturn, SplayTree splay, TreeKey key, TreeCompare compare) { @@ -590,6 +626,8 @@ static void SplayAssembleRev(SplayTree splay, SplayState state) } +/* SplaySplit -- call SplaySplitDown or SplaySplitRev as appropriate */ + static Compare SplaySplit(SplayStateStruct *stateReturn, SplayTree splay, TreeKey key, TreeCompare compare) { @@ -599,6 +637,9 @@ static Compare SplaySplit(SplayStateStruct *stateReturn, return SplaySplitDown(stateReturn, splay, key, compare); } + +/* SplayAssemble -- call SplayAssembleDown or SplayAssembleRev as appropriate */ + static void SplayAssemble(SplayTree splay, SplayState state) { if (SplayHasUpdate(splay)) @@ -610,8 +651,11 @@ static void SplayAssemble(SplayTree splay, SplayState state) /* SplaySplay -- splay the tree around a given key * - * If the key is not found, splays around an arbitrary neighbour. - * Returns the relationship of the new tree root to the 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. * * See . */ @@ -649,10 +693,13 @@ static Compare SplaySplay(SplayTree splay, TreeKey key, TreeCompare compare) } -/* SplayTreeInsert -- Insert a node into a splay tree +/* SplayTreeInsert -- insert a node into a splay tree * - * See and - * . + * + * 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 @@ -702,13 +749,17 @@ Bool SplayTreeInsert(SplayTree splay, Tree node) { } -/* 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, or the then ``FALSE`` will be returned, and the node will + * not be deleted. The function first splays the tree at the given key. + * The client must not pass a node whose key compares equal to a different + * node in the tree. * - * TODO: Consider using SplaySplit. If the found node has zero - * or one children, then the replacement will be leftLast or rightFirst. + * TODO: If the node has zero or one children, then the replacement + * would be the leftLast or rightFirst after a SplaySplit, and would + * avoid a search for a replacement in more cases. */ Bool SplayTreeDelete(SplayTree splay, Tree node) { @@ -753,8 +804,9 @@ Bool SplayTreeDelete(SplayTree splay, Tree node) { /* 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. */ Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) { @@ -772,7 +824,7 @@ Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) { } -/* 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 TreeEMPTY is returned, and the tree is unchanged. @@ -807,13 +859,19 @@ static Tree SplayTreeSuccessor(SplayTree splay) { /* SplayTreeNeighbours * * Search for the two nodes in a splay tree neighbouring a key. + * Splays the tree at the key. ``*leftReturn`` will be the neighbour + * which compares less than the key if such a neighbour exists; otherwise + * it will be ``TreeEMPTY``. ``*rightReturn`` will be the neighbour which + * compares greater than the key if such a neighbour exists; otherwise + * it will be ``TreeEMPTY``. The function returns ``FALSE`` if any node + * in the tree compares ``CompareEQUAL`` with the given key. * * TODO: Change to SplayTreeCoalesce that takes a function that can * direct the deletion of one of the neighbours, since this is a * good moment to do it, avoiding another search and splay. * - * See and - * . + * This implementation uses SplaySplit to find both neighbours in a + * single splay (see design.mps.splay.impl.neighbours). */ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, @@ -872,20 +930,22 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, } -/* SplayTreeFirst, SplayTreeNext -- Iterators +/* SplayTreeFirst, SplayTreeNext -- iterators * * SplayTreeFirst receives a key that must precede all * 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 TreeEMPTY 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. * - * Iterating over the tree using these functions will leave the tree - * totally unbalanced. Consider using TreeTraverse. + * SplayTreeFirst and SplayTreeNext do not require the tree to remain + * unmodified. + * + * IMPORTANT: Iterating over the tree using these functions will leave + * the tree totally unbalanced, throwing away optimisations of the tree + * shape caused by previous splays. Consider using TreeTraverse instead. */ Tree SplayTreeFirst(SplayTree splay) { @@ -970,6 +1030,21 @@ static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream, } +/* 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; @@ -990,6 +1065,8 @@ static Compare SplayFindFirstCompare(Tree node, TreeKey key) 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; @@ -1021,6 +1098,8 @@ static Compare SplayFindLastCompare(Tree node, TreeKey key) 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; @@ -1047,6 +1126,8 @@ static Compare SplayFindLastCompare(Tree node, TreeKey key) * 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 @@ -1115,14 +1196,16 @@ Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, } -/* 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 splay, Tree node) From 347436131e725de8ea21ed16292cf22bd48168da Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 26 Feb 2014 12:59:54 +0000 Subject: [PATCH 4/6] Updating references to not go through unpublished mminfo documents. Copied from Perforce Change: 184549 ServerID: perforce.ravenbrook.com --- mps/design/splay.txt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 2b69b03c94b..77b573270a4 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. @@ -914,6 +914,18 @@ 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 ---------------- From dc4652b2718f4212b7ec829e0fd5efbc37f70fda Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 26 Feb 2014 13:00:07 +0000 Subject: [PATCH 5/6] Minor tweaks to comments. Copied from Perforce Change: 184550 ServerID: perforce.ravenbrook.com --- mps/code/splay.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mps/code/splay.c b/mps/code/splay.c index a7ed45d2fe5..a9769335a38 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -7,7 +7,7 @@ * 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, @@ -37,7 +37,7 @@ SRCID(splay, "$Id$"); /* SplayTreeCheck -- check consistency of SplayTree * - * See guide.impl.c.adt.check and design.mps.check. + * See guide.impl.c.adt.check and . */ Bool SplayTreeCheck(SplayTree splay) @@ -60,7 +60,7 @@ Bool SplayTreeCheck(SplayTree splay) * * ``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 may be passed, faster + * design.mps.splay.prop). If SplayTrivUpdate is be passed, faster * algorithms are chosen for splaying (FIXME: xref design). */ From 65a00f594502bbf753321916e4ec8b10e7f5f904 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 26 Feb 2014 13:23:31 +0000 Subject: [PATCH 6/6] Minor documentation improvements to binary trees. Copied from Perforce Change: 184552 ServerID: perforce.ravenbrook.com --- mps/code/tree.c | 24 ++++++++++++++++++------ mps/code/tree.h | 3 ++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mps/code/tree.c b/mps/code/tree.c index 59d492987ac..20d03ce6f46 100644 --- a/mps/code/tree.c +++ b/mps/code/tree.c @@ -2,6 +2,9 @@ * * $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). */ #include "tree.h" @@ -154,10 +157,11 @@ Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, * * * - * The tree may not be modified during the traversal, and the traversal - * must complete. + * The tree may not be accessed or modified during the traversal, and + * the traversal must complete in order to repair the tree. * - * TreeTraverse is generally superior if comparisons are cheap. + * TreeTraverse is generally superior if comparisons are cheap, but + * TreeTraverseMorris does not require any comparison function. */ Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, @@ -202,7 +206,13 @@ Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, } -/* TreeTraverse -- traverse tree using pointer reversal */ +/* TreeTraverse -- traverse tree 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. + * + * TreeTraverseMorris is an alternative when no cheap comparison is available. + */ static Tree stepDownLeft(Tree node, Tree *parentIO) { @@ -300,7 +310,8 @@ 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. + * child of node, leftwards in the order stated. Preserves tree + * ordering. */ void TreeRotateLeft(Tree *treeIO) @@ -323,7 +334,8 @@ void TreeRotateLeft(Tree *treeIO) /* 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. + * child of node, leftwards in the order stated. Preserves tree + * ordering. */ void TreeRotateRight(Tree *treeIO) { diff --git a/mps/code/tree.h b/mps/code/tree.h index 42aa135054c..1eaebd23932 100644 --- a/mps/code/tree.h +++ b/mps/code/tree.h @@ -3,7 +3,8 @@ * $Id$ * Copyright (C) 2014 Ravenbrook Limited. See end of file for license. * - * Simple binary trees, for use as building blocks. + * Simple binary trees with utilities, for use as building blocks. + * Keep it simple, like Rings (see ring.h). */ #ifndef tree_h