about summary refs log tree commit diff
path: root/misc/tsearch.c
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2016-08-25 23:47:19 +0200
committerMark Wielaard <mjw@redhat.com>2016-08-25 23:48:05 +0200
commit9d6861b8c3edcb74faedcebb0a6960c01bb6f34d (patch)
treefaf9e620c2b6e28269b087aff73eebfdcca3ffdc /misc/tsearch.c
parent7ed2b544511e8ad69ac95634388037ec264d52a7 (diff)
downloadglibc-9d6861b8c3edcb74faedcebb0a6960c01bb6f34d.tar.gz
glibc-9d6861b8c3edcb74faedcebb0a6960c01bb6f34d.tar.xz
glibc-9d6861b8c3edcb74faedcebb0a6960c01bb6f34d.zip
Reduce memory size of tsearch red-black tree.
A tsearch red-black tree node contains 3 pointers (key, left, right)
and 1 bit to hold the red-black flag. When allocating new nodes
this 1 bit is expanded to a full word. Causing the overhead per node
to be 3 times the key size.

We can reduce this overhead to just 2 times the key size.
malloc returns naturally aligned memory. All nodes are internally
allocated with malloc and the left/right node pointers are used
as implementation details. So we can use the low bits of the
left/right node pointers to store extra information.

Replace all direct accesses of the struct node_t node pointers and
red-black value with defines that take care of the red-black flag in
the low bit of the (left) node pointer. This reduces the size of the
nodes on 32-bit systems from 16 to 12 bytes and on 64-bit systems
from 32 to 24 bytes.

Also fix a call to CHECK_TREE so the code can be build (and tested)
with DEBUGGING defined again.

V2 changes:

- Add assert after malloc to catch any odd pointers from bad
  interposed mallocs.
- Rename implementation flag to USE_MALLOC_LOW_BIT.

ChangeLog:

       * misc/tsearch.c (struct node_t): Reduce to 3 pointers if
       USE_MALLOC_LOW_BIT.  Define pointer/value accessors.
       (check_tree_recurse): Use newly defined accessors.
       (check_tree): Likewise.
       (maybe_split_for_insert): Likewise.
       (__tfind): Likewise.
       (__tdelete): Likewise.
       (trecurse): Likewise.
       (tdestroy_recurse): Likewise.
       (__tsearch): Likewise. And add asserts for malloc alignment.
       (__twalk): Cast root to node in case CHECK_TREE is defined.
Diffstat (limited to 'misc/tsearch.c')
-rw-r--r--misc/tsearch.c398
1 files changed, 245 insertions, 153 deletions
diff --git a/misc/tsearch.c b/misc/tsearch.c
index ffb89ec0f8..c07e606708 100644
--- a/misc/tsearch.c
+++ b/misc/tsearch.c
@@ -83,19 +83,70 @@
    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;
-  struct node_t *left;
-  struct node_t *right;
-  unsigned int red:1;
+  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
@@ -104,8 +155,6 @@ typedef const struct node_t *const_node;
 
 /* Routines to check tree invariants.  */
 
-#include <assert.h>
-
 #define CHECK_TREE(a) check_tree(a)
 
 static void
@@ -117,12 +166,14 @@ check_tree_recurse (node p, int d_sofar, int d_total)
       return;
     }
 
-  check_tree_recurse (p->left, d_sofar + (p->left && !p->left->red), d_total);
-  check_tree_recurse (p->right, d_sofar + (p->right && !p->right->red), d_total);
-  if (p->left)
-    assert (!(p->left->red && p->red));
-  if (p->right)
-    assert (!(p->right->red && p->red));
+  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
@@ -132,13 +183,12 @@ check_tree (node root)
   node p;
   if (root == NULL)
     return;
-  root->red = 0;
-  for(p = root->left; p; p = p->left)
-    cnt += !p->red;
+  SETBLACK(root);
+  for(p = LEFT(root); p; p = LEFT(p))
+    cnt += !RED(p);
   check_tree_recurse (root, 0, cnt);
 }
 
-
 #else
 
 #define CHECK_TREE(a)
@@ -155,28 +205,31 @@ static void
 maybe_split_for_insert (node *rootp, node *parentp, node *gparentp,
 			int p_r, int gp_r, int mode)
 {
-  node root = *rootp;
+  node root = DEREFNODEPTR(rootp);
   node *rp, *lp;
-  rp = &(*rootp)->right;
-  lp = &(*rootp)->left;
+  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
-      || ((*rp) != NULL && (*lp) != NULL && (*rp)->red && (*lp)->red))
+      || ((rpn) != NULL && (lpn) != NULL && RED(rpn) && RED(lpn)))
     {
       /* This node becomes red, its successors black.  */
-      root->red = 1;
-      if (*rp)
-	(*rp)->red = 0;
-      if (*lp)
-	(*lp)->red = 0;
+      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 && (*parentp)->red)
+      if (parentp != NULL && RED(DEREFNODEPTR(parentp)))
 	{
-	  node gp = *gparentp;
-	  node p = *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.
@@ -186,45 +239,45 @@ maybe_split_for_insert (node *rootp, node *parentp, node *gparentp,
 	    {
 	      /* 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;
+	      SETRED(p);
+	      SETRED(gp);
+	      SETBLACK(root);
 	      if (p_r < 0)
 		{
 		  /* Child is left of parent.  */
-		  p->left = *rp;
-		  *rp = p;
-		  gp->right = *lp;
-		  *lp = gp;
+		  SETLEFT(p,rpn);
+		  SETNODEPTR(rp,p);
+		  SETRIGHT(gp,lpn);
+		  SETNODEPTR(lp,gp);
 		}
 	      else
 		{
 		  /* Child is right of parent.  */
-		  p->right = *lp;
-		  *lp = p;
-		  gp->left = *rp;
-		  *rp = gp;
+		  SETRIGHT(p,lpn);
+		  SETNODEPTR(lp,p);
+		  SETLEFT(gp,rpn);
+		  SETNODEPTR(rp,gp);
 		}
-	      *gparentp = root;
+	      SETNODEPTR(gparentp,root);
 	    }
 	  else
 	    {
-	      *gparentp = *parentp;
+	      SETNODEPTR(gparentp,p);
 	      /* Parent becomes the top of the tree, grandparent and
 		 child are its successors.  */
-	      p->red = 0;
-	      gp->red = 1;
+	      SETBLACK(p);
+	      SETRED(gp);
 	      if (p_r < 0)
 		{
 		  /* Left edges.  */
-		  gp->left = p->right;
-		  p->right = gp;
+		  SETLEFT(gp,RIGHT(p));
+		  SETRIGHT(p,gp);
 		}
 	      else
 		{
 		  /* Right edges.  */
-		  gp->right = p->left;
-		  p->left = gp;
+		  SETRIGHT(gp,LEFT(p));
+		  SETLEFT(p,gp);
 		}
 	    }
 	}
@@ -237,25 +290,30 @@ maybe_split_for_insert (node *rootp, node *parentp, node *gparentp,
 void *
 __tsearch (const void *key, void **vrootp, __compar_fn_t compar)
 {
-  node q;
+  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.  */
-  if (*rootp != NULL)
-    (*rootp)->red = 0;
+  root = DEREFNODEPTR(rootp);
+  if (root != NULL)
+    SETBLACK(root);
 
-  CHECK_TREE (*rootp);
+  CHECK_TREE (root);
 
   nextp = rootp;
-  while (*nextp != NULL)
+  while (DEREFNODEPTR(nextp) != NULL)
     {
-      node root = *rootp;
+      root = DEREFNODEPTR(rootp);
       r = (*compar) (key, root->key);
       if (r == 0)
 	return root;
@@ -265,8 +323,8 @@ __tsearch (const void *key, void **vrootp, __compar_fn_t compar)
 	 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)
+      nextp = r < 0 ? LEFTPTR(root) : RIGHTPTR(root);
+      if (DEREFNODEPTR(nextp) == NULL)
 	break;
 
       gparentp = parentp;
@@ -280,10 +338,20 @@ __tsearch (const void *key, void **vrootp, __compar_fn_t compar)
   q = (struct node_t *) malloc (sizeof (struct node_t));
   if (q != NULL)
     {
-      *nextp = q;			/* link new node to old */
+      /* 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 */
-      q->red = 1;
-      q->left = q->right = NULL;
+      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
@@ -303,23 +371,25 @@ weak_alias (__tsearch, tsearch)
 void *
 __tfind (const void *key, void *const *vrootp, __compar_fn_t compar)
 {
+  node root;
   node *rootp = (node *) vrootp;
 
   if (rootp == NULL)
     return NULL;
 
-  CHECK_TREE (*rootp);
+  root = DEREFNODEPTR(rootp);
+  CHECK_TREE (root);
 
-  while (*rootp != NULL)
+  while (DEREFNODEPTR(rootp) != NULL)
     {
-      node root = *rootp;
+      root = DEREFNODEPTR(rootp);
       int r;
 
       r = (*compar) (key, root->key);
       if (r == 0)
 	return root;
 
-      rootp = r < 0 ? &root->left : &root->right;
+      rootp = r < 0 ? LEFTPTR(root) : RIGHTPTR(root);
     }
   return NULL;
 }
@@ -346,13 +416,14 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 
   if (rootp == NULL)
     return NULL;
-  p = *rootp;
+  p = DEREFNODEPTR(rootp);
   if (p == NULL)
     return NULL;
 
   CHECK_TREE (p);
 
-  while ((cmp = (*compar) (key, (*rootp)->key)) != 0)
+  root = DEREFNODEPTR(rootp);
+  while ((cmp = (*compar) (key, root->key)) != 0)
     {
       if (sp == stacksize)
 	{
@@ -363,11 +434,18 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 	}
 
       nodestack[sp++] = rootp;
-      p = *rootp;
-      rootp = ((cmp < 0)
-	       ? &(*rootp)->left
-	       : &(*rootp)->right);
-      if (*rootp == NULL)
+      p = DEREFNODEPTR(rootp);
+      if (cmp < 0)
+	{
+	  rootp = LEFTPTR(p);
+	  root = LEFT(p);
+	}
+      else
+	{
+	  rootp = RIGHTPTR(p);
+	  root = RIGHT(p);
+	}
+      if (root == NULL)
 	return NULL;
     }
 
@@ -380,16 +458,17 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
      it with its successor and unchain the successor.  If there is no
      successor, we really unchain the node to be deleted.  */
 
-  root = *rootp;
+  root = DEREFNODEPTR(rootp);
 
-  r = root->right;
-  q = root->left;
+  r = RIGHT(root);
+  q = LEFT(root);
 
   if (q == NULL || r == NULL)
     unchained = root;
   else
     {
-      node *parent = rootp, *up = &root->right;
+      node *parentp = rootp, *up = RIGHTPTR(root);
+      node upn;
       for (;;)
 	{
 	  if (sp == stacksize)
@@ -399,34 +478,35 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 	      newstack = alloca (sizeof (node *) * stacksize);
 	      nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
 	    }
-	  nodestack[sp++] = parent;
-	  parent = up;
-	  if ((*up)->left == NULL)
+	  nodestack[sp++] = parentp;
+	  parentp = up;
+	  upn = DEREFNODEPTR(up);
+	  if (LEFT(upn) == NULL)
 	    break;
-	  up = &(*up)->left;
+	  up = LEFTPTR(upn);
 	}
-      unchained = *up;
+      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 = unchained->left;
+  r = LEFT(unchained);
   if (r == NULL)
-    r = unchained->right;
+    r = RIGHT(unchained);
   if (sp == 0)
-    *rootp = r;
+    SETNODEPTR(rootp,r);
   else
     {
-      q = *nodestack[sp-1];
-      if (unchained == q->right)
-	q->right = r;
+      q = DEREFNODEPTR(nodestack[sp-1]);
+      if (unchained == RIGHT(q))
+	SETRIGHT(q,r);
       else
-	q->left = r;
+	SETLEFT(q,r);
     }
 
   if (unchained != root)
     root->key = unchained->key;
-  if (!unchained->red)
+  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
@@ -435,17 +515,17 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 	 in the first iteration.  */
       /* NULL nodes are considered black throughout - this is necessary for
 	 correctness.  */
-      while (sp > 0 && (r == NULL || !r->red))
+      while (sp > 0 && (r == NULL || !RED(r)))
 	{
 	  node *pp = nodestack[sp - 1];
-	  p = *pp;
+	  p = DEREFNODEPTR(pp);
 	  /* Two symmetric cases.  */
-	  if (r == p->left)
+	  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 = p->right;
-	      if (q->red)
+	      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
@@ -454,21 +534,21 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 		     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;
+		  SETBLACK(q);
+		  SETRED(p);
 		  /* Left rotate p.  */
-		  p->right = q->left;
-		  q->left = p;
-		  *pp = q;
+		  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 = &q->left;
-		  q = p->right;
+		  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 ((q->left == NULL || !q->left->red)
-		  && (q->right == NULL || !q->right->red))
+	      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
@@ -478,7 +558,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 		     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;
+		  SETRED(q);
 		  r = p;
 		}
 	      else
@@ -486,7 +566,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 		  /* 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)
+		  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
@@ -498,14 +578,17 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 			 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;
+		      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
 		    {
@@ -513,15 +596,18 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 			 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;
+		      if (RED(p))
+			SETRED(q);
+		      else
+			SETBLACK(q);
+		      SETBLACK(p);
 
-		      q->right->red = 0;
+		      SETBLACK(RIGHT(q));
 
 		      /* left rotate p */
-		      p->right = q->left;
-		      q->left = p;
-		      *pp = q;
+		      SETRIGHT(p,LEFT(q));
+		      SETLEFT(q,p);
+		      SETNODEPTR(pp,q);
 		    }
 
 		  /* We're done.  */
@@ -532,44 +618,50 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 	  else
 	    {
 	      /* Comments: see above.  */
-	      q = p->left;
-	      if (q->red)
+	      q = LEFT(p);
+	      if (RED(q))
 		{
-		  q->red = 0;
-		  p->red = 1;
-		  p->left = q->right;
-		  q->right = p;
-		  *pp = q;
-		  nodestack[sp++] = pp = &q->right;
-		  q = p->left;
+		  SETBLACK(q);
+		  SETRED(p);
+		  SETLEFT(p,RIGHT(q));
+		  SETRIGHT(q,p);
+		  SETNODEPTR(pp,q);
+		  nodestack[sp++] = pp = RIGHTPTR(q);
+		  q = LEFT(p);
 		}
-	      if ((q->right == NULL || !q->right->red)
-		       && (q->left == NULL || !q->left->red))
+	      if ((RIGHT(q) == NULL || !RED(RIGHT(q)))
+		  && (LEFT(q) == NULL || !RED(LEFT(q))))
 		{
-		  q->red = 1;
+		  SETRED(q);
 		  r = p;
 		}
 	      else
 		{
-		  if (q->left == NULL || !q->left->red)
+		  if (LEFT(q) == NULL || !RED(LEFT(q)))
 		    {
-		      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;
+		      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
 		    {
-		      q->red = p->red;
-		      p->red = 0;
-		      q->left->red = 0;
-		      p->left = q->right;
-		      q->right = p;
-		      *pp = q;
+		      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;
@@ -578,7 +670,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
 	  --sp;
 	}
       if (r != NULL)
-	r->red = 0;
+	SETBLACK(r);
     }
 
   free (unchained);
@@ -597,16 +689,16 @@ trecurse (const void *vroot, __action_fn_t action, int level)
 {
   const_node root = (const_node) vroot;
 
-  if (root->left == NULL && root->right == NULL)
+  if (LEFT(root) == NULL && RIGHT(root) == NULL)
     (*action) (root, leaf, level);
   else
     {
       (*action) (root, preorder, level);
-      if (root->left != NULL)
-	trecurse (root->left, action, level + 1);
+      if (LEFT(root) != NULL)
+	trecurse (LEFT(root), action, level + 1);
       (*action) (root, postorder, level);
-      if (root->right != NULL)
-	trecurse (root->right, action, level + 1);
+      if (RIGHT(root) != NULL)
+	trecurse (RIGHT(root), action, level + 1);
       (*action) (root, endorder, level);
     }
 }
@@ -620,7 +712,7 @@ __twalk (const void *vroot, __action_fn_t action)
 {
   const_node root = (const_node) vroot;
 
-  CHECK_TREE (root);
+  CHECK_TREE ((node) root);
 
   if (root != NULL && action != NULL)
     trecurse (root, action, 0);
@@ -636,10 +728,10 @@ static void
 internal_function
 tdestroy_recurse (node root, __free_fn_t freefct)
 {
-  if (root->left != NULL)
-    tdestroy_recurse (root->left, freefct);
-  if (root->right != NULL)
-    tdestroy_recurse (root->right, 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);