#ifdef MALLOC_bibopmalloc
/*
**
** This is a special purpose malloc for lpmud.
** It is geared towards the special needs of the game driver:
**   - most allocations (>98%) are of small objects (<100 byte)
**   - very few allocations (< 0.03%) are large (>64k)
**
** This has lead to a malloc using three different strategies:
** small, medium, and large.
** All allocation is based on pages (typical page size is 32K).
** Pages are allocated on pages boundaries.  This makes it possible
** to store information about a page and its objects just at the
** beginning of a page.  Given a pointer in the middle of a page
** the address is just masked to page boundary and the page information
** is available there.
**
** Small objects:
** Objects with a size smaller than a threshold will
** be allocated in BIBOP (big bag of pages) style.
** A page consists of some information about the page in the
** beginning followed by a bitmap indicating the usage
** of objects and an array of objects.  Within a page
** all objects have the same size.
** To get to the information given a pointer to an
** object you mask off the lower part of the address.
** This method gives a very small space overhead (<2 bits) per object.
**
** Medium objects:
** Medium sized objects are allocated within a page.
** All the pages are linked, and the objects are linked
** within a page.  All free space is linked into doubly
** linked lists.  There are a number of list headers for different
** sizes of the free object.  The allocation uses best fit
** among the headers, and first fit in the doubly linked list.
** Free objects are coalesced when being freed.
**
** Large objects:
** Large objects are given several consecutive pages.
** Large requests are always rounded to a multiple of
** pages.
**
** Free pages (from freeing large or medium requests) are kept
** on a free list where best fit is used to do allocation.
**
** The current tuning of the allocation parameters seems to indicate
** that the space overhead is around 15%.
*/

/*#define BIBOP_DEBUG */
/*#define FREESTAT*/
#define PARANOIA

#include <stdio.h>
#include "config.h"
#include <math.h>

#ifdef __alpha
#define SMALL_SIZE 96		/* smaller than this is uses bibop */
#define PAGE_SIZE 0x10000	/* 64k pages MUST!! be a power of 2 */
#define SIZE_SLOP 96		/* don't split a free space if leftover is less than this */
#else
#define SMALL_SIZE 56		/* smaller than this is uses bibop */
#define PAGE_SIZE 0x8000	/* 32k pages MUST!! be a power of 2 */
#define SIZE_SLOP 56		/* don't split a free space if leftover is less than this */
#endif
#define NFREE 20		/* number of headers for medium blocks */

typedef double aligntype;
#define ALIGNMENT (sizeof(aligntype)) /* alignment requirement for this platform */
typedef unsigned long mapword;	/* should be the largest possible unsigned type */

#define BITS_PER_BYTE 8		/* bits per unit in sizeof */

#define CEIL(x) (((x)+ALIGNMENT-1) & ~(ALIGNMENT-1))
#define PAGEMASK (~(PAGE_SIZE-1))

#define PTR_SIZE (sizeof(void *))

#define BITS_PER_MAPWORD (sizeof(mapword) * BITS_PER_BYTE)

struct bibop {
    int objsize;		/* Size of the objects (rounded) */
    int objfree;		/* Number of free objects */
    int maxobjfree;		/* Maximum objects that fit on the page */
    mapword *freemap;		/* Bitmap for free objects */
    int nextfree;		/* (Cached) index to look for free object */
    int maxfree;		/* Max index into freemap */
    void *objs;			/* Pointer to first object */
};
#define BIBOPMAGIC 0x55AA5A01
static int p_bibop, m_bibop, f_bibop;
static long s_bibop;

struct medium {
#if 0
    int freecnt;		/* number of free objects in this page */
    int maxfree;		/* largest free object, or 0 if unknown */
#endif
    aligntype data;
};
#define ALLOCMAGIC 0x55AA5A02
static int p_alloc, m_alloc, f_alloc;
static long s_alloc;
#define OBJSIZE(cur) ((char *)cur->next - (char *)OBJ_TO_DATA(cur))

struct freeblk {
    struct freeblk *fwd, *bwd;
#ifdef FREESTAT
    struct freehead *head;
#endif
};
struct freehead {
    struct freeblk h;
    int maxsize;
    int nalloc, nreal, nsplit, nfree, njoin, njoin1, njoin2, ndrop, nwhy, nskip;
};
static struct freehead freehead[NFREE];
#define FDEL(p) (p)->bwd->fwd = (p)->fwd, (p)->fwd->bwd = (p)->bwd
#define FINS(p, q) (p)->fwd = (q)->fwd, (q)->fwd = (p), (p)->bwd = (q), (p)->fwd->bwd = (p)

struct big {
    int npages;
    aligntype data;
};
#define BIGMAGIC 0x55AA5A03
static int p_big, m_big, f_big;
static long s_big;

struct descr {
    int magic;			/* magic number for different page types */
    struct descr *dnext;	/* next page in whatever list it happens to be in */
    union {
	struct bibop bibop;
	struct medium medium;
	struct big big;
    } u;
};

#ifdef USE_SWAP
extern int used_memory; /* for the swap to know */
#endif

static struct descr *bibops[(SMALL_SIZE+ALIGNMENT-1)/ALIGNMENT]; /* start of bibop chains */

static struct descr *newbibop();

static struct descr *firstpage;

typedef unsigned long PTR;	/* same size as a pointer */

static struct descr *freepage = 0;
static int p_free;

struct block {
#ifdef BIBOP_DEBUG
    int bmagic;
#endif
    int busy;
    struct block *next;
    union {
	aligntype data;
	struct freeblk fb;
    } u;
};
#define DATA_TO_OBJ(p) ((struct block *)( (char *)p - (long)&((struct block *)0)->u.data ))
#define OBJ_TO_DATA(p) ((void *)&p->u.data)
#define BMAGIC 0x19930921

static char *nextpage, *firstsbrk;

static int initdone = 0;

extern void *sbrk();

#define OFFSET(t,f) ((long) &(((t *)0)->f))

/* Insert pages on free list, coalescing pages if possible */
static void
insertfreepage(struct descr *p)
{
    struct descr **fp;

#ifdef BIBOP_DEBUG
    fprintf(stderr, "freeing %d pages at %lx", p->u.big.npages, p);
#endif
    /* find position */
    for(fp = &freepage; *fp && p > *fp; fp = &(*fp)->dnext)
	;
    /* insert into chain */
    p->dnext = *fp;
    *fp = p;
    /* check for adjecent following page */
    if ((char *)p->dnext == (char *)p + p->u.big.npages * PAGE_SIZE) {
	p->u.big.npages += p->dnext->u.big.npages;
	p->dnext = p->dnext->dnext;
#ifdef BIBOP_DEBUG
    fprintf(stderr, ", join with next block");
#endif
    }
    /* check for adjecent preceding page */
    if (fp != &freepage) {
	struct descr *q;
	q = (struct descr *)((char *)fp - OFFSET(struct descr, dnext));
	if ((char *)q->dnext == (char *)q + q->u.big.npages * PAGE_SIZE) {
	    q->u.big.npages += q->dnext->u.big.npages;
	    q->dnext = q->dnext->dnext;
#ifdef BIBOP_DEBUG
    fprintf(stderr, ", join with previous block");
#endif
	}
    }
#ifdef BIBOP_DEBUG
    {
	struct descr *f;
	fprintf(stderr, "\nfree page list (nextpage=%lx):\n", nextpage);
	for(f = freepage; f; f = f->dnext) {
	    fprintf(stderr, "    %lx (%lx) %d\n", f, (char *)f + f->u.big.npages * PAGE_SIZE, f->u.big.npages);
	}
    }
#endif
}

/* Allocate n pages. */
static void *
pages(unsigned int n)
{
    void *r;

#ifdef BIBOP_DEBUG
    fprintf(stderr, "allocating %d pages", n);
#endif

    if (freepage) {
	/* try to find something on the free list */
	/* use best fit, slower but better */
	struct descr **p, **last, **best = 0, *b;
	for(p = &freepage; *p; last = p, p = &(*p)->dnext) {
	    if ((*p)->u.big.npages == n) {
		best = p;
		break;
	    } else if ((*p)->u.big.npages > n) {
		if (!best || (*p)->u.big.npages < (*best)->u.big.npages)
		    best = p;
	    }
	}
	if (!best) {
	    /* couldn't find anything, try extending last block */
	    if ((char *)(*last) + (*last)->u.big.npages * PAGE_SIZE == nextpage) {
		int k = n - (*last)->u.big.npages;
		r = sbrk(k*PAGE_SIZE);
		if (!r || r == (void *)-1) {
		    fprintf(stderr, "sbrk failed %ld\n", nextpage - firstsbrk);
		    return 0;
		}
		nextpage += k*PAGE_SIZE;
		(*last)->u.big.npages += k;
		best = last;
		p_free += k;
#ifdef BIBOP_DEBUG
		fprintf(stderr, ", extending %d", k);
#endif
	    } else
		goto notfound;
	}
#ifdef BIBOP_DEBUG
	fprintf(stderr, ", from free list");
#endif
	/* best points at something usable */
	b = *best;
	r = b;
	if (b->u.big.npages > n) {
	    /* split the block */
	    *best = (struct descr *)((char *)b + n * PAGE_SIZE);
	    (*best)->magic = b->magic;
	    (*best)->u.big.npages = b->u.big.npages - n;
	    (*best)->dnext = b->dnext;
#ifdef BIBOP_DEBUG
	fprintf(stderr, ", split it %d %d", b->u.big.npages, n);
#endif
	} else {
	    *best = b->dnext;	/* remove block */
	}
	p_free -= n;
#ifdef BIBOP_DEBUG
    {
	struct descr *f;
	fprintf(stderr, "\nfree page list (nextpage=%lx):\n", nextpage);
	for(f = freepage; f; f = f->dnext) {
	    fprintf(stderr, "    %lx (%lx) %d\n", f, (char *)f + f->u.big.npages * PAGE_SIZE, f->u.big.npages);
	}
    }
#endif
	return r;
    }

 notfound:
#ifdef BIBOP_DEBUG
    fprintf(stderr, " fresh\n");
#endif
    r = sbrk(n*PAGE_SIZE);
    if (!r || r == (void *)-1) {
	fprintf(stderr, "sbrk failed %ld\n", nextpage - firstsbrk);
	return 0;
    }
    if (!(nextpage - PAGE_SIZE <= (char *)r && (char *)r <= nextpage + PAGE_SIZE)) { fprintf(stderr, "bad sbrk %lx %lx\n", r, nextpage); }
    r = nextpage;
    nextpage += n*PAGE_SIZE;
    return r;
}

/* Insert a (medium) block into the appropriate free list */
static void
insfree(struct block *b, int how)
{
    int s = OBJSIZE(b);
    int i;

    for(i = 0; i < NFREE && freehead[i].maxsize <= s; i++)
	;
#ifdef PARANOIA
    if (i == NFREE) {
	fprintf(stderr, "insfree %d\n", s);
	abort();
    }
#endif
    FINS(&b->u.fb, &freehead[i].h);
#ifdef FREESTAT
    if (how == 1) freehead[i].nsplit++;
    if (how == 2) freehead[i].nfree++;
    if (how == 3) freehead[i].njoin++;
    b->u.fb.head = &freehead[i];
#endif
}

/* Initialize a new medium page */
static void
init_allocpage(struct descr *p)
{
    struct block *first, *last;

    p->magic = ALLOCMAGIC;
#if 0
    p->u.medium.freecnt = 1;		/* one free block after init */
#endif
    p->dnext = 0;
    first = (struct block *)&p->u.medium.data;
    last = (struct block *)((char *)p + PAGE_SIZE - sizeof(struct block));
#ifdef BIBOP_DEBUG
    first->bmagic = BMAGIC;
    last->bmagic = BMAGIC;
#endif    
    first->busy = 0;
    first->next = last;
    last->busy = 0;
    last->next = 0;
    insfree(first, 0);
#if 0
    p->u.medium.maxfree = OBJSIZE(first);
#endif
}

/* Do a non-small allocation */
static void *
bigmalloc(unsigned int size)
{
    struct descr *p;
    struct block *cur, *next;
    int cursize;

    if (size > PAGE_SIZE - sizeof(struct descr) - ALIGNMENT - PTR_SIZE) {
	/* really big! */
	int n = (size + sizeof(struct descr) + PAGE_SIZE - 1) / PAGE_SIZE;
	struct descr *bigp = pages(n);
	if (!bigp)
	    return 0;
#ifdef USE_SWAP
	used_memory += n * PAGE_SIZE; /* for the swap to know */
#endif
	p_big += n;
	bigp->magic = BIGMAGIC;
	bigp->u.big.npages = n;
	m_big++;
	s_big += size;
	return (void *)&bigp->u.big.data;
    }
	
#if 0
    /* find first page with enough space */
    for(p = firstpage; p; p = p->next) {
	if (p->u.medium.freecnt == 0 || p->u.medium.maxfree && p->u.medium.maxfree < size)
	    continue;
	for(cur = (struct block *)&p->u.medium.data; cur->next; cur = cur->next) {
	    if (cur->busy)
		continue;
	    next = cur->next;
	    cursize = OBJSIZE(cur);
	    if (cursize > p->u.medium.maxfree)
		p->u.medium.maxfree = cursize;
	    if (cursize >= size)
		goto found;
	}
    }
#else
    {
	/* find first block which is big enough */
	int i;
	for(i = 0; i < NFREE; i++) {
	    if (freehead[i].maxsize > size) {
		struct freeblk *fp;
		for(fp = freehead[i].h.fwd; fp != &freehead[i].h; fp = fp->fwd) {
		    cur = DATA_TO_OBJ(fp);
		    cursize = OBJSIZE(cur);
		    if (cursize >= size) {
			p = (struct descr *)((PTR)cur & PAGEMASK);
			goto found;
		    }
		}
		freehead[i].nskip++;
	    }
	}
    }
#endif
    /* No page had a block that was big enough, get a new one. */
    p = pages(1);
    if (!p)
	return 0;
    p_alloc++;
    init_allocpage(p);
    p->dnext = firstpage;
    firstpage = p;
    cur = (struct block *)&p->u.medium.data;
    cursize = OBJSIZE(cur);
 found:
#ifdef BIBOP_DEBUG
    if (cur->bmagic != BMAGIC) {
	fprintf(stderr, "bad magic 2\n");
	abort();
    }
#endif
#if 0
    p->u.medium.freecnt--;		/* used one free */
#endif
    FDEL(&cur->u.fb);			/* remove from free lists */
#ifdef FREESTAT
    cur->u.fb.head->nalloc++;
    {
	int i;
	for(i = 0; i < NFREE && freehead[i].maxsize < size; i++)
	    ;
#ifdef PARANOIA
	if (i == NFREE)
	    abort();
#endif
	freehead[i].nreal++;
	if (cur == (struct block *)&p->u.medium.data && cur->next->next == 0)
	    freehead[i].nwhy++;
    }
#endif
    if (size + SIZE_SLOP < cursize) {
	/* we have to split the current block */
	next = (struct block *)((char *)cur + size + sizeof(struct block));
#ifdef BIBOP_DEBUG
	next->bmagic = BMAGIC;
#endif
	next->busy = 0;
#ifdef PARANOIA
	if ((struct descr *)((PTR)cur->next & PAGEMASK) != p) 
	    abort();
#endif
	next->next = cur->next;
	cur->next = next;
#ifdef PARANOIA
	if ((struct descr *)((PTR)cur->next & PAGEMASK) != p) 
	    abort();
#endif
#if 0
	p->u.medium.freecnt++;		/* split one into two */
	if (p->u.medium.maxfree == cursize)
	    p->u.medium.maxfree = OBJSIZE(next);
#endif
	insfree(next, 1);		/* insert second half */
    }
    cur->busy = 1;
    m_alloc++;
    s_alloc += size;
#if 0
    if (p->u.medium.maxfree == cursize)
	p->u.medium.maxfree = 0;
#endif
#ifdef USE_SWAP
    used_memory += OBJSIZE(cur); /* for the swap to know */
#endif
    return (void *)&cur->u.data;
}

/* Table of the bit number of the last bit set in a byte. */
static int flsb[] = {
-1,0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
};

/* External interface */
void *
malloc(unsigned int size)
{
    register struct descr *bp;
    register int i, j;
    register mapword m;

    size = CEIL(size);
    if (!initdone) {
	/* Fake it if this is an early malloc. */
	return sbrk(size);
    }
    if (size >= SMALL_SIZE)
	return bigmalloc(size);
    for(bp = bibops[size/ALIGNMENT - 1]; bp && bp->u.bibop.objfree == 0; bp = bp->dnext)
	;
    if (!bp) {
	/* Add a new page */
	bp = newbibop(size);
	if (!bp)
	    return 0;
	bp->dnext = bibops[size/ALIGNMENT - 1];
	bibops[size/ALIGNMENT - 1] = bp;
    }
    /* locate word with free block */
    for(i = bp->u.bibop.nextfree; ;) {
	m = bp->u.bibop.freemap[i];
	if (m)
	    break;
	if (++i >= bp->u.bibop.maxfree)
	    i = 0;
#ifdef PARANOIA
	if (i == bp->u.bibop.nextfree) {
	    /* Help!  We've wrapped around without finding the promised free block! */
	    fprintf(stderr, "bmalloc wrapped around, size = %d\n", size);
	    abort();
	}
#endif
    }
    bp->u.bibop.nextfree = i;
    /* locate bit within the word */
#define ONES (~(mapword)0)
    j = 0;
    if ((m & (ONES >> (BITS_PER_MAPWORD/2))) == 0) 
	j += BITS_PER_MAPWORD/2, m >>= BITS_PER_MAPWORD/2; 
    else 
	m &= (ONES >> (BITS_PER_MAPWORD/2));
    if ((m & (ONES >> (3*BITS_PER_MAPWORD/4))) == 0) 
	j += BITS_PER_MAPWORD/4, m >>= BITS_PER_MAPWORD/4; 
    else 
	m &= (ONES >> (3*BITS_PER_MAPWORD/4));
    if ((m & (ONES >> (7*BITS_PER_MAPWORD/8))) == 0) 
	j += BITS_PER_MAPWORD/8, m >>= BITS_PER_MAPWORD/8; 
    else 
	m &= (ONES >> (7*BITS_PER_MAPWORD/8));
    /* We're down to at most 8 bits now (if there are <= 64 bits in a word).
       Use a table to look up the last bit set.
    */
    j += flsb[m];
    bp->u.bibop.freemap[i] &= ~ ((mapword)1<<j);	/* mark as allocated */
    bp->u.bibop.objfree--;		/* one less free */
#ifdef USE_SWAP
    used_memory += bp->u.bibop.objsize;
#endif
    m_bibop++;
    s_bibop += size;
    return (void *) ((char *)bp->u.bibop.objs + (i * BITS_PER_MAPWORD + j) * size);
}

/* External interface */
void
free(void *ptr)
{
    register struct descr *bp;
    register unsigned int size, offs;
    int i, j;

    if (!initdone)
	return;
    bp = (struct descr *)((PTR)ptr & PAGEMASK);
    if (bp->magic == BIGMAGIC) {
	/* freeing really big block, stuff all pages on free list */
	int n = bp->u.big.npages;

#ifdef USE_SWAP
	used_memory -= n * PAGE_SIZE; /* for the swap to know */
#endif
	insertfreepage(bp);
	f_big++;
	p_big -= n;
	p_free += n;
    } else if (bp->magic == ALLOCMAGIC) {
	/* freeing part of a page */
	struct block *this, *cur, *next;
	int cursize;

#ifdef USE_SWAP
	used_memory -= OBJSIZE(DATA_TO_OBJ(ptr)); /* for the swap to know */
#endif	
#ifdef BIBOP_DEBUG
	if (DATA_TO_OBJ(ptr)->bmagic != BMAGIC) {
	    fprintf(stderr, "bad magic 3\n");
	    abort();
	}
#endif
	this = DATA_TO_OBJ(ptr);
	this->busy = 0;
#if 0
	bp->u.medium.freecnt++; /* another free */
#endif
	insfree(this, 2);
	/* coalescing is tedious since we don't have back pointers */
	for(cur = (struct block *)&bp->u.medium.data; cur->next; cur = cur->next)
	    if (cur == this || cur->next == this && !cur->busy)
		break;
#ifdef PARANOIA
	if (!cur->next || cur->busy)
	    abort();
#endif
	/* coalesce free blocks (max 3) */
	for(;;) {
	    next = cur->next;
#ifdef BIBOP_DEBUG
	    if (next->bmagic != BMAGIC) {
		fprintf(stderr, "bad magic 1\n");
		abort();
	    }
#endif
	    if (!next->next || next->busy)
		break;
	    cur->next = next->next;
#ifdef BIBOP_DEBUG
	    next->bmagic = 0;
#endif
#ifdef PARANOIA
	    if ((struct descr *)((PTR)cur->next & PAGEMASK) != bp)
		abort();
#endif
#if 0
	    bp->u.medium.freecnt--;	/* joined two free into one */
#endif
#ifdef FREESTAT
	    cur->u.fb.head->njoin1++;
	    next->u.fb.head->njoin2++;
#endif
	    FDEL(&cur->u.fb);
	    FDEL(&next->u.fb);
	    insfree(cur, 3);
	}
	cursize = OBJSIZE(cur);
#if 0
	if (cursize > bp->u.medium.maxfree)
	    bp->u.medium.maxfree = cursize;
#endif

	if (((struct block *)&bp->u.medium.data)->next->next == 0) {
	    /* The whole page is free now, move it to free list */
	    struct descr **p;
	    struct block *blk = (struct block *)&bp->u.medium.data;
	    FDEL(&blk->u.fb);
#ifdef FREESTAT
	    blk->u.fb.head->ndrop++;
#endif
	    for(p = &firstpage; *p != bp; p = &(*p)->dnext)
		;
	    *p = (*p)->dnext;
	    bp->u.big.npages = 1;
	    insertfreepage(bp);
	    p_free++;
	    p_alloc--;
	}
	f_alloc++;
    } else if (bp->magic == BIBOPMAGIC) {
	offs = ((char *)ptr - (char *)bp->u.bibop.objs) / bp->u.bibop.objsize; /* block offset from first block */
	i = offs / BITS_PER_MAPWORD;
	j = offs % BITS_PER_MAPWORD;
	bp->u.bibop.freemap[i] |= (mapword)1<<j;	/* mark as free */
	bp->u.bibop.nextfree = i;	/* we have a free block here */
	bp->u.bibop.objfree++;		/* one more free */
	f_bibop++;
#ifdef USE_SWAP
	used_memory -= bp->u.bibop.objsize;
#endif
    } else {
	/* Happens when freeing prematurely allocated objects. */
	fprintf(stderr, "Warning, bad magic number in free %x\n", bp->magic);
    }
}

/* Figure out (maximum) object size. */
static unsigned int
objsize(void *ptr)
{
    register struct descr *bp;

    bp = (struct descr *)((PTR)ptr & PAGEMASK);
    if (bp->magic == BIGMAGIC) {
	return bp->u.big.npages * PAGE_SIZE;
    } else if (bp->magic == ALLOCMAGIC) {
	return OBJSIZE(DATA_TO_OBJ(ptr));
    } else if (bp->magic == BIBOPMAGIC) {
	return bp->u.bibop.objsize;
    } else {
	return -1;
    }
}

/* External interface */
void *
realloc(void *old, unsigned int size)
{
    void *new;
    int osize;
    struct descr *bp;
    
    bp = (struct descr *)((PTR)old & PAGEMASK);
    switch (bp->magic)
    {
    case BIGMAGIC:
	osize = bp->u.big.npages * PAGE_SIZE;
	break;
    case ALLOCMAGIC:
	osize = OBJSIZE(DATA_TO_OBJ(old));
	break;
    case BIBOPMAGIC:
	osize = bp->u.bibop.objsize;
	break;
    default:
	fprintf(stderr, "Bad magic number in free %x\n", bp->magic);
    }
    if (osize > size)
	osize = size;

    /* Always assume the worst and allocate&copy */
    new = malloc(size);
    if (!new)
	return 0;
    
    memcpy(new, old, osize);

    free(old);
    return new;
}

/* Allocate and initialize a new bibop page. */
static struct descr *
newbibop(size)
unsigned int size;
{
    struct descr *bp;
    int nobj, nmap, nnmap;
    int i, j;

/*  fprintf(stderr, "newbibop %d\n", size);*/
    bp = pages(1);
    if (!bp)
	return 0;
    p_bibop++;
    /* I'm to lazy to figure out the exact formula.  Iterate at runtime instead. */
    for(nmap = 0, nnmap = -1; nmap != nnmap;) {
	nobj = (PAGE_SIZE - sizeof(struct descr) - nmap * sizeof(mapword) - 2 * ALIGNMENT) / size;
	nnmap = nmap;
	nmap = (nobj + BITS_PER_MAPWORD - 1) / BITS_PER_MAPWORD;
    }
    bp->magic = BIBOPMAGIC;
    bp->u.bibop.objsize = size;
    bp->u.bibop.objfree = nobj;
    bp->u.bibop.maxobjfree = nobj;
    bp->u.bibop.freemap = (void *) ((char *)bp + CEIL(sizeof(struct descr)));
    bp->u.bibop.nextfree = 0;
    bp->u.bibop.maxfree = nmap;
    bp->u.bibop.objs = (void *) ((char *)bp->u.bibop.freemap + CEIL(nmap * sizeof(mapword)));
    bp->dnext = 0;

    /* fill the freemap */
    for(i = 0; i < nmap-1; i++)
	bp->u.bibop.freemap[i] = ~0;
    j = nobj % BITS_PER_MAPWORD; /* no of bits needed in last word, or 0 all are needed */
    if (j)
	bp->u.bibop.freemap[i] = ((mapword)~0) >> (BITS_PER_MAPWORD - j);
    else
	bp->u.bibop.freemap[i] = ~0;
    return bp;
}

/* External interface.  Should be called before first malloc. */
void
bibop_init()
{
    int mins, maxs;
    double sfact, smult, s;
    int i;

    initdone++;
    /* set up nextpage to point to space at a page boundary */
    firstsbrk = nextpage = (char *)( ( (PTR)sbrk(PAGE_SIZE+1) & PAGEMASK) + PAGE_SIZE );

    mins = SMALL_SIZE;
    maxs = PAGE_SIZE;
    sfact = (double)maxs / mins;
    smult = exp(log(sfact) / NFREE);
    for(s = smult, i = 0; i < NFREE; i++, s *= smult) {
	freehead[i].h.fwd = &freehead[i].h;
	freehead[i].h.bwd = &freehead[i].h;
	freehead[i].maxsize = (int) (mins * s);
	/*fprintf(stderr, "bucket %d holds size <%d\n", i, freehead[i].maxsize);*/
#ifdef FREESTAT
	freehead[i].h.head = &freehead[i];
#endif
    }
}

/* External interface.  Show statistics. */
char *
dump_malloc_data()
{
    static char mbuf[3000];
    int u_bibop, u_alloc, u_big, n_bibop, n_alloc, n_big, a_bibop, a_alloc, a_big;
    struct descr *p;
    int i, n;

    u_bibop = u_alloc = u_big = n_bibop = n_alloc = n_big = a_bibop = a_alloc = a_big = 0;
    for(i = 0; i < (SMALL_SIZE+ALIGNMENT-1)/ALIGNMENT; i++) {
	for(p = bibops[i]; p; p = p->dnext) {
	    n = p->u.bibop.objfree * p->u.bibop.objsize;
	    n_bibop += n;
	    u_bibop += PAGE_SIZE - n;
	    a_bibop += p->u.bibop.objfree;
	}
    }
    for(p = firstpage; p; p = p->dnext) {
	struct block *q;
	for(q = (struct block *)&p->u.medium.data; q->next; q = q->next) {
	    if (q->busy)
		u_alloc += OBJSIZE(q);
	    else
		n_alloc += OBJSIZE(q), a_alloc++;
	}
    }
    u_big = PAGE_SIZE * p_big;
    sprintf(mbuf, "\
%-17s %10s %10s %10s %10s\n\
%-17s %10d %10d %10d %10d\n\
%-17s %10d %10d %10d %10d (%d in free pages)\n\
%-17s %10d %10d %10d %10d\n\
%-17s %10d %10d %10d %10d\n\
%-17s %10d %10d %10d %10d (%d free) = %d bytes (page=%d)\n\
\n\
%-17s %10d %10d %10d %10d\n\
%-17s %10d %10d %10d %10d\n\
%-17s %10ld %10ld %10ld %10ld\n\
\n\
sbrk requests: %d %d (a) \n\
", 
	    "",            "small", "medium", "large", "total",
	    "used memory", u_bibop, u_alloc, u_big, u_bibop+u_alloc+u_big, 
	    "free memory", n_bibop, n_alloc, n_big, n_bibop+n_alloc+n_big+p_free*PAGE_SIZE, p_free*PAGE_SIZE, 
	    "used blocks", m_bibop-f_bibop, m_alloc-f_alloc, m_big-f_big, m_bibop+m_alloc+m_big-(f_bibop+f_alloc+f_big),
	    "free blocks", a_bibop, a_alloc, a_big, a_bibop + a_alloc + a_big,
	    "allocated pages", p_bibop, p_alloc, p_big, p_bibop+p_alloc+p_big+p_free+1, p_free, (p_bibop+p_alloc+p_big+p_free+1)*PAGE_SIZE, PAGE_SIZE,

	    "# of malloc()", m_bibop, m_alloc, m_big, m_bibop+m_alloc+m_big,
	    "# of free()", f_bibop, f_alloc, f_big, f_bibop+f_alloc+f_big,
	    "total allocation", s_bibop, s_alloc, s_big, s_bibop+s_alloc+s_big,
	    p_bibop+p_alloc+p_big+p_free+1, (p_bibop+p_alloc+p_big+p_free+1)*PAGE_SIZE);
#ifdef FREESTAT
    {
	char xxxxx[2000];
	int k, tf;
	strcpy(xxxxx, "medium sizes:\n size unus  split   free   join  alloc join1 join2 drop  real why  skip\n");
	for(tf = 0, k = 0; k < NFREE; k++) {
	    struct freeblk *p;
	    int s;
	    for(s = 0, p = freehead[k].h.fwd; p != &freehead[k].h; p = p->fwd, s++)
		;
	    sprintf(xxxxx+strlen(xxxxx), "%5d %4d %6d %6d %6d %6d %5d %5d %4d %5d %3d %5d\n",
		    freehead[k].maxsize, s, 
		    freehead[k].nsplit, freehead[k].nfree, freehead[k].njoin,
		    freehead[k].nalloc, freehead[k].njoin1, freehead[k].njoin2, freehead[k].ndrop, 
		    freehead[k].nreal, freehead[k].nwhy, freehead[k].nskip);
	    tf += s;
	}
	sprintf(xxxxx+strlen(xxxxx), "tot=%d(%d)\n", tf, a_alloc);
	strcat(mbuf, xxxxx);
    }
#endif
    return mbuf;
}
#endif /* MALLOC_bibop */