/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by Mike Parker.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#include <stdio.h>

#define hidden

/*
 * malloc.c (Caltech) 2/21/82
 * Chris Kingsley, kingsley@cit-20.
 *
 * This is a very fast storage allocator.  It allocates blocks of a small 
 * number of different sizes, and keeps free lists of each size.  Blocks that
 * don't exactly fit are passed up to the next larger size.  In this 
 * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
 * This is designed for use in a program that uses vast quantities of memory,
 * but bombs when it runs out. 
 */

#include <sys/types.h>

/*
 * The overhead on a block is at least 4 bytes.  When free, this space
 * contains a pointer to the next free block, and the bottom two bits must
 * be zero.  When in use, the first byte is set to MAGIC, and the second
 * byte is the size index.  The remaining bytes are for alignment.
 * If range checking is enabled and the size of the block fits
 * in two bytes, then the top two bytes hold the size of the requested block
 * plus the range checking words, and the header word MINUS ONE.
 */
union overhead {
  union overhead *ov_next;	/* when free */
  struct {
    unsigned char ovu_magic;	/* magic number */
    unsigned char ovu_index;	/* bucket # */
    } ovu;
  } ;
#define ov_magic  ovu.ovu_magic
#define ov_index  ovu.ovu_index
#define ov_size   ovu.ovu_size
#define ov_rmagic ovu.ovu_rmagic

#define	MAGIC 0xfd		/* magic # on accounting info */

#define	NBUCKETS 30

hidden union overhead *nextf[NBUCKETS];
extern char *sbrk();
extern int getmem();

hidden int malloc_ncalls = -1;

int malloc_debug = 0;

char *X_malloc(nbytes,isslave)
unsigned int nbytes;
int isslave;
{
 register union overhead *p;
 register int bucket;
 register unsigned shiftr;

 bucket = 0;
 if (!--malloc_ncalls)
  { *(char *)0x20000000 = 0;
  }
 if (malloc_debug > 1)
  { printf("<malloc %d",nbytes);
  }
 if (malloc_debug > 0)
  { check_for_ff();
  }
 nbytes += sizeof(union overhead);
 nbytes = (nbytes + 3) &~ 3;
 shiftr = (nbytes - 1) >> 2;
 /* apart from this loop, this is O(1) */
 while (shiftr >>= 1)
  { bucket ++;
  }
 if (malloc_debug > 1)
  { printf(" in %d",bucket);
  }
 if (malloc_debug > 0)
  { check_for_ff();
  }
 if (nextf[bucket] == 0)
  { morecore(bucket,isslave);
  }
 p = (union overhead *) nextf[bucket];
 if (p == 0)
  { return(0);
  }
 if (malloc_debug > 1)
  { printf(" get %x>\n",(int)p);
    fflush(stdout);
  }
 if (malloc_debug > 0)
  { check_for_ff();
  }
 nextf[bucket] = nextf[bucket]->ov_next;
 p->ov_magic = MAGIC;
 p->ov_index = bucket;
 if (malloc_debug > 0)
  { check_for_ff();
  }
 return((char *)(p+1));
}

char *malloc(nbytes)
unsigned int nbytes;
{
 return(X_malloc(nbytes,0));
}

char *Smalloc(nbytes)
unsigned int nbytes;
{
 return(X_malloc(nbytes,1));
}

check_for_ff()
{
 register union overhead *op;
 register int b;
 register int n;
 register int i;

 for (b=0;b<NBUCKETS;b++)
  { n = 0;
    for (op=nextf[b];op;n++,op=op->ov_next)
     { i = (int)op;
       if (i & 1)
	{ printf("<bucket %d #%d %x>\n",b,n,i);
	  fflush(stdout);
	  abort();
	}
     }
  }
}

/*
 * Allocate more memory to the indicated bucket.
 */
hidden morecore(bucket,isslave)
register bucket;
int isslave;
{
 register union overhead *op;
 register int rnu;       /* 2^rnu bytes will be requested */
 register int nblks;     /* become nblks blocks of the desired size */
 register int siz;

 if (nextf[bucket])
  { return;
  }
 if (malloc_debug > 1)
  { printf(" (morecore %d)",bucket);
  }
 op = isslave ? (union overhead *) getmem(0) : (union overhead *) sbrk(0);
 siz = getpagesize();
 if (((int)op) % siz)
  { register int n;
    n = siz - (((int)op) % siz);
    if (isslave) getmem(n); else sbrk(n);
  }
 /* take 2k unless the block is bigger than that */
 rnu = (bucket <= 8) ? 11 : bucket + 3;
 nblks = 1 << (rnu - (bucket + 3));  /* how many blocks to get */
 if (rnu < bucket)
  { rnu = bucket;
  }
 op = isslave ? (union overhead *) getmem(1<<rnu) : (union overhead *) sbrk(1<<rnu);
 if (op == (isslave?(union overhead *)-1:(union overhead *)(char *)-1))
  { return;
  }
 if (((int)op) % siz)
  { printf("sbrk mismatch\n");
    return;
  }
 nextf[bucket] = op;
 siz = 1 << (bucket + 3);
 while (--nblks > 0)
  { op->ov_next = (union overhead *)(((char *)op)+siz);
    op = op->ov_next;
  }
 op->ov_next = NULL;
 if (malloc_debug > 0)
  { check_for_ff();
  }
}

free(cp)
char *cp;
{
 register int size;
 register union overhead *op;

 if (cp == 0)
  { return;
  }
 if (malloc_debug > 1)
  { printf("<free %x",(int)cp);
  }
 op = (union overhead *) ((caddr_t)cp-sizeof(union overhead));
 if (malloc_debug > 1)
  { printf(" real %x",(int)op);
  }
 if (malloc_debug > 0)
  { check_for_ff();
  }
 if (op->ov_magic != MAGIC)
  { return;				/* sanity */
  }
 if (malloc_debug > 1)
  { printf(" in %d",op->ov_index);
  }
 if (malloc_debug > 0)
  { check_for_ff();
  }
 size = op->ov_index;
 bfill((char *)op,1<<(size+3),0x5a);
 op->ov_next = nextf[size];
 nextf[size] = op;
 if (malloc_debug > 1)
  { printf(">\n");
    fflush(stdout);
  }
 if (malloc_debug > 0)
  { check_for_ff();
  }
}

char *realloc(cp,nbytes)
char *cp; 
unsigned nbytes;
{
 register u_int onb;
 union overhead *op;
 char *res;
 register int i;

 if (cp == 0)
  { return(malloc(nbytes));
  }
 op = (union overhead *)(cp-sizeof(union overhead));
 if (op->ov_magic != MAGIC)
  { printf("realloc of unallocated block\n");
    return(0);
  }
 i = op->ov_index;
 onb = (1<<(i+3)) - sizeof(*op);
 if ( (nbytes <= onb) &&
      (nbytes > (onb>>1)-sizeof(*op)) )
  { return(cp);
  }
 res = malloc(nbytes);
 if (res == 0)
  { return(0);
  }
 if (cp != res)
  { bcopy(cp,res,(nbytes<onb)?nbytes:onb);
  }
 free(cp);
 return(res);
}
