home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1992 Regents of the University of California */
-
- #ifndef lint
- static char SCCSid[] = "@(#)malloc.c 2.8 10/8/92 LBL";
- #endif
-
- /*
- * Fast malloc for memory hogs and VM environments.
- * Performs a minimum of searching through free lists.
- * malloc(), realloc() and free() use buckets
- * containing blocks in powers of two, similar to CIT routines.
- * bfree() puts memory from any source into malloc free lists.
- * bmalloc() doesn't keep track of free lists -- it's usually
- * just a buffered call to sbrk(2). However, bmalloc will
- * call mscrounge() if sbrk fails.
- * mscrounge() returns whatever free memory it can find to satisfy the
- * request along with the number of bytes (modified).
- * mcompact() takes the same arguments as mscrounge() (and is in fact
- * called by mscrounge() under dire circumstances), but it uses
- * memory compaction to return a larger block. (MCOMP)
- *
- * Greg Ward Lawrence Berkeley Laboratory
- */
-
- #include <errno.h>
-
- #ifndef BSD
- #define bcopy(s,d,n) (void)memcpy(d,s,n)
- #define bzero(d,n) (void)memset(d,0,n)
- extern char *memcpy(), *memset();
- #endif
-
- #ifdef MSTATS
- #include <stdio.h>
- static unsigned b_nsbrked = 0;
- static unsigned b_nalloced = 0;
- static unsigned b_nfreed = 0;
- static unsigned b_nscrounged = 0;
- static unsigned b_ncompacted = 0;
- static unsigned m_nalloced = 0;
- static unsigned m_nfreed = 0;
- static unsigned m_nwasted = 0;
- #else
- #define NULL 0
- #endif
-
- #ifndef ALIGN
- #define ALIGN int /* align type */
- #endif
- #define BYTES_WORD sizeof(ALIGN)
-
- #ifndef MAXINCR
- #define MAXINCR (1<<16) /* largest sbrk(2) increment */
- #endif
- /* malloc free lists */
- typedef union m_head {
- union m_head *next;
- struct {
- short magic;
- short bucket;
- } a;
- ALIGN dummy;
- } M_HEAD;
-
- #define MAGIC 0x1a2 /* magic number for allocated memory */
-
- #define FIRSTBUCKET 3
- #define NBUCKETS 30
-
- static M_HEAD *free_list[NBUCKETS];
-
- static ALIGN dummy_mem;
-
- static char *memlim[2];
-
- #define DUMMYLOC ((char *)&dummy_mem)
-
- #define BADPTR(p) ((p) < memlim[0] | (p) >= memlim[1])
-
- #ifdef MCOMP /* memory compaction routines */
- static char seedtab[1024]; /* seed for compaction table */
-
- static struct mblk {
- char *ptr; /* pointer to memory block */
- unsigned siz; /* size of memory block */
- } cptab = {seedtab, sizeof(seedtab)};
-
- #define mtab(mb) ((struct mblk *)(mb)->ptr)
- #define mtablen(mb) ((mb)->siz/sizeof(struct mblk))
-
-
- static int
- mbcmp(m1, m2) /* compare memory block locations */
- register struct mblk *m1, *m2;
- {
- /* put empty blocks at end */
- if (m1->siz == 0)
- return(m2->siz == 0 ? 0 : 1);
- if (m2->siz == 0)
- return(-1);
- /* otherwise, ascending order */
- return(m1->ptr - m2->ptr);
- }
-
-
- int
- compactfree() /* compact free lists (moving to table) */
- {
- register struct mblk *tp;
- register int bucket;
- unsigned n, bsiz, ncomp;
- /* fill table from free lists */
- tp = mtab(&cptab); n = mtablen(&cptab);
- bucket = NBUCKETS-1; bsiz = 1<<(NBUCKETS-1);
- ncomp = 0;
- for ( ; ; ) {
- while (tp->siz) { /* find empty slot */
- if (--n == 0)
- goto loopexit;
- tp++;
- }
- while (free_list[bucket] == NULL) { /* find block */
- if (--bucket < FIRSTBUCKET)
- goto loopexit;
- bsiz >>= 1;
- }
- tp->ptr = (char *)free_list[bucket];
- ncomp += (tp->siz = bsiz + sizeof(M_HEAD));
- free_list[bucket] = free_list[bucket]->next;
- }
- loopexit:
- if (ncomp == 0)
- return(0); /* nothing new to compact */
- #ifdef MSTATS
- b_ncompacted += ncomp;
- #endif
- tp = mtab(&cptab); n = mtablen(&cptab); /* sort table */
- qsort((char *)tp, n, sizeof(struct mblk), mbcmp);
- ncomp = 0; /* collect blocks */
- while (--n && tp[1].siz) {
- if (tp[0].ptr + tp[0].siz == tp[1].ptr) {
- tp[1].ptr = tp[0].ptr;
- tp[1].siz += tp[0].siz;
- ncomp += tp[0].siz;
- tp[0].siz = 0;
- }
- tp++;
- }
- return(ncomp);
- }
-
-
- char *
- mcompact(np) /* compact free lists to satisfy request */
- unsigned *np;
- {
- struct mblk *tab;
- unsigned tablen;
- register struct mblk *bp, *big;
-
- for ( ; ; ) {
- /* compact free lists */
- compactfree();
- /* find largest block */
- tab = mtab(&cptab); tablen = mtablen(&cptab);
- big = tab;
- bp = tab + tablen;
- while (--bp > tab)
- if (bp->siz > big->siz)
- big = bp;
- if (big->siz >= *np) { /* big enough? */
- *np = big->siz;
- big->siz = 0; /* remove from table */
- return(big->ptr); /* return it */
- }
- if (mtablen(big) <= tablen) {
- *np = 0; /* cannot grow table */
- return(NULL); /* report failure */
- }
- /* enlarge table, free old one */
- mtab(big)[0].ptr = cptab.ptr;
- mtab(big)[0].siz = cptab.siz;
- cptab.ptr = big->ptr;
- cptab.siz = big->siz;
- big->siz = 0; /* clear and copy */
- bcopy((char *)tab, (char *)(mtab(&cptab)+1),
- tablen*sizeof(struct mblk));
- bzero((char *)(mtab(&cptab)+tablen+1),
- (mtablen(&cptab)-tablen-1)*sizeof(struct mblk));
- } /* next round */
- }
- #endif /* MCOMP */
-
-
- char *
- mscrounge(np) /* search free lists to satisfy request */
- register unsigned *np;
- {
- char *p;
- register int bucket;
- register unsigned bsiz;
-
- for (bucket = FIRSTBUCKET, bsiz = 1<<FIRSTBUCKET;
- bucket < NBUCKETS; bucket++, bsiz <<= 1)
- if (free_list[bucket] != NULL && bsiz+sizeof(M_HEAD) >= *np) {
- *np = bsiz+sizeof(M_HEAD);
- p = (char *)free_list[bucket];
- free_list[bucket] = free_list[bucket]->next;
- #ifdef MSTATS
- b_nscrounged += *np;
- #endif
- return(p);
- }
- #ifdef MCOMP
- return(mcompact(np)); /* try compaction */
- #else
- *np = 0;
- return(NULL);
- #endif
- }
-
-
- char *
- bmalloc(n) /* allocate a block of n bytes, sans header */
- register unsigned n;
- {
- extern char *sbrk();
- static unsigned pagesz = 0;
- static unsigned amnt;
- static char *bpos;
- static unsigned nrem;
- unsigned thisamnt;
- register char *p;
-
- if (pagesz == 0) { /* initialize */
- pagesz = amnt = getpagesize();
- nrem = (int)sbrk(0); /* page align break */
- nrem = pagesz - (nrem&(pagesz-1));
- bpos = sbrk(nrem);
- if ((int)bpos == -1)
- return(NULL);
- #ifdef MSTATS
- b_nsbrked += nrem;
- #endif
- bpos += nrem & (BYTES_WORD-1); /* align pointer */
- nrem &= ~(BYTES_WORD-1);
- memlim[0] = bpos;
- memlim[1] = bpos + nrem;
- }
-
- n = (n+(BYTES_WORD-1))&~(BYTES_WORD-1); /* word align rqst. */
-
- if (n > nrem) { /* need more core */
- tryagain:
- if (n > amnt) { /* big chunk */
- thisamnt = (n+(pagesz-1))&~(pagesz-1);
- if (thisamnt <= MAXINCR) /* increase amnt */
- amnt = thisamnt;
- } else
- thisamnt = amnt;
- p = sbrk(thisamnt);
- if ((int)p == -1) { /* uh-oh, ENOMEM */
- errno = 0; /* call cavalry */
- if (thisamnt >= n+pagesz) {
- amnt = pagesz; /* minimize request */
- goto tryagain;
- }
- thisamnt = n;
- p = mscrounge(&thisamnt); /* search free lists */
- if (p == NULL) { /* we're really out */
- errno = ENOMEM;
- return(NULL);
- }
- }
- #ifdef MSTATS
- else b_nsbrked += thisamnt;
- #endif
- if (p != bpos+nrem) { /* not an increment */
- bfree(bpos, nrem); /* free what we have */
- bpos = p;
- nrem = thisamnt;
- } else /* otherwise tack on */
- nrem += thisamnt;
- if (bpos < memlim[0])
- memlim[0] = bpos;
- if (bpos + nrem > memlim[1])
- memlim[1] = bpos + nrem;
- }
- p = bpos;
- bpos += n; /* advance */
- nrem -= n;
- #ifdef MSTATS
- b_nalloced += n;
- #endif
- return(p);
- }
-
-
- bfree(p, n) /* free n bytes of random memory */
- register char *p;
- register unsigned n;
- {
- register int bucket;
- register unsigned bsiz;
-
- if (n < 1<<FIRSTBUCKET)
- return;
- #ifdef MSTATS
- b_nfreed += n;
- #endif
- /* align pointer */
- bsiz = BYTES_WORD - ((unsigned)p&(BYTES_WORD-1));
- if (bsiz < BYTES_WORD) {
- p += bsiz;
- n -= bsiz;
- }
- if (p < memlim[0])
- memlim[0] = p;
- if (p + n > memlim[1])
- memlim[1] = p + n;
- /* fill big buckets first */
- for (bucket = NBUCKETS-1, bsiz = 1<<(NBUCKETS-1);
- bucket >= FIRSTBUCKET; bucket--, bsiz >>= 1)
- if (n >= bsiz+sizeof(M_HEAD)) {
- ((M_HEAD *)p)->next = free_list[bucket];
- free_list[bucket] = (M_HEAD *)p;
- p += bsiz+sizeof(M_HEAD);
- n -= bsiz+sizeof(M_HEAD);
- }
- }
-
-
- char *
- malloc(n) /* allocate n bytes of memory */
- unsigned n;
- {
- register M_HEAD *mp;
- register int bucket;
- register unsigned bsiz;
- /* don't return NULL on 0 request */
- if (n == 0)
- return(DUMMYLOC);
- /* find first bucket that fits */
- for (bucket = FIRSTBUCKET, bsiz = 1<<FIRSTBUCKET;
- bucket < NBUCKETS; bucket++, bsiz <<= 1)
- if (bsiz >= n)
- break;
- if (bucket >= NBUCKETS) {
- errno = EINVAL;
- return(NULL);
- }
- if (free_list[bucket] == NULL) { /* need more core */
- mp = (M_HEAD *)bmalloc(bsiz+sizeof(M_HEAD));
- if (mp == NULL)
- return(NULL);
- } else { /* got it */
- mp = free_list[bucket];
- free_list[bucket] = mp->next;
- }
- #ifdef MSTATS
- m_nalloced += bsiz + sizeof(M_HEAD);
- m_nwasted += bsiz + sizeof(M_HEAD) - n;
- #endif
- mp->a.magic = MAGIC; /* tag block */
- mp->a.bucket = bucket;
- return((char *)(mp+1));
- }
-
-
- char *
- realloc(op, n) /* reallocate memory using malloc() */
- register char *op;
- unsigned n;
- {
- char *p;
- register unsigned on;
- /* get old size */
- if (op != DUMMYLOC && !BADPTR(op) &&
- ((M_HEAD *)op-1)->a.magic == MAGIC)
- on = 1 << ((M_HEAD *)op-1)->a.bucket;
- else
- on = 0;
- if (n <= on && (n > on>>1 || on == 1<<FIRSTBUCKET))
- return(op); /* same bucket */
- if ((p = malloc(n)) == NULL)
- return(n<=on ? op : NULL);
- if (on) {
- bcopy(op, p, n>on ? on : n);
- free(op);
- }
- return(p);
- }
-
-
- free(p) /* free memory allocated by malloc */
- char *p;
- {
- register M_HEAD *mp;
- register int bucket;
-
- if (p == DUMMYLOC)
- return(1);
- if (BADPTR(p))
- goto invalid;
- mp = (M_HEAD *)p - 1;
- if (mp->a.magic != MAGIC) /* sanity check */
- goto invalid;
- bucket = mp->a.bucket;
- if (bucket < FIRSTBUCKET | bucket >= NBUCKETS)
- goto invalid;
- mp->next = free_list[bucket];
- free_list[bucket] = mp;
- #ifdef MSTATS
- m_nfreed += (1 << bucket) + sizeof(M_HEAD);
- #endif
- return(1);
- invalid:
- errno = EINVAL;
- return(0);
- }
-
-
- #ifdef MSTATS
- printmemstats(fp) /* print memory statistics to stream */
- FILE *fp;
- {
- register int i;
- int n;
- register M_HEAD *mp;
- unsigned int total = 0;
-
- fprintf(fp, "Memory statistics:\n");
- fprintf(fp, "\tbmalloc: %d bytes from sbrk\n", b_nsbrked);
- fprintf(fp, "\tbmalloc: %d bytes allocated\n", b_nalloced);
- fprintf(fp, "\tbmalloc: %d bytes freed\n", b_nfreed);
- fprintf(fp, "\tbmalloc: %d bytes scrounged\n", b_nscrounged);
- fprintf(fp, "\tbmalloc: %d bytes compacted\n", b_ncompacted);
- fprintf(fp, "\tmalloc: %d bytes allocated\n", m_nalloced);
- fprintf(fp, "\tmalloc: %d bytes wasted (%.1f%%)\n", m_nwasted,
- 100.0*m_nwasted/m_nalloced);
- fprintf(fp, "\tmalloc: %d bytes freed\n", m_nfreed);
- for (i = NBUCKETS-1; i >= FIRSTBUCKET; i--) {
- n = 0;
- for (mp = free_list[i]; mp != NULL; mp = mp->next)
- n++;
- if (n) {
- fprintf(fp, "\t%d * %u\n", n, 1<<i);
- total += n * ((1<<i) + sizeof(M_HEAD));
- }
- }
- fprintf(fp, "\t %u total bytes in free list\n", total);
- }
- #endif
-