aboutsummaryrefslogtreecommitdiff
path: root/misc/tsearch.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc/tsearch.c')
-rw-r--r--misc/tsearch.c750
1 files changed, 0 insertions, 750 deletions
diff --git a/misc/tsearch.c b/misc/tsearch.c
deleted file mode 100644
index 5e2e7986d3..0000000000
--- a/misc/tsearch.c
+++ /dev/null
@@ -1,750 +0,0 @@
-/* Copyright (C) 1995-2017 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>, 1997.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
-
-/* Tree search for red/black trees.
- The algorithm for adding nodes is taken from one of the many "Algorithms"
- books by Robert Sedgewick, although the implementation differs.
- The algorithm for deleting nodes can probably be found in a book named
- "Introduction to Algorithms" by Cormen/Leiserson/Rivest. At least that's
- the book that my professor took most algorithms from during the "Data
- Structures" course...
-
- Totally public domain. */
-
-/* Red/black trees are binary trees in which the edges are colored either red
- or black. They have the following properties:
- 1. The number of black edges on every path from the root to a leaf is
- constant.
- 2. No two red edges are adjacent.
- Therefore there is an upper bound on the length of every path, it's
- O(log n) where n is the number of nodes in the tree. No path can be longer
- than 1+2*P where P is the length of the shortest path in the tree.
- Useful for the implementation:
- 3. If one of the children of a node is NULL, then the other one is red
- (if it exists).
-
- In the implementation, not the edges are colored, but the nodes. The color
- interpreted as the color of the edge leading to this node. The color is
- meaningless for the root node, but we color the root node black for
- convenience. All added nodes are red initially.
-
- Adding to a red/black tree is rather easy. The right place is searched
- with a usual binary tree search. Additionally, whenever a node N is
- reached that has two red successors, the successors are colored black and
- the node itself colored red. This moves red edges up the tree where they
- pose less of a problem once we get to really insert the new node. Changing
- N's color to red may violate rule 2, however, so rotations may become
- necessary to restore the invariants. Adding a new red leaf may violate
- the same rule, so afterwards an additional check is run and the tree
- possibly rotated.
-
- Deleting is hairy. There are mainly two nodes involved: the node to be
- deleted (n1), and another node that is to be unchained from the tree (n2).
- If n1 has a successor (the node with a smallest key that is larger than
- n1), then the successor becomes n2 and its contents are copied into n1,
- otherwise n1 becomes n2.
- Unchaining a node may violate rule 1: if n2 is black, one subtree is
- missing one black edge afterwards. The algorithm must try to move this
- error upwards towards the root, so that the subtree that does not have
- enough black edges becomes the whole tree. Once that happens, the error
- has disappeared. It may not be necessary to go all the way up, since it
- is possible that rotations and recoloring can fix the error before that.
-
- Although the deletion algorithm must walk upwards through the tree, we
- do not store parent pointers in the nodes. Instead, delete allocates a
- small array of parent pointers and fills it while descending the tree.
- Since we know that the length of a path is O(log n), where n is the number
- of nodes, this is likely to use less memory. */
-
-/* Tree rotations look like this:
- A C
- / \ / \
- B C A G
- / \ / \ --> / \
- D E F G B F
- / \
- D E
-
- In this case, A has been rotated left. This preserves the ordering of the
- binary tree. */
-
-#include <assert.h>
-#include <stdalign.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <search.h>
-
-/* Assume malloc returns naturally aligned (alignof (max_align_t))
- pointers so we can use the low bits to store some extra info. This
- works for the left/right node pointers since they are not user
- visible and always allocated by malloc. The user provides the key
- pointer and so that can point anywhere and doesn't have to be
- aligned. */
-#define USE_MALLOC_LOW_BIT 1
-
-#ifndef USE_MALLOC_LOW_BIT
-typedef struct node_t
-{
- /* Callers expect this to be the first element in the structure - do not
- move! */
- const void *key;
- struct node_t *left_node;
- struct node_t *right_node;
- unsigned int is_red:1;
-} *node;
-
-#define RED(N) (N)->is_red
-#define SETRED(N) (N)->is_red = 1
-#define SETBLACK(N) (N)->is_red = 0
-#define SETNODEPTR(NP,P) (*NP) = (P)
-#define LEFT(N) (N)->left_node
-#define LEFTPTR(N) (&(N)->left_node)
-#define SETLEFT(N,L) (N)->left_node = (L)
-#define RIGHT(N) (N)->right_node
-#define RIGHTPTR(N) (&(N)->right_node)
-#define SETRIGHT(N,R) (N)->right_node = (R)
-#define DEREFNODEPTR(NP) (*(NP))
-
-#else /* USE_MALLOC_LOW_BIT */
-
-typedef struct node_t
-{
- /* Callers expect this to be the first element in the structure - do not
- move! */
- const void *key;
- uintptr_t left_node; /* Includes whether the node is red in low-bit. */
- uintptr_t right_node;
-} *node;
-
-#define RED(N) (node)((N)->left_node & ((uintptr_t) 0x1))
-#define SETRED(N) (N)->left_node |= ((uintptr_t) 0x1)
-#define SETBLACK(N) (N)->left_node &= ~((uintptr_t) 0x1)
-#define SETNODEPTR(NP,P) (*NP) = (node)((((uintptr_t)(*NP)) \
- & (uintptr_t) 0x1) | (uintptr_t)(P))
-#define LEFT(N) (node)((N)->left_node & ~((uintptr_t) 0x1))
-#define LEFTPTR(N) (node *)(&(N)->left_node)
-#define SETLEFT(N,L) (N)->left_node = (((N)->left_node & (uintptr_t) 0x1) \
- | (uintptr_t)(L))
-#define RIGHT(N) (node)((N)->right_node)
-#define RIGHTPTR(N) (node *)(&(N)->right_node)
-#define SETRIGHT(N,R) (N)->right_node = (uintptr_t)(R)
-#define DEREFNODEPTR(NP) (node)((uintptr_t)(*(NP)) & ~((uintptr_t) 0x1))
-
-#endif /* USE_MALLOC_LOW_BIT */
-typedef const struct node_t *const_node;
-
-#undef DEBUGGING
-
-#ifdef DEBUGGING
-
-/* Routines to check tree invariants. */
-
-#define CHECK_TREE(a) check_tree(a)
-
-static void
-check_tree_recurse (node p, int d_sofar, int d_total)
-{
- if (p == NULL)
- {
- assert (d_sofar == d_total);
- return;
- }
-
- check_tree_recurse (LEFT(p), d_sofar + (LEFT(p) && !RED(LEFT(p))),
- d_total);
- check_tree_recurse (RIGHT(p), d_sofar + (RIGHT(p) && !RED(RIGHT(p))),
- d_total);
- if (LEFT(p))
- assert (!(RED(LEFT(p)) && RED(p)));
- if (RIGHT(p))
- assert (!(RED(RIGHT(p)) && RED(p)));
-}
-
-static void
-check_tree (node root)
-{
- int cnt = 0;
- node p;
- if (root == NULL)
- return;
- SETBLACK(root);
- for(p = LEFT(root); p; p = LEFT(p))
- cnt += !RED(p);
- check_tree_recurse (root, 0, cnt);
-}
-
-#else
-
-#define CHECK_TREE(a)
-
-#endif
-
-/* Possibly "split" a node with two red successors, and/or fix up two red
- edges in a row. ROOTP is a pointer to the lowest node we visited, PARENTP
- and GPARENTP pointers to its parent/grandparent. P_R and GP_R contain the
- comparison values that determined which way was taken in the tree to reach
- ROOTP. MODE is 1 if we need not do the split, but must check for two red
- edges between GPARENTP and ROOTP. */
-static void
-maybe_split_for_insert (node *rootp, node *parentp, node *gparentp,
- int p_r, int gp_r, int mode)
-{
- node root = DEREFNODEPTR(rootp);
- node *rp, *lp;
- node rpn, lpn;
- rp = RIGHTPTR(root);
- rpn = RIGHT(root);
- lp = LEFTPTR(root);
- lpn = LEFT(root);
-
- /* See if we have to split this node (both successors red). */
- if (mode == 1
- || ((rpn) != NULL && (lpn) != NULL && RED(rpn) && RED(lpn)))
- {
- /* This node becomes red, its successors black. */
- SETRED(root);
- if (rpn)
- SETBLACK(rpn);
- if (lpn)
- SETBLACK(lpn);
-
- /* If the parent of this node is also red, we have to do
- rotations. */
- if (parentp != NULL && RED(DEREFNODEPTR(parentp)))
- {
- node gp = DEREFNODEPTR(gparentp);
- node p = DEREFNODEPTR(parentp);
- /* There are two main cases:
- 1. The edge types (left or right) of the two red edges differ.
- 2. Both red edges are of the same type.
- There exist two symmetries of each case, so there is a total of
- 4 cases. */
- if ((p_r > 0) != (gp_r > 0))
- {
- /* Put the child at the top of the tree, with its parent
- and grandparent as successors. */
- SETRED(p);
- SETRED(gp);
- SETBLACK(root);
- if (p_r < 0)
- {
- /* Child is left of parent. */
- SETLEFT(p,rpn);
- SETNODEPTR(rp,p);
- SETRIGHT(gp,lpn);
- SETNODEPTR(lp,gp);
- }
- else
- {
- /* Child is right of parent. */
- SETRIGHT(p,lpn);
- SETNODEPTR(lp,p);
- SETLEFT(gp,rpn);
- SETNODEPTR(rp,gp);
- }
- SETNODEPTR(gparentp,root);
- }
- else
- {
- SETNODEPTR(gparentp,p);
- /* Parent becomes the top of the tree, grandparent and
- child are its successors. */
- SETBLACK(p);
- SETRED(gp);
- if (p_r < 0)
- {
- /* Left edges. */
- SETLEFT(gp,RIGHT(p));
- SETRIGHT(p,gp);
- }
- else
- {
- /* Right edges. */
- SETRIGHT(gp,LEFT(p));
- SETLEFT(p,gp);
- }
- }
- }
- }
-}
-
-/* Find or insert datum into search tree.
- KEY is the key to be located, ROOTP is the address of tree root,
- COMPAR the ordering function. */
-void *
-__tsearch (const void *key, void **vrootp, __compar_fn_t compar)
-{
- node q, root;
- node *parentp = NULL, *gparentp = NULL;
- node *rootp = (node *) vrootp;
- node *nextp;
- int r = 0, p_r = 0, gp_r = 0; /* No they might not, Mr Compiler. */
-
-#ifdef USE_MALLOC_LOW_BIT
- static_assert (alignof (max_align_t) > 1, "malloc must return aligned ptrs");
-#endif
-
- if (rootp == NULL)
- return NULL;
-
- /* This saves some additional tests below. */
- root = DEREFNODEPTR(rootp);
- if (root != NULL)
- SETBLACK(root);
-
- CHECK_TREE (root);
-
- nextp = rootp;
- while (DEREFNODEPTR(nextp) != NULL)
- {
- root = DEREFNODEPTR(rootp);
- r = (*compar) (key, root->key);
- if (r == 0)
- return root;
-
- maybe_split_for_insert (rootp, parentp, gparentp, p_r, gp_r, 0);
- /* If that did any rotations, parentp and gparentp are now garbage.
- That doesn't matter, because the values they contain are never
- used again in that case. */
-
- nextp = r < 0 ? LEFTPTR(root) : RIGHTPTR(root);
- if (DEREFNODEPTR(nextp) == NULL)
- break;
-
- gparentp = parentp;
- parentp = rootp;
- rootp = nextp;
-
- gp_r = p_r;
- p_r = r;
- }
-
- q = (struct node_t *) malloc (sizeof (struct node_t));
- if (q != NULL)
- {
- /* Make sure the malloc implementation returns naturally aligned
- memory blocks when expected. Or at least even pointers, so we
- can use the low bit as red/black flag. Even though we have a
- static_assert to make sure alignof (max_align_t) > 1 there could
- be an interposed malloc implementation that might cause havoc by
- not obeying the malloc contract. */
-#ifdef USE_MALLOC_LOW_BIT
- assert (((uintptr_t) q & (uintptr_t) 0x1) == 0);
-#endif
- SETNODEPTR(nextp,q); /* link new node to old */
- q->key = key; /* initialize new node */
- SETRED(q);
- SETLEFT(q,NULL);
- SETRIGHT(q,NULL);
-
- if (nextp != rootp)
- /* There may be two red edges in a row now, which we must avoid by
- rotating the tree. */
- maybe_split_for_insert (nextp, rootp, parentp, r, p_r, 1);
- }
-
- return q;
-}
-libc_hidden_def (__tsearch)
-weak_alias (__tsearch, tsearch)
-
-
-/* Find datum in search tree.
- KEY is the key to be located, ROOTP is the address of tree root,
- COMPAR the ordering function. */
-void *
-__tfind (const void *key, void *const *vrootp, __compar_fn_t compar)
-{
- node root;
- node *rootp = (node *) vrootp;
-
- if (rootp == NULL)
- return NULL;
-
- root = DEREFNODEPTR(rootp);
- CHECK_TREE (root);
-
- while (DEREFNODEPTR(rootp) != NULL)
- {
- root = DEREFNODEPTR(rootp);
- int r;
-
- r = (*compar) (key, root->key);
- if (r == 0)
- return root;
-
- rootp = r < 0 ? LEFTPTR(root) : RIGHTPTR(root);
- }
- return NULL;
-}
-libc_hidden_def (__tfind)
-weak_alias (__tfind, tfind)
-
-
-/* Delete node with given key.
- KEY is the key to be deleted, ROOTP is the address of the root of tree,
- COMPAR the comparison function. */
-void *
-__tdelete (const void *key, void **vrootp, __compar_fn_t compar)
-{
- node p, q, r, retval;
- int cmp;
- node *rootp = (node *) vrootp;
- node root, unchained;
- /* Stack of nodes so we remember the parents without recursion. It's
- _very_ unlikely that there are paths longer than 40 nodes. The tree
- would need to have around 250.000 nodes. */
- int stacksize = 40;
- int sp = 0;
- node **nodestack = alloca (sizeof (node *) * stacksize);
-
- if (rootp == NULL)
- return NULL;
- p = DEREFNODEPTR(rootp);
- if (p == NULL)
- return NULL;
-
- CHECK_TREE (p);
-
- root = DEREFNODEPTR(rootp);
- while ((cmp = (*compar) (key, root->key)) != 0)
- {
- if (sp == stacksize)
- {
- node **newstack;
- stacksize += 20;
- newstack = alloca (sizeof (node *) * stacksize);
- nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
- }
-
- nodestack[sp++] = rootp;
- p = DEREFNODEPTR(rootp);
- if (cmp < 0)
- {
- rootp = LEFTPTR(p);
- root = LEFT(p);
- }
- else
- {
- rootp = RIGHTPTR(p);
- root = RIGHT(p);
- }
- if (root == NULL)
- return NULL;
- }
-
- /* This is bogus if the node to be deleted is the root... this routine
- really should return an integer with 0 for success, -1 for failure
- and errno = ESRCH or something. */
- retval = p;
-
- /* We don't unchain the node we want to delete. Instead, we overwrite
- it with its successor and unchain the successor. If there is no
- successor, we really unchain the node to be deleted. */
-
- root = DEREFNODEPTR(rootp);
-
- r = RIGHT(root);
- q = LEFT(root);
-
- if (q == NULL || r == NULL)
- unchained = root;
- else
- {
- node *parentp = rootp, *up = RIGHTPTR(root);
- node upn;
- for (;;)
- {
- if (sp == stacksize)
- {
- node **newstack;
- stacksize += 20;
- newstack = alloca (sizeof (node *) * stacksize);
- nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
- }
- nodestack[sp++] = parentp;
- parentp = up;
- upn = DEREFNODEPTR(up);
- if (LEFT(upn) == NULL)
- break;
- up = LEFTPTR(upn);
- }
- unchained = DEREFNODEPTR(up);
- }
-
- /* We know that either the left or right successor of UNCHAINED is NULL.
- R becomes the other one, it is chained into the parent of UNCHAINED. */
- r = LEFT(unchained);
- if (r == NULL)
- r = RIGHT(unchained);
- if (sp == 0)
- SETNODEPTR(rootp,r);
- else
- {
- q = DEREFNODEPTR(nodestack[sp-1]);
- if (unchained == RIGHT(q))
- SETRIGHT(q,r);
- else
- SETLEFT(q,r);
- }
-
- if (unchained != root)
- root->key = unchained->key;
- if (!RED(unchained))
- {
- /* Now we lost a black edge, which means that the number of black
- edges on every path is no longer constant. We must balance the
- tree. */
- /* NODESTACK now contains all parents of R. R is likely to be NULL
- in the first iteration. */
- /* NULL nodes are considered black throughout - this is necessary for
- correctness. */
- while (sp > 0 && (r == NULL || !RED(r)))
- {
- node *pp = nodestack[sp - 1];
- p = DEREFNODEPTR(pp);
- /* Two symmetric cases. */
- if (r == LEFT(p))
- {
- /* Q is R's brother, P is R's parent. The subtree with root
- R has one black edge less than the subtree with root Q. */
- q = RIGHT(p);
- if (RED(q))
- {
- /* If Q is red, we know that P is black. We rotate P left
- so that Q becomes the top node in the tree, with P below
- it. P is colored red, Q is colored black.
- This action does not change the black edge count for any
- leaf in the tree, but we will be able to recognize one
- of the following situations, which all require that Q
- is black. */
- SETBLACK(q);
- SETRED(p);
- /* Left rotate p. */
- SETRIGHT(p,LEFT(q));
- SETLEFT(q,p);
- SETNODEPTR(pp,q);
- /* Make sure pp is right if the case below tries to use
- it. */
- nodestack[sp++] = pp = LEFTPTR(q);
- q = RIGHT(p);
- }
- /* We know that Q can't be NULL here. We also know that Q is
- black. */
- if ((LEFT(q) == NULL || !RED(LEFT(q)))
- && (RIGHT(q) == NULL || !RED(RIGHT(q))))
- {
- /* Q has two black successors. We can simply color Q red.
- The whole subtree with root P is now missing one black
- edge. Note that this action can temporarily make the
- tree invalid (if P is red). But we will exit the loop
- in that case and set P black, which both makes the tree
- valid and also makes the black edge count come out
- right. If P is black, we are at least one step closer
- to the root and we'll try again the next iteration. */
- SETRED(q);
- r = p;
- }
- else
- {
- /* Q is black, one of Q's successors is red. We can
- repair the tree with one operation and will exit the
- loop afterwards. */
- if (RIGHT(q) == NULL || !RED(RIGHT(q)))
- {
- /* The left one is red. We perform the same action as
- in maybe_split_for_insert where two red edges are
- adjacent but point in different directions:
- Q's left successor (let's call it Q2) becomes the
- top of the subtree we are looking at, its parent (Q)
- and grandparent (P) become its successors. The former
- successors of Q2 are placed below P and Q.
- P becomes black, and Q2 gets the color that P had.
- This changes the black edge count only for node R and
- its successors. */
- node q2 = LEFT(q);
- if (RED(p))
- SETRED(q2);
- else
- SETBLACK(q2);
- SETRIGHT(p,LEFT(q2));
- SETLEFT(q,RIGHT(q2));
- SETRIGHT(q2,q);
- SETLEFT(q2,p);
- SETNODEPTR(pp,q2);
- SETBLACK(p);
- }
- else
- {
- /* It's the right one. Rotate P left. P becomes black,
- and Q gets the color that P had. Q's right successor
- also becomes black. This changes the black edge
- count only for node R and its successors. */
- if (RED(p))
- SETRED(q);
- else
- SETBLACK(q);
- SETBLACK(p);
-
- SETBLACK(RIGHT(q));
-
- /* left rotate p */
- SETRIGHT(p,LEFT(q));
- SETLEFT(q,p);
- SETNODEPTR(pp,q);
- }
-
- /* We're done. */
- sp = 1;
- r = NULL;
- }
- }
- else
- {
- /* Comments: see above. */
- q = LEFT(p);
- if (RED(q))
- {
- SETBLACK(q);
- SETRED(p);
- SETLEFT(p,RIGHT(q));
- SETRIGHT(q,p);
- SETNODEPTR(pp,q);
- nodestack[sp++] = pp = RIGHTPTR(q);
- q = LEFT(p);
- }
- if ((RIGHT(q) == NULL || !RED(RIGHT(q)))
- && (LEFT(q) == NULL || !RED(LEFT(q))))
- {
- SETRED(q);
- r = p;
- }
- else
- {
- if (LEFT(q) == NULL || !RED(LEFT(q)))
- {
- node q2 = RIGHT(q);
- if (RED(p))
- SETRED(q2);
- else
- SETBLACK(q2);
- SETLEFT(p,RIGHT(q2));
- SETRIGHT(q,LEFT(q2));
- SETLEFT(q2,q);
- SETRIGHT(q2,p);
- SETNODEPTR(pp,q2);
- SETBLACK(p);
- }
- else
- {
- if (RED(p))
- SETRED(q);
- else
- SETBLACK(q);
- SETBLACK(p);
- SETBLACK(LEFT(q));
- SETLEFT(p,RIGHT(q));
- SETRIGHT(q,p);
- SETNODEPTR(pp,q);
- }
- sp = 1;
- r = NULL;
- }
- }
- --sp;
- }
- if (r != NULL)
- SETBLACK(r);
- }
-
- free (unchained);
- return retval;
-}
-libc_hidden_def (__tdelete)
-weak_alias (__tdelete, tdelete)
-
-
-/* Walk the nodes of a tree.
- ROOT is the root of the tree to be walked, ACTION the function to be
- called at each node. LEVEL is the level of ROOT in the whole tree. */
-static void
-internal_function
-trecurse (const void *vroot, __action_fn_t action, int level)
-{
- const_node root = (const_node) vroot;
-
- if (LEFT(root) == NULL && RIGHT(root) == NULL)
- (*action) (root, leaf, level);
- else
- {
- (*action) (root, preorder, level);
- if (LEFT(root) != NULL)
- trecurse (LEFT(root), action, level + 1);
- (*action) (root, postorder, level);
- if (RIGHT(root) != NULL)
- trecurse (RIGHT(root), action, level + 1);
- (*action) (root, endorder, level);
- }
-}
-
-
-/* Walk the nodes of a tree.
- ROOT is the root of the tree to be walked, ACTION the function to be
- called at each node. */
-void
-__twalk (const void *vroot, __action_fn_t action)
-{
- const_node root = (const_node) vroot;
-
- CHECK_TREE ((node) root);
-
- if (root != NULL && action != NULL)
- trecurse (root, action, 0);
-}
-libc_hidden_def (__twalk)
-weak_alias (__twalk, twalk)
-
-
-
-/* The standardized functions miss an important functionality: the
- tree cannot be removed easily. We provide a function to do this. */
-static void
-internal_function
-tdestroy_recurse (node root, __free_fn_t freefct)
-{
- if (LEFT(root) != NULL)
- tdestroy_recurse (LEFT(root), freefct);
- if (RIGHT(root) != NULL)
- tdestroy_recurse (RIGHT(root), freefct);
- (*freefct) ((void *) root->key);
- /* Free the node itself. */
- free (root);
-}
-
-void
-__tdestroy (void *vroot, __free_fn_t freefct)
-{
- node root = (node) vroot;
-
- CHECK_TREE (root);
-
- if (root != NULL)
- tdestroy_recurse (root, freefct);
-}
-weak_alias (__tdestroy, tdestroy)