/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	buddyAlloc
 *
 * DESCRIPTION:	This module contains the routines for implementing
 *		a buddy system storage allocator. The algorithm used
 *		is described in Knuth, _The Art of Computer Programming_,
 *		Volume 1.
 *
 *		This package was written to provide a fast storage
 *		allocator, primarily for the RCI message passing primitives.
 * 
 * CAVEATS:	The allocator really isn't that fast. The first-fit allocator
 *		(in 'firstAlloc.c') seems to be faster in some tests.
 *		This allocator also chokes if you allocate a size
 *		that is equal to a power of 2, because we don't adjust
 *		for the overhead that goes on top of that. 
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include "../h/cdefs.h"
#include "../h/buddyAlloc.h"

typedef unsigned int u_int;

/*---------------------------------------------------------------------------

	Results of speed testing:

	Using the testing program below, and running first with no option
	and secondly with the -f (fake) option to determine the program
	overhead, we got an average allocate/free time of 250 usec. This
	was using unoptimized code.

	With slightly cleaned up code (registers, etc) we got down to about
	210 usec. This is liveable for the moment.

 ---------------------------------------------------------------------------*/

buddyAllocInit (bi, pool, poolSize, smallSize, largeSize)
BINFO *bi;
char *pool;
int poolSize, smallSize, largeSize;
{	
	register int i, numBlocks;
	register BHEAD *hp;

	int smallK;
	int largeK;
	int poolK;
	BHEAD *avail;

	bi->base = (void*)pool;
	avail = bi->avail;

	smallK = largeK = poolK = 0;
	while (poolSize)
  	 { poolSize >>= 1;
	   poolK++;
	 }
	while (smallSize)
  	 { smallSize >>= 1;
	   smallK++;
	 }
	while (largeSize)
  	 { largeSize >>= 1;
	   largeK++;
	 }
	largeSize = (1<<(largeK-1));

	for (i=0; i<(largeK); i++)
	 { avail[i].prev = avail[i].next = &avail[i];
	 }
	
	/* set up the pool so that we have a set of areas of the
	   largest allowed size */

	numBlocks = (1<<(poolK-largeK));
	hp = (BHEAD *)(&pool[(numBlocks-1)*largeSize]);
	while (numBlocks--)
	 { hp->size = largeK-1;
	   hp->used = 0;
	   hp->next = avail[largeK-1].next;
	   hp->prev = &avail[largeK-1];
	   avail[largeK-1].next->prev = avail[largeK-1].next = hp;
	   hp = (BHEAD *)((u_int)hp - largeSize);
	 }

	bi->largeK = largeK;
	bi->smallK = smallK;
}

char *buddyAlloc (bi, size)
BINFO *bi;
int size;
{
	register int k, j, x;
	register BHEAD *node;
	register BHEAD *split;
	register BHEAD *avail = bi->avail;

	int smallK = bi->smallK;
	int largeK = bi->largeK;

	size += sizeof(BHEAD);

	x = 1 << (smallK-1);		/* determine block size */
	for (k = (smallK-1); k < largeK; k++)
	 { if (size <= x)
	    { break;
	    }
	   x <<= 1;
	 }
	if (k == largeK)
	 { return (NULL);
	 }
	for (j = k; j < largeK; j++)	
	 { if (&avail[j] != avail[j].next)
	    { break;
	    }
	 }
	if (j == largeK)
	 { return (NULL);
	 }
	node = avail[j].next;
	avail[j].next = node->next;
	node->next->prev = &avail[j];
	node->size = k;
	node->used = 1;

	while (j-- != k)
	 { split = (BHEAD *)((u_int)(node) + (1<<j));
	   split->size = j;
	   split->used = 0;
	   split->prev = split->next = &avail[j];
	   avail[j].next = split;
	   avail[j].prev = split;
	 }
	return ((char *)((u_int)node + sizeof(BHEAD)));
}

buddyFree (bi, addr)
BINFO *bi;
register BHEAD *addr;
{
	register BHEAD *buddy;
	register BHEAD *avail = bi->avail;
	register int k;

	int largeK = bi->largeK;
	void *base = bi->base;

	addr--;
	k = addr->size;
	while (k < largeK-1)
/* UGH!*/{ buddy = (BHEAD*)( ((((u_int)addr)-((u_int)base))^(1<<k))+((u_int)base));
	   if (buddy->size != k || buddy->used)
	    { break;
	    }

	   buddy->prev->next = buddy->next;
	   buddy->next->prev = buddy->prev;

	   k++;
	   if (buddy < addr)
	    { addr = buddy;
	    }
	 }
	addr->size = k;
	addr->used = 0;
	addr->next = avail[k].next;
	addr->next->prev = addr;
	addr->prev = &avail[k];
	avail[k].next = addr;
}

#if TEST

#define LOWER_K		 7
#define UPPER_K		12	
#define POOL_K		19	
#define SIZE		(1<<(POOL_K-UPPER_K))

BINFO binfo;
char  pool[1<<(POOL_K-1)];

struct _MEMP {
	char *mem;
	struct _MEMP *next;
	struct _MEMP *prev;
};

static struct _MEMP mempPool[SIZE];
static struct _MEMP mempHead;
static struct _MEMP *free;
static int numMemp;

static char fake = 0;
static char writeOver = 0;
static char buf[1<<(POOL_K-1)];

char *get(size)
int size;
{
	int i;
	char *mp;

	size -= sizeof(BHEAD);

	if ((mp = (fake ? buf : buddyAlloc (&binfo, size))) == NULL)
	 { return (NULL);
	 }
	if (writeOver)
  	 { for (i=0; i<size; i++)
	    { mp[i] = '\1';
	    }
	 }
	return (mp);
}

main(argc, argv)
int argc;
char **argv;
{
	int n, i, numAlloc, numFree, numSkip;
	struct _MEMP *mp;

	int upperK = UPPER_K;
 	int lowerK = LOWER_K;
	int poolK = POOL_K;

	while (--argc > 0)
  	 { ++argv;
	   if (ARGUMENT ("-f"))
	    { fake = 1;
	    }
	   else if (ARGUMENT ("-w"))
	    { writeOver = 1;
	    }
	   else if (ARGUMENT ("-u") && --argc > 0)
	    { upperK = atoi (*++argv);
	    }
	   else if (ARGUMENT ("-l") && --argc > 0)
	    { lowerK = atoi (*++argv);
	    }
	   else if (ARGUMENT ("-p") && --argc > 0)
	    { poolK = atoi (*++argv);
	    }
	   else
	    { 
usageMsg (0, "buddayalloc [-f] [-w] [-u <upperK>] [-l <lowerK>] [-p <poolK>] \n");
	    }
	 }

	if (lowerK > upperK)
  	 { fatalMsg (-1, "lowerK (%d) is greater than upperK (%d)\n",
	   	  lowerK, upperK);
	 }
 	if (lowerK > POOL_K || upperK > POOL_K || poolK > POOL_K)
  	 { 
fatalMsg (-1, "lowerK, upperK, and poolK cannot exceed %d\n", UPPER_K);
	 }

	mempHead.next = mempHead.prev = &mempHead;
	numMemp = 0;
	for (i=0; i<SIZE; i++)
	 { mp = &mempPool[i];
	   mp->next = free;
	   free = mp;
	 }

	buddyAllocInit (&binfo, pool, (1<<(poolK-1)), 
				      (1<<(lowerK-1)), (1<<(upperK-1)));

	n = 1;
	while (1)
	 { /* allocate some random number of times */

	   numAlloc = irandom (1,10);
	   if (numAlloc + numMemp > SIZE)
	    { numAlloc = SIZE - numMemp;
	    }
	   for (i=0; i<numAlloc; i++)
	    { mp = free;
	      free = mp->next;
	      mp->mem = get (irandom(15,(1<<(upperK-1))));
	      if (mp->mem == NULL)
	       { printf ("get failed on try %d\n", n);
	       }
	      mp->next = mempHead.next;
	      mp->prev = &mempHead;
	      mempHead.next->prev = mp;
	      mempHead.next = mp;
	    }
	   numMemp += numAlloc;

	   numFree = irandom (1,10);
	   if (numMemp < numFree)
	    { numFree = numMemp;
	    }
	   numSkip = irandom (0,numMemp-numFree);
	   mp = mempHead.next;
	   for (i=0; i<numSkip; i++)
	    { mp = mp->next;
	    }
	   for (i=0; i<numFree; i++)
	    { struct _MEMP *mp2;
	      mp2 = mp->next;
	      mp->prev->next = mp->next;
	      mp->next->prev = mp->prev;
	      if (!fake)
	       { buddyFree (&binfo, mp->mem);
	       }
	      mp->next = free;
	      free = mp;
	      mp = mp2;
	    }
	   numMemp -= numFree;
	   if ((n / 1000) != ((n+numAlloc) / 1000))
	    { printf ("%7d allocations\n", n);
	    }
	   n += numAlloc;
	 }
}

#endif

#ifdef notdef

buddyCheck()
{
 int i;
 int n;
 int a;
 int odd;
 int max;
 BHEAD *bh1;
 BHEAD *bh2;

 max = base + (1 << (poolK-1));
 for (i=0;i<largeK;i++)
  { bh1 = avail[i].next;
    bh2 = bh1;
    n = 1;
    odd = 1;
    while (bh1 != &avail[i])
     { a = (u_int) bh1;
       if ((a != (u_int)&avail[i]) && ((a < base) || (a >= max)))
	{ printf("bad pointer for size %d, block %d, addr %x\n",
			i,n,a);
	  diehorribly();
	}
       a = (u_int) bh1->prev;
       if ((a != (u_int)&avail[i]) && ((a < base) || (a >= max)))
	{ printf("bad prev link for size %d, block %d, addr %x\n",
			i,n,a);
	  diehorribly();
	}
       a = (u_int) bh1->next;
       if ((a != (u_int)&avail[i]) && ((a < base) || (a >= max)))
	{ printf("bad next link for size %d, block %d, addr %x\n",
			i,n,a);
	  diehorribly();
	}
       if (bh1->prev->next != bh1)
	{ printf("bad prev->next for size %d, block %d, link %x but want %x\n",
				i,n,(u_int)bh1->prev->next,(u_int)bh1);
	  diehorribly();
	}
       bh1 = bh1->next;
       if (odd^=1)
	{ bh2 = bh2->next;
	}
       if (bh1 == bh2)
	{ printf("root got dropped for size %d\n",i);
	  diehorribly();
	}
       n ++;
     }
  }
}

diehorribly()
{
 int i;
 int max;
 int odd;
 int a;
 BHEAD *bh;
 BHEAD *bh2;

 max = base + (1 << (poolK-1));
 for (i=0;i<largeK;i++)
  { printf("size %d: ",i);
    bh = avail[i].next;
    if (bh == &avail[i])
     { printf(" none");
nextk:
       printf("\n");
       continue;
     }
    odd = 1;
    bh2 = bh;
    while (bh != &avail[i])
     { a = (u_int) bh;
       if ((a != (u_int)&avail[i]) && ((a < base) || (a >= max)))
	{ printf(" <bad %x>",a);
	  goto nextk; /* aka break 1 continue; */
	}
       printf(" %x",a);
       a = (u_int) bh->prev;
       if ((a != (u_int)&avail[i]) && ((a < base) || (a >= max)))
	{ printf(" <bad prev %x>",a);
	  goto nextk; /* aka break 1 continue; */
	}
       a = (u_int) bh->next;
       if ((a != (u_int)&avail[i]) && ((a < base) || (a >= max)))
	{ printf(" <bad next %x>",a);
	  goto nextk; /* aka break 1 continue; */
	}
       if (bh->prev->next != bh)
	{ printf(" <bad prev->next %x not %x>",(u_int)bh->prev->next,(u_int)bh);
	  goto nextk; /* aka break 1 continue; */
	}
       bh = bh->next;
       if (odd^=1)
	{ bh2 = bh2->next;
	}
       if (bh == bh2)
	{ printf(" <missing root>");
	  goto nextk; /* aka break 1 continue; */
	}
     }
  }
 abort();
}

#endif
