Back to index

courier  0.68.2
Classes | Defines | Typedefs | Functions
tsearch.c File Reference
#include <config.h>
#include <search.h>
#include <stdlib.h>
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  node_t

Defines

#define __tsearch   tsearch
#define __tfind   tfind
#define __tdelete   tdelete
#define __twalk   twalk
#define internal_function
#define CHECK_TREE(a)

Typedefs

typedef int(* __compar_fn_t )(const void *, const void *)
typedef void(* __action_fn_t )(const void *, VISIT, int)
typedef struct node_tnode
typedef struct node_tconst_node

Functions

static void maybe_split_for_insert (node *rootp, node *parentp, node *gparentp, int p_r, int gp_r, int mode)
void * __tsearch (const void *key, void **vrootp, __compar_fn_t compar)
void * __tfind (void *key, void *const *vrootp, __compar_fn_t compar) const
void * __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
static void internal_function trecurse (const void *vroot, __action_fn_t action, int level)
void __twalk (const void *vroot, __action_fn_t action)

Class Documentation

struct node_t

Definition at line 116 of file tsearch.c.

Collaboration diagram for node_t:
Class Members
const void * key
struct node_t * left
unsigned int red:1
struct node_t * right

Define Documentation

#define __tdelete   tdelete

Definition at line 106 of file tsearch.c.

#define __tfind   tfind

Definition at line 105 of file tsearch.c.

#define __tsearch   tsearch

Definition at line 104 of file tsearch.c.

#define __twalk   twalk

Definition at line 107 of file tsearch.c.

#define CHECK_TREE (   a)

Definition at line 170 of file tsearch.c.

Definition at line 113 of file tsearch.c.


Typedef Documentation

typedef void(* __action_fn_t)(const void *, VISIT, int)

Definition at line 101 of file tsearch.c.

typedef int(* __compar_fn_t)(const void *, const void *)

Definition at line 100 of file tsearch.c.

typedef struct node_t* const_node

Definition at line 125 of file tsearch.c.

typedef struct node_t * node

Function Documentation

void* __tdelete ( const void *  key,
void **  vrootp,
__compar_fn_t  compar 
)

Definition at line 365 of file tsearch.c.

{
  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 = 100;
  int sp = 0;
  node *nodestack[100];

  if (rootp == NULL)
    return NULL;
  p = *rootp;
  if (p == NULL)
    return NULL;

  CHECK_TREE (p);

  while ((cmp = (*compar) (key, (*rootp)->key)) != 0)
    {
      if (sp == stacksize)
        abort ();

      nodestack[sp++] = rootp;
      p = *rootp;
      rootp = ((cmp < 0)
               ? &(*rootp)->left
               : &(*rootp)->right);
      if (*rootp == 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 = *rootp;

  r = root->right;
  q = root->left;

  if (q == NULL || r == NULL)
    unchained = root;
  else
    {
      node *parent = rootp, *up = &root->right;
      for (;;)
        {
          if (sp == stacksize)
            abort ();
          nodestack[sp++] = parent;
          parent = up;
          if ((*up)->left == NULL)
            break;
          up = &(*up)->left;
        }
      unchained = *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 = unchained->left;
  if (r == NULL)
    r = unchained->right;
  if (sp == 0)
    *rootp = r;
  else
    {
      q = *nodestack[sp-1];
      if (unchained == q->right)
        q->right = r;
      else
        q->left = r;
    }

  if (unchained != root)
    root->key = unchained->key;
  if (!unchained->red)
    {
      /* 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 || !r->red))
        {
          node *pp = nodestack[sp - 1];
          p = *pp;
          /* Two symmetric cases.  */
          if (r == p->left)
            {
              /* 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 = p->right;
              if (q->red)
                {
                  /* 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.  */
                  q->red = 0;
                  p->red = 1;
                  /* Left rotate p.  */
                  p->right = q->left;
                  q->left = p;
                  *pp = q;
                  /* Make sure pp is right if the case below tries to use
                     it.  */
                  nodestack[sp++] = pp = &q->left;
                  q = p->right;
                }
              /* We know that Q can't be NULL here.  We also know that Q is
                 black.  */
              if ((q->left == NULL || !q->left->red)
                  && (q->right == NULL || !q->right->red))
                {
                  /* 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.  */
                  q->red = 1;
                  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 (q->right == NULL || !q->right->red)
                    {
                      /* 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 = q->left;
                      q2->red = p->red;
                      p->right = q2->left;
                      q->left = q2->right;
                      q2->right = q;
                      q2->left = p;
                      *pp = q2;
                      p->red = 0;
                    }
                  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.  */
                      q->red = p->red;
                      p->red = 0;

                      q->right->red = 0;

                      /* left rotate p */
                      p->right = q->left;
                      q->left = p;
                      *pp = q;
                    }

                  /* We're done.  */
                  sp = 1;
                  r = NULL;
                }
            }
          else
            {
              /* Comments: see above.  */
              q = p->left;
              if (q->red)
                {
                  q->red = 0;
                  p->red = 1;
                  p->left = q->right;
                  q->right = p;
                  *pp = q;
                  nodestack[sp++] = pp = &q->right;
                  q = p->left;
                }
              if ((q->right == NULL || !q->right->red)
                       && (q->left == NULL || !q->left->red))
                {
                  q->red = 1;
                  r = p;
                }
              else
                {
                  if (q->left == NULL || !q->left->red)
                    {
                      node q2 = q->right;
                      q2->red = p->red;
                      p->left = q2->right;
                      q->right = q2->left;
                      q2->left = q;
                      q2->right = p;
                      *pp = q2;
                      p->red = 0;
                    }
                  else
                    {
                      q->red = p->red;
                      p->red = 0;
                      q->left->red = 0;
                      p->left = q->right;
                      q->right = p;
                      *pp = q;
                    }
                  sp = 1;
                  r = NULL;
                }
            }
          --sp;
        }
      if (r != NULL)
        r->red = 0;
    }

  free (unchained);
  return retval;
}

Here is the call graph for this function:

void* __tfind ( void *  key,
void *const *  vrootp,
__compar_fn_t  compar 
) const

Definition at line 331 of file tsearch.c.

{
  node *rootp = (node *) vrootp;

  if (rootp == NULL)
    return NULL;

  CHECK_TREE (*rootp);

  while (*rootp != NULL)
    {
      node root = *rootp;
      int r;

      r = (*compar) (key, root->key);
      if (r == 0)
        return root;

      rootp = r < 0 ? &root->left : &root->right;
    }
  return NULL;
}
void* __tsearch ( const void *  key,
void **  vrootp,
__compar_fn_t  compar 
)

Definition at line 264 of file tsearch.c.

{
  node q;
  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.  */

  if (rootp == NULL)
    return NULL;

  /* This saves some additional tests below.  */
  if (*rootp != NULL)
    (*rootp)->red = 0;

  CHECK_TREE (*rootp);

  nextp = rootp;
  while (*nextp != NULL)
    {
      node root = *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 ? &root->left : &root->right;
      if (*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)
    {
      *nextp = q;                       /* link new node to old */
      q->key = key;                     /* initialize new node */
      q->red = 1;
      q->left = q->right = 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;
}

Here is the call graph for this function:

void __twalk ( const void *  vroot,
__action_fn_t  action 
)

Definition at line 641 of file tsearch.c.

{
  const_node root = (const_node) vroot;

  CHECK_TREE (root);

  if (root != NULL && action != NULL)
    trecurse (root, action, 0);
}

Here is the call graph for this function:

static void maybe_split_for_insert ( node rootp,
node parentp,
node gparentp,
int  p_r,
int  gp_r,
int  mode 
) [static]

Definition at line 181 of file tsearch.c.

{
  node root = *rootp;
  node *rp, *lp;
  rp = &(*rootp)->right;
  lp = &(*rootp)->left;

  /* See if we have to split this node (both successors red).  */
  if (mode == 1
      || ((*rp) != NULL && (*lp) != NULL && (*rp)->red && (*lp)->red))
    {
      /* This node becomes red, its successors black.  */
      root->red = 1;
      if (*rp)
        (*rp)->red = 0;
      if (*lp)
        (*lp)->red = 0;

      /* If the parent of this node is also red, we have to do
         rotations.  */
      if (parentp != NULL && (*parentp)->red)
        {
          node gp = *gparentp;
          node p = *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.  */
              p->red = 1;
              gp->red = 1;
              root->red = 0;
              if (p_r < 0)
                {
                  /* Child is left of parent.  */
                  p->left = *rp;
                  *rp = p;
                  gp->right = *lp;
                  *lp = gp;
                }
              else
                {
                  /* Child is right of parent.  */
                  p->right = *lp;
                  *lp = p;
                  gp->left = *rp;
                  *rp = gp;
                }
              *gparentp = root;
            }
          else
            {
              *gparentp = *parentp;
              /* Parent becomes the top of the tree, grandparent and
                 child are its successors.  */
              p->red = 0;
              gp->red = 1;
              if (p_r < 0)
                {
                  /* Left edges.  */
                  gp->left = p->right;
                  p->right = gp;
                }
              else
                {
                  /* Right edges.  */
                  gp->right = p->left;
                  p->left = gp;
                }
            }
        }
    }
}

Here is the caller graph for this function:

static void internal_function trecurse ( const void *  vroot,
__action_fn_t  action,
int  level 
) [static]

Definition at line 618 of file tsearch.c.

{
  const_node root = (const_node) vroot;

  if (root->left == NULL && root->right == NULL)
    (*action) (root, leaf, level);
  else
    {
      (*action) (root, preorder, level);
      if (root->left != NULL)
        trecurse (root->left, action, level + 1);
      (*action) (root, postorder, level);
      if (root->right != NULL)
        trecurse (root->right, action, level + 1);
      (*action) (root, endorder, level);
    }
}

Here is the caller graph for this function: