fmalloc.c   [plain text]


/*
 * $Xorg: fmalloc.c,v 1.5 2001/02/09 02:06:19 xorgcvs Exp $
 *
Copyright 1992, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.
 *
 * Author:  Keith Packard, MIT X Consortium
 */

/* $XFree86: xc/util/memleak/fmalloc.c,v 3.14 2002/04/04 14:06:00 eich Exp $ */


/*
 * Leak tracing allocator -- using C lib malloc/free, tracks
 * all allocations.  When requested, performs a garbage-collection
 * style mark/sweep on static memory (data and stack), locating
 * objects referenced therein.  Recursively marks objects.
 * Sweeps through all allocations, warning of possible violations
 * (unreferenced allocated, referenced freed etc).
 */

#include    <stdio.h>

extern char **environ;
extern xf86DriverList;
extern etext;
extern _etext;
extern __data_start;
extern _end;

#ifndef TOP_OF_DATA
#define TOP_OF_DATA 0
#endif

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

#ifdef X_NOT_POSIX
#define NO_ATEXIT
#endif

typedef unsigned long mem;
typedef unsigned int  magic;

#ifdef HAS_GET_RETURN_ADDRESS
#define MAX_RETURN_STACK    16
#endif

#define MAX_FREED_MEMORY    (1*1024*1024)

#define ACTIVE_HEAD_MAGIC   0xff1111ff
#define ACTIVE_TAIL_MAGIC   0xee2222ee
#define ACTIVE_DATA_MAGIC   0xdd3333dd
#define FREED_HEAD_MAGIC    0xcc4444cc
#define FREED_TAIL_MAGIC    0xbb5555bb
#define FREED_DATA_MAGIC    0xcc6666cc

/*
 * the marked fields in each head have two bits - one indicating
 * references to the head of the block, and one indicating references
 * to the middle of the block
 */
#define UNREFERENCED	    0
#define REFERENCED_HEAD	    1
#define REFERENCED_MIDDLE   2

typedef struct _head {
    struct _head    *left, *right;
    struct _head    *next;
    int		    balance;
#ifdef HAS_GET_RETURN_ADDRESS
    mem		    returnStack[MAX_RETURN_STACK];
#endif
    mem		    *from;
    mem             *fromReturnStack;
    unsigned long   allocTime;
    unsigned long   freeTime;
    int		    size;
    int		    desiredsize;
    int		    actualSize;
    int		    marked;
    magic    	    headMagic;
} HeadRec, *HeadPtr;

typedef struct _tail {
    magic    	    tailMagic;
    magic    	    tailPad;
} TailRec, *TailPtr;

#define Header(p)	((HeadPtr) (((char *) (p)) - sizeof (HeadRec)))
#define DataForHead(h)	((mem *) ((h) + 1))
#define Tailer(p)	((TailPtr) (((char *) (p)) + Header(p)->size))
#define TailForHead(h)	(Tailer(DataForHead(h)))
#define RoundSize	(sizeof (mem))
#define RoundUp(s)	(((s) + RoundSize - 1) & ~(RoundSize - 1))
#define TotalSize(s)	((s) + sizeof (HeadRec) + sizeof (TailRec))
#define CheckInit()	if (!endOfStaticMemory) endOfStaticMemory = sbrk(0)
#define BlockContains(h,p)  (DataForHead(h) <= (p) && (p) < (mem *) TailForHead(h))

typedef HeadRec		tree;
typedef mem		*tree_data;

#define COMPARE_ADDR(a,b,op)	(((mem) (a)) op ((mem) (b)))
#define COMPARE(a,b,op,s)	((!s) ? \
			 COMPARE_ADDR(a,b,op) :\
			(((a)->actualSize op (b)->actualSize) || \
			 ((a)->actualSize == (b)->actualSize && \
			  COMPARE_ADDR(a,b,op))))
				

#define LESS_THAN(a,b,s)    COMPARE(a,b,<,s)
#define GREATER_THAN(a,b,s) COMPARE(a,b,>,s)

#define SEARCH(top,result,p) for (result = top; result;) {\
    if ((mem *) (p) < DataForHead(result)) \
	result = result->left; \
    else if ((mem *) TailForHead(result) < (mem *) (p)) \
	result = result->right; \
    else \
	break; \
}
    

static tree	*activeMemory, *freedMemory, *deadMemory;

static mem	*endOfStaticMemory = (mem *) TOP_OF_DATA;
static mem	*highestAllocatedMemory;

static int	freedMemoryTotal;
static int	freedMemoryCount;
static int	activeMemoryTotal;
static int	activeMemoryCount;
static int	deadMemoryTotal;
static int	unreferencedAllocatedTotal;
static int	unreferencedAllocatedCount;

int		FindLeakWarnMiddlePointers = 0;
unsigned long	FindLeakAllocBreakpoint = ~0;
unsigned long	FindLeakFreeBreakpoint = ~0;
unsigned long	FindLeakTime;
int		FindLeakCheckAlways = 0;
int		FindLeakValidateAlways = 0;
int		FindPrintAllocations = 0;

static void MarkActiveBlock ();
static int  tree_insert (), tree_delete ();
void	    CheckMemory ();
char	    *malloc (), *realloc (), *calloc ();
void	    free ();
extern char *sbrk ();

#ifdef HAS_GET_RETURN_ADDRESS
static void
PrintReturnStack (m, ra)
    char    *m;
    mem	    *ra;
{
    int	    i;

    fprintf (stderr, "   %s:", m);
    for (i = 0; i < MAX_RETURN_STACK && ra[i]; i++)
	fprintf (stderr, " 0x%lx", ra[i]);
    fprintf (stderr, "\n");
}
#endif

static void
MemError (s, h, ourRet)
    char	*s;
    HeadPtr	h;
    int		ourRet;
{
    mem *ra;
    int	i;

    if (h)
    {
	fprintf (stderr, "%s 0x%08lx (size %d) (from 0x%lx) ",
	     s, DataForHead(h), h->desiredsize, h->from);
#ifdef HAS_GET_RETURN_ADDRESS
	if (h->fromReturnStack)
	    PrintReturnStack ("\nallocated at", h->fromReturnStack);
	else
	  fprintf(stderr,"\n");
	PrintReturnStack ("Saved return stack", h->returnStack);
#else
	fprintf(stderr,"\n");
#endif
    }
    else 
	fprintf (stderr, "%s\n", s);
#ifdef HAS_GET_RETURN_ADDRESS
    if (ourRet)
    {
	mem	returnStack[MAX_RETURN_STACK];

	getStackTrace (returnStack, MAX_RETURN_STACK);
	PrintReturnStack ("Current return stack", returnStack);
    }
#endif
}

static void
MarkMemoryRegion (low, high)
    mem	*low, *high;
{
    mem	**start = (mem **) low, **end = (mem **) high;
    mem	*p;

    while (start < end) {
	p = *start;
	if (endOfStaticMemory <= p && p < highestAllocatedMemory)
	    MarkActiveBlock (p, (mem *) start);
	start++;
    }
}

static void
MarkActiveBlock (p, from)
    mem   *p, *from;
{
    HeadPtr	h, hh;
    int		marked;
    int		oldMarked;

    SEARCH(activeMemory, h, p)
    if (h) {
	marked = REFERENCED_HEAD;
        if (p != DataForHead(h))
	    marked = REFERENCED_MIDDLE;
	oldMarked = h->marked;
	if (!(oldMarked & marked))
	{
	    h->marked |= marked;
	    h->from = from;
#ifdef HAS_GET_RETURN_ADDRESS
	    SEARCH(activeMemory, hh, h->from)
	      if (hh) 
		h->fromReturnStack = hh->returnStack;
#endif
	    if (!oldMarked)
		MarkMemoryRegion (DataForHead(h), (mem *) TailForHead(h));
	}
	return;
    }
    SEARCH(freedMemory, h, p)
    if (h)
    {
	marked = REFERENCED_HEAD;
        if (p != DataForHead(h))
	    marked = REFERENCED_MIDDLE;
	if (!(h->marked & marked))
	{
	    h->marked |= marked;
	    h->from = from;
#ifdef HAS_GET_RETURN_ADDRESS
	    SEARCH(activeMemory, hh, h->from)
            if (hh) 
		h->fromReturnStack = hh->returnStack;
#endif
	}
	return;
    }
}

static void
ClearTree (t)
    tree    *t;
{
    if (!t)
	return;
    ClearTree (t->left);
    t->marked = 0;
    t->from = 0;
    ClearTree (t->right);
}

static void
SweepActiveTree (t)
    tree    *t;
{
    if (!t)
	return;
    SweepActiveTree (t->left);
    if (!t->marked) {
	unreferencedAllocatedTotal += t->desiredsize;
	unreferencedAllocatedCount++;
	MemError ("Unreferenced allocated", t, FALSE);
    }
    else if (!(t->marked & REFERENCED_HEAD))
	MemError ("Referenced allocated middle", t, FALSE);
    SweepActiveTree (t->right);
}

/*
 * run a thread through the tree at the same time
 * - the thread runs 
 *
 * root -> left_child ... -> right_child ... -> null
 */

static tree *
SweepFreedTree (t)
    tree    *t;
{
    tree    *left_last, *right_last;

    if (!t)
	return 0;

    left_last = SweepFreedTree (t->left);
    if (t->marked)
    {
	if (t->marked & REFERENCED_HEAD)
	    MemError ("Referenced freed base", t, FALSE);
	else if (FindLeakWarnMiddlePointers)
	    MemError ("Referenced freed middle", t, FALSE);
    }
    right_last = SweepFreedTree (t->right);

    if (t->left)
	t->next = t->left;
    else
	t->next = t->right;
    if (left_last)
	left_last->next = t->right;
    if (!right_last)
	right_last = left_last;
    if (!right_last)
	right_last = t;
    return right_last;
}

static void
SweepFreedMemory ()
{
    tree    *t, *n;
    int	    count, shouldCount;

    (void) SweepFreedTree (freedMemory);
    count = 0;
    shouldCount = freedMemoryCount;
    for (t = freedMemory; t; t = n) {
	n = t->next;
	count++;
	if (!t->marked) 
	{
	    (void) tree_delete (&freedMemory, t, FALSE);
	    freedMemoryTotal -= t->desiredsize;
	    freedMemoryCount--;
	    tree_insert (&deadMemory, t, TRUE);
	}
    }
    if (count != shouldCount)
	abort ();
}

static void
ValidateTree (head, headMagic, tailMagic, bodyMagic, mesg)
    tree    *head;
    mem	    headMagic, tailMagic, bodyMagic;
    char    *mesg;
{
    TailPtr	tail;
    magic    	*p;
    int		i;

    if (!head)
	return;
    ValidateTree (head->left, headMagic, tailMagic, bodyMagic, mesg);
    tail = TailForHead (head);
    if (head->headMagic != headMagic)
	MemError (mesg, head, FALSE);
    if (tail->tailMagic != tailMagic)
	MemError (mesg, head, FALSE);
    if (bodyMagic) {
	i = head->size / sizeof (magic);
	p = (magic *) DataForHead(head);
	while (i--) {
	    if (*p++ != bodyMagic)
	    {
		MemError (mesg, head, FALSE);
		break;
	    }
	}
    }
    ValidateTree (head->right, headMagic, tailMagic, bodyMagic, mesg);
}

static void
ValidateActiveMemory ()
{
    ValidateTree (activeMemory, ACTIVE_HEAD_MAGIC, ACTIVE_TAIL_MAGIC,
		  0, "Store outside of active memory");
}

static void
ValidateFreedMemory ()
{
    ValidateTree (freedMemory, FREED_HEAD_MAGIC, FREED_TAIL_MAGIC,
		  FREED_DATA_MAGIC, "Store into freed memory");
}

static void
AddActiveBlock (h)
    HeadPtr	h;
{
    TailPtr	t = TailForHead(h);
    magic    	*p;
    int		i;

    tree_insert (&activeMemory, h, FALSE);
    if ((mem *) t > highestAllocatedMemory)
	highestAllocatedMemory = (mem *) t;

    /*
     * Breakpoint position - assign FindLeakAllocBreakpoint with
     * debugger and set a breakpoint in the conditional clause below
     */
    if (FindLeakTime == FindLeakAllocBreakpoint)
	h->headMagic = ACTIVE_HEAD_MAGIC;	    /* set breakpoint here */

    h->allocTime = FindLeakTime++;

    h->headMagic = ACTIVE_HEAD_MAGIC;
    t->tailMagic = ACTIVE_TAIL_MAGIC;
    i = h->size / sizeof (magic);
    p = (magic *) DataForHead(h);
    while (i--)
	*p++ = ACTIVE_DATA_MAGIC;
    activeMemoryTotal += h->desiredsize;
    activeMemoryCount++;
}

static void
RemoveActiveBlock (h)
    HeadPtr	h;
{
    activeMemoryTotal -= h->desiredsize;
    activeMemoryCount--;
    tree_delete (&activeMemory, h, FALSE);
}

static void
AddFreedBlock (h)
    HeadPtr	h;
{
    TailPtr	t = TailForHead(h);
    int		i;
    magic    	*p;

    tree_insert (&freedMemory, h, FALSE);

    /*
     * Breakpoint position - assign FindLeakFreeBreakpoint with
     * debugger and set a breakpoint in the conditional clause below
     */
    if (FindLeakTime == FindLeakFreeBreakpoint)
	h->headMagic = FREED_HEAD_MAGIC;	    /* set breakpoint here */

    h->freeTime = FindLeakTime++;

    h->headMagic = FREED_HEAD_MAGIC;
    t->tailMagic = FREED_TAIL_MAGIC;
    i = h->size / sizeof (magic);
    p = (magic *) DataForHead(h);
    while (i--)
	*p++ = FREED_DATA_MAGIC;
    freedMemoryTotal += h->desiredsize;
    freedMemoryCount++;
    /* GC if we've got piles of unused memory */
    if (freedMemoryTotal - deadMemoryTotal >= MAX_FREED_MEMORY)
	CheckMemory ();
}
#if 0
static void
WarnReferencedRange(rangeStart,rangeEnd,from,to)
     mem *rangeStart;
     mem *rangeEnd;
     mem *from;
     mem *to;
{
  mem *range = rangeStart;

  while ( range < rangeEnd) {
    if ((mem *)*range >= from && (mem *)*range <= to)
      fprintf(stderr, "0x%lx still points into newly allocated range\n",
	      (unsigned long) range);
    range++;
  }
}

static void
WarnReferencedTree(head, from, to)
     tree *head;
     char *from;
     char *to;
{
  if (!head) return;
  WarnReferencedTree(head->right,from,to);
  WarnReferencedRange(DataForHead(head),TailForHead(head),from,to);
  WarnReferencedTree(head->left,from,to);
}

static void
WarnReferenced(from, to)
     char *from;
     char *to;
{
    mem	foo;

    foo = 1;
    WarnReferencedTree(activeMemory,from,to);
    WarnReferencedRange(BOTTOM_OF_DATA, endOfStaticMemory,from,to);
    WarnReferencedRange(&foo, TOP_OF_STACK,from,to);
}  
#endif
/*
 * Entry points:
 *
 *  CheckMemory ()		--	Verifies heap
 *  malloc (size)		--	Allocates memory
 *  free (old)			--	Deallocates memory
 *  realloc (old, size)		--	Allocate, copy, free
 *  calloc (num, size_per)	--	Allocate and zero
 */

void
CheckMemory ()
{
#if 0
    mem	foo;

    unreferencedAllocatedTotal = 0;
    unreferencedAllocatedCount = 0;
    foo = 1;
    fprintf (stderr, "\nCheckMemory\n");
    fprintf (stderr, "Static Memory Area: 0x%lx to 0x%lx\n",
               BOTTOM_OF_DATA, endOfStaticMemory);
    fprintf (stderr, "%d bytes active memory in %d allocations\n",
	     activeMemoryTotal, activeMemoryCount);
    fprintf (stderr, "%d bytes freed memory held from %d allocations\n",
	    freedMemoryTotal, freedMemoryCount);
    ValidateActiveMemory ();
    ValidateFreedMemory ();
    ClearTree (activeMemory);
    ClearTree (freedMemory);
    MarkMemoryRegion (BOTTOM_OF_DATA, endOfStaticMemory);
    MarkMemoryRegion (&foo, TOP_OF_STACK);
    SweepActiveTree (activeMemory);
    SweepFreedMemory ();
    fprintf (stderr, "%d bytes freed memory still held from %d allocations\n",
	     freedMemoryTotal, freedMemoryCount);
    fprintf (stderr, 
	   "%d bytes of allocated memory not referenced from %d allocations\n",
	     unreferencedAllocatedTotal,unreferencedAllocatedCount);
    deadMemoryTotal = freedMemoryTotal;
    fprintf (stderr, "CheckMemory done\n");
#endif
}

/*
 * Allocator interface -- malloc and free (others in separate files)
 */

#define CORE_CHUNK  16384

static char	*core;
static unsigned	core_left;
static unsigned	total_core_used;

static char *
morecore (size)
    unsigned	size;
{
    unsigned	alloc_size;
    char	*alloc, *newcore;

    if (core_left < size)
    {
	alloc_size = (size + CORE_CHUNK - 1) & ~(CORE_CHUNK-1);
    	newcore = sbrk (alloc_size);
    	if (((long) newcore) == -1)
	    return 0;
	core = newcore;
	core_left = alloc_size;
	total_core_used += alloc_size;
    }
    alloc = core;
    core += size;
    core_left -= size;
    return alloc;
}

char *
malloc (desiredsize)
    unsigned	desiredsize;
{
    char	*ret;
    unsigned	size;
    unsigned	totalsize;
    HeadPtr	h;

    if (!endOfStaticMemory)
	endOfStaticMemory = (mem *) sbrk(0);
    if (FindLeakCheckAlways)
	CheckMemory ();
    else if (FindLeakValidateAlways)
    {
	ValidateActiveMemory ();
	ValidateFreedMemory ();
    }
    size = RoundUp(desiredsize);
    totalsize = TotalSize (size);

    h = deadMemory;
    while (h)
    {
	if (h->actualSize == size)
	    break;
	else if (h->actualSize < size)
	    h = h->right;
	else {
	    if (!h->left)
		break;
	    h = h->left;
	}
    }
    if (h) 
    {
	tree_delete (&deadMemory, h, TRUE);
    }
    else
    {
	h = (HeadPtr) morecore (totalsize);
	if (!h)
	    return NULL;
	h->actualSize = size;
    }
    h->desiredsize = desiredsize;
    h->size = size;
#ifdef HAS_GET_RETURN_ADDRESS
    getStackTrace (h->returnStack, MAX_RETURN_STACK);
#endif
    AddActiveBlock (h);
    ret =  (char *) DataForHead(h);
    if (FindPrintAllocations) {
	fprintf(stderr,"Allocated %i bytes at 0x%lx\n",desiredsize,ret);
#ifdef HAS_GET_RETURN_ADDRESS
	PrintReturnStack ("at",h->returnStack);
#endif
    }
    return ret;
}

void
free (p)
    char    *p;
{
    HeadPtr	h;
    static int	beenHere;
    
#ifndef NO_ATEXIT
    /* do it at free instead of malloc to avoid recursion? */
    if (!beenHere)
    {
	beenHere = TRUE;
	atexit (CheckMemory);
    }
#endif
    if (!p)
    {
	MemError ("Freeing NULL", (HeadPtr) 0, TRUE);
	return;
    }
    SEARCH (activeMemory, h, p);
    if (!h)
    {
	SEARCH(freedMemory, h, p);
	if (h)
	    MemError ("Freeing something twice", h, TRUE);
	else
	    MemError ("Freeing something never allocated", h, TRUE);
	return;
    }
    if (DataForHead(h) != (mem *) p)
    {
	MemError ("Freeing pointer to middle of allocated block", h, TRUE);
	return;
    }
    if (h->headMagic != ACTIVE_HEAD_MAGIC ||
	TailForHead(h)->tailMagic != ACTIVE_TAIL_MAGIC)
	MemError ("Freeing corrupted data", h, TRUE);
    RemoveActiveBlock (h);
#ifdef HAS_GET_RETURN_ADDRESS
    getStackTrace (h->returnStack, MAX_RETURN_STACK);
#endif
    AddFreedBlock (h);
    if (FindLeakCheckAlways)
	CheckMemory ();
    else if (FindLeakValidateAlways)
    {
	ValidateActiveMemory ();
	ValidateFreedMemory ();
    }
    if (FindPrintAllocations) {
	fprintf(stderr,"Freed at:  0x%lx\n",p);
	PrintReturnStack ("at",h->returnStack);
    }

}

char *
realloc (old, desiredsize)
    char	*old;
    unsigned	desiredsize;
{
    char    *new;
    HeadPtr  h, fh;
    int	    copysize;

    new = malloc (desiredsize);
    if (!new)
	return NULL;
    if (!old)
	return new;
    SEARCH(activeMemory, h, old);
    if (!h)
    {
	SEARCH(freedMemory, fh, old);
	if (fh)
	    MemError ("Reallocing from freed data", fh, TRUE);
	else
	    MemError ("Reallocing from something not allocated", h, TRUE);
    }
    else
    {
	if (DataForHead(h) != (mem *) old)
	{
	    MemError ("Reallocing from pointer to middle of allocated block", h, TRUE);
	}
	else
	{
	    if (h->headMagic != ACTIVE_HEAD_MAGIC ||
		TailForHead(h)->tailMagic != ACTIVE_TAIL_MAGIC)
		MemError ("Reallocing corrupted data", h, TRUE);
	    copysize = desiredsize;
	    if (h->desiredsize < desiredsize)
		copysize = h->desiredsize;
#ifdef SVR4
	    memmove (new, old, copysize);
#else
	    bcopy (old, new, copysize);
#endif
	    RemoveActiveBlock (h);
#ifdef HAS_GET_RETURN_ADDRESS
	    getStackTrace (h->returnStack, MAX_RETURN_STACK);
#endif
	    AddFreedBlock (h);
	}
    }
    if (FindPrintAllocations) {
	fprintf(stderr,"Freed at: 0x%lx\n",old);
	fprintf(stderr,"Reallocated: %i bytes at:  0x%lx\n",desiredsize,new);
#ifdef HAS_GET_RETURN_ADDRESS
        PrintReturnStack ("at", h->returnStack);
#endif
    }
    return new;
}

char *
calloc (num, size)
    unsigned	num, size;
{
    char *ret;

    size *= num;
    ret = malloc (size);
    if (!ret)
	return NULL;
#ifdef SVR4
    memset (ret, 0, size);
#else
    bzero (ret, size);
#endif
    return ret;
}

/*
 * Semi-Balanced trees (avl).  This only contains two
 * routines - insert and delete.  Searching is
 * reserved for the client to write.
 */

static	rebalance_right (), rebalance_left ();

/*
 * insert a new node
 *
 * this routine returns non-zero if the tree has grown
 * taller
 */

static int
tree_insert (treep, new, bySize)
tree	**treep;
tree	*new;
int	bySize;
{
	if (!(*treep)) {
		(*treep) = new;
		(*treep)->left = 0;
		(*treep)->right = 0;
		(*treep)->balance = 0;
		return 1;
	} else {
		if (LESS_THAN (*treep, new, bySize)) {
			if (tree_insert (&((*treep)->right), new, bySize))
				switch (++(*treep)->balance) {
				case 0:
					return 0;
				case 1:
					return 1;
				case 2:
					(void) rebalance_right (treep);
				}
			return 0;
		} else if (GREATER_THAN(*treep, new, bySize)) {
			if (tree_insert (&((*treep)->left), new, bySize))
				switch (--(*treep)->balance) {
				case 0:
					return 0;
				case -1:
					return 1;
				case -2:
					(void) rebalance_left (treep);
				}
			return 0;
		} else {
			return 0;
		}
	}
	/*NOTREACHED*/
}
					
/*
 * delete a node from a tree
 *
 * this routine return non-zero if the tree has been shortened
 */

static int
tree_delete (treep, old, bySize)
tree	**treep;
tree	*old;
int	bySize;
{
	tree	*to_be_deleted;
	tree	*replacement;
	tree	*replacement_parent;
	int	replacement_direction;
	int	delete_direction;
	tree	*swap_temp;
	int	balance_temp;

	if (!*treep)
		/* node not found */
		return 0;
	if (LESS_THAN(*treep, old, bySize)) {
		if (tree_delete (&(*treep)->right, old, bySize))
			/*
			 * check the balance factors
			 * Note that the conditions are
			 * inverted from the insertion case
			 */
			switch (--(*treep)->balance) {
			case 0:
				return 1;
			case -1:
				return 0;
			case -2:
				return rebalance_left (treep);
			}
		return 0;
	} else if (GREATER_THAN(*treep, old, bySize)) {
		if (tree_delete (&(*treep)->left, old, bySize))
			switch (++(*treep)->balance) {
			case 0:
				return 1;
			case 1:
				return 0;
			case 2:
				return rebalance_right (treep);
			}
		return 0;
	} else {
		to_be_deleted = *treep;
		/*
		 * find an empty down pointer (if any)
		 * and rehook the tree
		 */
		if (!to_be_deleted->right) {
			(*treep) = to_be_deleted->left;
			return 1;
		} else if (!to_be_deleted->left) {
			(*treep) = to_be_deleted->right;
			return 1;
		} else {
		/* 
		 * if both down pointers are full, then
		 * move a node from the bottom of the tree up here.
		 *
		 * This builds an incorrect tree -- the replacement
		 * node and the to_be_deleted node will not
		 * be in correct order.  This doesn't matter as
		 * the to_be_deleted node will obviously not leave
		 * this routine alive.
		 */
			/*
			 * if the tree is left heavy, then go left
			 * else go right
			 */
			replacement_parent = to_be_deleted;
			if (to_be_deleted->balance == -1) {
				delete_direction = -1;
				replacement_direction = -1;
				replacement = to_be_deleted->left;
				while (replacement->right) {
					replacement_parent = replacement;
					replacement_direction = 1;
					replacement = replacement->right;
				}
			} else {
				delete_direction = 1;
				replacement_direction = 1;
				replacement = to_be_deleted->right;
				while (replacement->left) {
					replacement_parent = replacement;
					replacement_direction = -1;
					replacement = replacement->left;
				}
			}
			/*
			 * swap the replacement node into
			 * the tree where the node is to be removed
			 * 
			 * this would be faster if only the data
			 * element was swapped -- but that
			 * won't work for findleak.  The alternate
			 * code would be:
			   data_temp = to_be_deleted->data;
			   to _be_deleted->data = replacement->data;
			   replacement->data = data_temp;
			 */
			swap_temp = to_be_deleted->left;
			to_be_deleted->left = replacement->left;
			replacement->left = swap_temp;
			swap_temp = to_be_deleted->right;
			to_be_deleted->right = replacement->right;
			replacement->right = swap_temp;
			balance_temp = to_be_deleted->balance;
			to_be_deleted->balance = replacement->balance;
			replacement->balance = balance_temp;
			/*
			 * if the replacement node is directly below
			 * the to-be-removed node, hook the to_be_deleted
			 * node below it (instead of below itself!)
			 */
			if (replacement_parent == to_be_deleted)
				replacement_parent = replacement;
			if (replacement_direction == -1)
				replacement_parent->left = to_be_deleted;
			else
				replacement_parent->right = to_be_deleted;
			(*treep) = replacement;
			/*
			 * delete the node from the sub-tree
			 */
			if (delete_direction == -1) {
				if (tree_delete (&(*treep)->left, old, bySize)) {
					switch (++(*treep)->balance) {
					case 2:
						abort ();
					case 1:
						return 0;
					case 0:
						return 1;
					}
				}
				return 0;
			} else {
				if (tree_delete (&(*treep)->right, old, bySize)) {
					switch (--(*treep)->balance) {
					case -2:
						abort ();
					case -1:
						return 0;
					case 0:
						return 1;
					}
				}
				return 0;
			}
		}
	}
	/*NOTREACHED*/
}

/*
 * two routines to rebalance the tree.
 *
 * rebalance_right -- the right sub-tree is too long
 * rebalance_left --  the left sub-tree is too long
 *
 * These routines are the heart of avl trees, I've tried
 * to make their operation reasonably clear with comments,
 * but some study will be necessary to understand the
 * algorithm.
 *
 * these routines return non-zero if the resultant
 * tree is shorter than the un-balanced version.  This
 * is only of interest to the delete routine as the
 * balance after insertion can never actually shorten
 * the tree.
 */
 
static
rebalance_right (treep)
tree	**treep;
{
	tree	*temp;
	/*
	 * rebalance the tree
	 */
	if ((*treep)->right->balance == -1) {
		/* 
		 * double whammy -- the inner sub-sub tree
		 * is longer than the outer sub-sub tree
		 *
		 * this is the "double rotation" from
		 * knuth.  Scheme:  replace the tree top node
		 * with the inner sub-tree top node and
		 * adjust the maze of pointers and balance
		 * factors accordingly.
		 */
		temp = (*treep)->right->left;
		(*treep)->right->left = temp->right;
		temp->right = (*treep)->right;
		switch (temp->balance) {
		case -1:
			temp->right->balance = 1;
			(*treep)->balance = 0;
			break;
		case 0:
			temp->right->balance = 0;
			(*treep)->balance = 0;
			break;
		case 1:
			temp->right->balance = 0;
			(*treep)->balance = -1;
			break;
		}
		temp->balance = 0;
		(*treep)->right = temp->left;
		temp->left = (*treep);
		(*treep) = temp;
		return 1;
	} else {
		/*
		 * a simple single rotation
		 *
		 * Scheme:  replace the tree top node
		 * with the sub-tree top node 
		 */
		temp = (*treep)->right->left;
		(*treep)->right->left = (*treep);
		(*treep) = (*treep)->right;
		(*treep)->left->right = temp;
		/*
		 * only two possible configurations --
		 * if the right sub-tree was balanced, then
		 * *both* sides of it were longer than the
		 * left side, so the resultant tree will
		 * have a long leg (the left inner leg being
		 * the same length as the right leg)
		 */
		if ((*treep)->balance == 0) {
			(*treep)->balance = -1;
			(*treep)->left->balance = 1;
			return 0;
		} else {
			(*treep)->balance = 0;
			(*treep)->left->balance = 0;
			return 1;
		}
	}
}

static
rebalance_left (treep)
tree	**treep;
{
	tree	*temp;
	/*
	 * rebalance the tree
	 */
	if ((*treep)->left->balance == 1) {
		/* 
		 * double whammy -- the inner sub-sub tree
		 * is longer than the outer sub-sub tree
		 *
		 * this is the "double rotation" from
		 * knuth.  Scheme:  replace the tree top node
		 * with the inner sub-tree top node and
		 * adjust the maze of pointers and balance
		 * factors accordingly.
		 */
		temp = (*treep)->left->right;
		(*treep)->left->right = temp->left;
		temp->left = (*treep)->left;
		switch (temp->balance) {
		case 1:
			temp->left->balance = -1;
			(*treep)->balance = 0;
			break;
		case 0:
			temp->left->balance = 0;
			(*treep)->balance = 0;
			break;
		case -1:
			temp->left->balance = 0;
			(*treep)->balance = 1;
			break;
		}
		temp->balance = 0;
		(*treep)->left = temp->right;
		temp->right = (*treep);
		(*treep) = temp;
		return 1;
	} else {
		/*
		 * a simple single rotation
		 *
		 * Scheme:  replace the tree top node
		 * with the sub-tree top node 
		 */
		temp = (*treep)->left->right;
		(*treep)->left->right = (*treep);
		(*treep) = (*treep)->left;
		(*treep)->right->left = temp;
		/*
		 * only two possible configurations --
		 * if the left sub-tree was balanced, then
		 * *both* sides of it were longer than the
		 * right side, so the resultant tree will
		 * have a long leg (the right inner leg being
		 * the same length as the left leg)
		 */
		if ((*treep)->balance == 0) {
			(*treep)->balance = 1;
			(*treep)->right->balance = -1;
			return 0;
		} else {
			(*treep)->balance = 0;
			(*treep)->right->balance = 0;
			return 1;
		}
	}
}

#ifdef DEBUG

static 
depth (treep)
tree	*treep;
{
	int	ldepth, rdepth;

	if (!treep)
		return 0;
	ldepth = depth (treep->left);
	rdepth = depth (treep->right);
	if (ldepth > rdepth)
		return ldepth + 1;
	return rdepth + 1;
}

static tree *
left_most (treep)
tree	*treep;
{
	while (treep && treep->left)
		treep = treep->left;
	return treep;
}

static tree *
right_most (treep)
tree	*treep;
{
	while (treep && treep->right)
		treep = treep->right;
	return treep;
}

tree_verify (treep)
tree	*treep;
{
	tree_data	left_data, right_data;

	if (!treep)
		return 1;
	if (treep->left)
		left_data = right_most (treep->left)->data;
	else
		left_data = treep->data - 1;
	if (treep->right)
		right_data = left_most (treep->right)->data;
	else
		right_data = treep->data + 1;
	if (treep->data < left_data || treep->data > right_data) {
		abort ();
		return 0;
	}
	if (treep->balance != depth (treep->right) - depth (treep->left)) {
		abort ();
		return 0;
	}
	return tree_verify (treep->left) && tree_verify (treep->right);
}

#endif