xref: /csrg-svn/lib/libc/stdlib/malloc.c (revision 17438)
113259Sroot #ifndef lint
2*17438Sralph static char sccsid[] = "@(#)malloc.c	4.6 (Berkeley) 11/28/84";
313259Sroot #endif
414953Skarels 
514953Skarels /*
614953Skarels  * malloc.c (Caltech) 2/21/82
714953Skarels  * Chris Kingsley, kingsley@cit-20.
814953Skarels  *
914953Skarels  * This is a very fast storage allocator.  It allocates blocks of a small
1014953Skarels  * number of different sizes, and keeps free lists of each size.  Blocks that
1114953Skarels  * don't exactly fit are passed up to the next larger size.  In this
1214953Skarels  * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
1314953Skarels  * This is designed for use in a program that uses vast quantities of memory,
1414953Skarels  * but bombs when it runs out.
1514953Skarels  */
1614953Skarels 
1714953Skarels #include <sys/types.h>
1814953Skarels 
1914953Skarels #define	NULL 0
2014953Skarels 
2114953Skarels /*
2214953Skarels  * The overhead on a block is at least 4 bytes.  When free, this space
2314953Skarels  * contains a pointer to the next free block, and the bottom two bits must
2414953Skarels  * be zero.  When in use, the first byte is set to MAGIC, and the second
2514953Skarels  * byte is the size index.  The remaining bytes are for alignment.
2614953Skarels  * If range checking is enabled and the size of the block fits
2714953Skarels  * in two bytes, then the top two bytes hold the size of the requested block
2814953Skarels  * plus the range checking words, and the header word MINUS ONE.
2914953Skarels  */
3014953Skarels union	overhead {
3114953Skarels 	union	overhead *ov_next;	/* when free */
3214953Skarels 	struct {
3317333Sralph #ifndef RCHECK
3414953Skarels 		u_char	ovu_magic;	/* magic number */
3514953Skarels 		u_char	ovu_index;	/* bucket # */
3617333Sralph #else
3717333Sralph 		u_int	ovu_size;	/* actual block size */
3817333Sralph 		u_char	ovu_magic;	/* magic number */
3917333Sralph 		u_char	ovu_index;	/* bucket # */
4017333Sralph 		u_short	ovu_rmagic;	/* range magic number */
4114953Skarels #endif
4214953Skarels 	} ovu;
4314953Skarels #define	ov_magic	ovu.ovu_magic
4414953Skarels #define	ov_index	ovu.ovu_index
4517333Sralph #define	ov_rmagic	ovu.ovu_rmagic
4614953Skarels #define	ov_size		ovu.ovu_size
4714953Skarels };
4814953Skarels 
4917333Sralph #define	MAGIC		0xef		/* magic # on accounting info */
5017333Sralph #define RMAGIC		0x5555		/* magic # on range info */
5117333Sralph 
5214953Skarels #ifdef RCHECK
5317333Sralph #define	RSLOP		sizeof (u_short)
5414953Skarels #else
5514953Skarels #define	RSLOP		0
5614953Skarels #endif
5714953Skarels 
5814953Skarels /*
5914953Skarels  * nextf[i] is the pointer to the next free block of size 2^(i+3).  The
6014953Skarels  * smallest allocatable block is 8 bytes.  The overhead information
6114953Skarels  * precedes the data area returned to the user.
6214953Skarels  */
6314953Skarels #define	NBUCKETS 30
6414953Skarels static	union overhead *nextf[NBUCKETS];
6514953Skarels extern	char *sbrk();
6614953Skarels 
6717333Sralph static	int pagesz;			/* page size */
6817333Sralph static	int pagebucket;			/* page size bucket */
6917333Sralph 
7014953Skarels #ifdef MSTATS
7114953Skarels /*
7214953Skarels  * nmalloc[i] is the difference between the number of mallocs and frees
7314953Skarels  * for a given block size.
7414953Skarels  */
7514953Skarels static	u_int nmalloc[NBUCKETS];
7614953Skarels #include <stdio.h>
7714953Skarels #endif
7814953Skarels 
7917333Sralph #ifdef DEBUG
8017333Sralph #define	ASSERT(p)   if (!(p)) botch("p")
8114953Skarels static
8213259Sroot botch(s)
8315003Ssam 	char *s;
8413259Sroot {
8515003Ssam 
8615003Ssam 	printf("assertion botched: %s\n", s);
8713259Sroot 	abort();
8813259Sroot }
8913259Sroot #else
9014953Skarels #define	ASSERT(p)
9113259Sroot #endif
9213259Sroot 
9313259Sroot char *
9413259Sroot malloc(nbytes)
9517333Sralph 	unsigned nbytes;
9613259Sroot {
9717333Sralph   	register union overhead *op;
9817333Sralph   	register int bucket;
9917333Sralph 	register unsigned amt, n;
10013259Sroot 
10114953Skarels 	/*
10217333Sralph 	 * First time malloc is called, setup page size and
10317333Sralph 	 * align break pointer so all data will be page aligned.
10414953Skarels 	 */
10517333Sralph 	if (pagesz == 0) {
10617333Sralph 		pagesz = n = getpagesize();
10717333Sralph 		op = (union overhead *)sbrk(0);
10817333Sralph   		n = n - sizeof (*op) - ((int)op & (n - 1));
10917333Sralph 		if (n < 0)
11017333Sralph 			n += pagesz;
11117333Sralph   		if (n) {
11217333Sralph   			if (sbrk(n) == (char *)-1)
11317333Sralph 				return (NULL);
11417333Sralph 		}
11517333Sralph 		bucket = 0;
11617333Sralph 		amt = 8;
11717333Sralph 		while (pagesz > amt) {
11817333Sralph 			amt <<= 1;
11917333Sralph 			bucket++;
12017333Sralph 		}
12117333Sralph 		pagebucket = bucket;
12217333Sralph 	}
12314953Skarels 	/*
12417333Sralph 	 * Convert amount of memory requested into closest block size
12517333Sralph 	 * stored in hash buckets which satisfies request.
12617333Sralph 	 * Account for space used per block for accounting.
12717333Sralph 	 */
12817333Sralph 	if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) {
12917333Sralph #ifndef RCHECK
13017333Sralph 		amt = 8;	/* size of first bucket */
13117333Sralph 		bucket = 0;
13217333Sralph #else
13317333Sralph 		amt = 16;	/* size of first bucket */
13417333Sralph 		bucket = 1;
13517333Sralph #endif
13617333Sralph 		n = -(sizeof (*op) + RSLOP);
13717333Sralph 	} else {
13817333Sralph 		amt = pagesz;
13917333Sralph 		bucket = pagebucket;
14017333Sralph 	}
14117333Sralph 	while (nbytes > amt + n) {
14217333Sralph 		amt <<= 1;
14317333Sralph 		bucket++;
14417333Sralph 	}
14517333Sralph 	/*
14614953Skarels 	 * If nothing in hash bucket right now,
14714953Skarels 	 * request more memory from the system.
14814953Skarels 	 */
14917333Sralph   	if ((op = nextf[bucket]) == NULL) {
15014953Skarels   		morecore(bucket);
15117333Sralph   		if ((op = nextf[bucket]) == NULL)
15217333Sralph   			return (NULL);
15317333Sralph 	}
15414953Skarels 	/* remove from linked list */
15517333Sralph   	nextf[bucket] = op->ov_next;
15617333Sralph 	op->ov_magic = MAGIC;
15717333Sralph 	op->ov_index = bucket;
15814953Skarels #ifdef MSTATS
15914953Skarels   	nmalloc[bucket]++;
16014953Skarels #endif
16114953Skarels #ifdef RCHECK
16214953Skarels 	/*
16314953Skarels 	 * Record allocated size of block and
16414953Skarels 	 * bound space with magic numbers.
16514953Skarels 	 */
166*17438Sralph 	op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
16717333Sralph 	op->ov_rmagic = RMAGIC;
168*17438Sralph   	*(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
16914953Skarels #endif
17017333Sralph   	return ((char *)(op + 1));
17113259Sroot }
17213259Sroot 
17314953Skarels /*
17414953Skarels  * Allocate more memory to the indicated bucket.
17514953Skarels  */
17614953Skarels static
17714953Skarels morecore(bucket)
17817333Sralph 	int bucket;
17913259Sroot {
18014953Skarels   	register union overhead *op;
18117333Sralph 	register int sz;		/* size of desired block */
18217333Sralph   	register int amt;		/* amount to allocate */
18317333Sralph   	register int nblks;		/* how many blocks we get */
18413259Sroot 
18517333Sralph 	sz = 1 << (bucket + 3);
18617333Sralph 	if (sz < pagesz) {
18717333Sralph 		amt = pagesz;
18817333Sralph   		nblks = amt / sz;
18917333Sralph 	} else {
19017333Sralph 		amt = sz + pagesz;
19117333Sralph 		nblks = 1;
19217333Sralph 	}
19317333Sralph 	op = (union overhead *)sbrk(amt);
19414953Skarels 	/* no more room! */
19514953Skarels   	if ((int)op == -1)
19614953Skarels   		return;
19714953Skarels 	/*
19814953Skarels 	 * Add new memory allocated to that on
19914953Skarels 	 * free list for this hash bucket.
20014953Skarels 	 */
20114953Skarels   	nextf[bucket] = op;
20214953Skarels   	while (--nblks > 0) {
20317333Sralph 		op->ov_next = (union overhead *)((caddr_t)op + sz);
20417333Sralph 		op = (union overhead *)((caddr_t)op + sz);
20514953Skarels   	}
20613259Sroot }
20713259Sroot 
20814953Skarels free(cp)
20914953Skarels 	char *cp;
21014953Skarels {
21114953Skarels   	register int size;
21214953Skarels 	register union overhead *op;
21313259Sroot 
21414953Skarels   	if (cp == NULL)
21514953Skarels   		return;
21614953Skarels 	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
21717333Sralph #ifdef DEBUG
21814953Skarels   	ASSERT(op->ov_magic == MAGIC);		/* make sure it was in use */
21914953Skarels #else
22014953Skarels 	if (op->ov_magic != MAGIC)
22114953Skarels 		return;				/* sanity */
22214953Skarels #endif
22314953Skarels #ifdef RCHECK
22414953Skarels   	ASSERT(op->ov_rmagic == RMAGIC);
22517333Sralph 	ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
22614953Skarels #endif
22714953Skarels   	size = op->ov_index;
22817333Sralph   	ASSERT(size < NBUCKETS);
22914953Skarels 	op->ov_next = nextf[size];
23014953Skarels   	nextf[size] = op;
23114953Skarels #ifdef MSTATS
23214953Skarels   	nmalloc[size]--;
23314953Skarels #endif
23414953Skarels }
23514953Skarels 
23614953Skarels /*
23714953Skarels  * When a program attempts "storage compaction" as mentioned in the
23814953Skarels  * old malloc man page, it realloc's an already freed block.  Usually
23914953Skarels  * this is the last block it freed; occasionally it might be farther
24014953Skarels  * back.  We have to search all the free lists for the block in order
24114953Skarels  * to determine its bucket: 1st we make one pass thru the lists
24214953Skarels  * checking only the first block in each; if that fails we search
24314953Skarels  * ``realloc_srchlen'' blocks in each list for a match (the variable
24414953Skarels  * is extern so the caller can modify it).  If that fails we just copy
24514953Skarels  * however many bytes was given to realloc() and hope it's not huge.
24614953Skarels  */
24715003Ssam int realloc_srchlen = 4;	/* 4 should be plenty, -1 =>'s whole list */
24814953Skarels 
24913259Sroot char *
25014953Skarels realloc(cp, nbytes)
25114953Skarels 	char *cp;
25214953Skarels 	unsigned nbytes;
25314953Skarels {
25417333Sralph   	register u_int onb, i;
25514953Skarels 	union overhead *op;
25614953Skarels   	char *res;
25714953Skarels 	int was_alloced = 0;
25814953Skarels 
25914953Skarels   	if (cp == NULL)
26014953Skarels   		return (malloc(nbytes));
26114953Skarels 	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
26214953Skarels 	if (op->ov_magic == MAGIC) {
26314953Skarels 		was_alloced++;
26414953Skarels 		i = op->ov_index;
26515003Ssam 	} else {
26615003Ssam 		/*
26715003Ssam 		 * Already free, doing "compaction".
26815003Ssam 		 *
26915003Ssam 		 * Search for the old block of memory on the
27015003Ssam 		 * free list.  First, check the most common
27115003Ssam 		 * case (last element free'd), then (this failing)
27215003Ssam 		 * the last ``realloc_srchlen'' items free'd.
27315003Ssam 		 * If all lookups fail, then assume the size of
27415003Ssam 		 * the memory block being realloc'd is the
27515003Ssam 		 * smallest possible.
27615003Ssam 		 */
27714953Skarels 		if ((i = findbucket(op, 1)) < 0 &&
27814953Skarels 		    (i = findbucket(op, realloc_srchlen)) < 0)
27917333Sralph #ifndef RCHECK
28015003Ssam 			i = 0;
28117333Sralph #else
28217333Sralph 			i = 1;	/* smallest possible w/ RCHECK */
28317333Sralph #endif
28414953Skarels 	}
28517333Sralph 	onb = 1 << (i + 3);
28617333Sralph 	if (onb < pagesz)
28717333Sralph 		onb -= sizeof (*op) + RSLOP;
28817333Sralph 	else
28917333Sralph 		onb += pagesz - sizeof (*op) - RSLOP;
29015003Ssam 	/* avoid the copy if same size block */
29117333Sralph 	if (was_alloced) {
29217333Sralph 		if (i) {
29317333Sralph 			i = 1 << (i + 2);
29417333Sralph 			if (i < pagesz)
29517333Sralph 				i -= sizeof (*op) + RSLOP;
29617333Sralph 			else
29717333Sralph 				i += pagesz - sizeof (*op) - RSLOP;
29817333Sralph 		}
29917333Sralph 		if (nbytes <= onb && nbytes > i) {
30017332Sralph #ifdef RCHECK
301*17438Sralph 			op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
30217333Sralph 			*(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
30317332Sralph #endif
30417333Sralph 			return(cp);
30517333Sralph 		} else
30617333Sralph 			free(cp);
30717332Sralph 	}
30814953Skarels   	if ((res = malloc(nbytes)) == NULL)
30914953Skarels   		return (NULL);
31014953Skarels   	if (cp != res)			/* common optimization */
31114953Skarels 		bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
31214953Skarels   	return (res);
31314953Skarels }
31414953Skarels 
31514953Skarels /*
31614953Skarels  * Search ``srchlen'' elements of each free list for a block whose
31714953Skarels  * header starts at ``freep''.  If srchlen is -1 search the whole list.
31814953Skarels  * Return bucket number, or -1 if not found.
31914953Skarels  */
32014953Skarels static
32114953Skarels findbucket(freep, srchlen)
32215003Ssam 	union overhead *freep;
32315003Ssam 	int srchlen;
32413259Sroot {
32514953Skarels 	register union overhead *p;
32614953Skarels 	register int i, j;
32713259Sroot 
32815003Ssam 	for (i = 0; i < NBUCKETS; i++) {
32915003Ssam 		j = 0;
33015003Ssam 		for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
33114953Skarels 			if (p == freep)
33214953Skarels 				return (i);
33315003Ssam 			j++;
33415003Ssam 		}
33515003Ssam 	}
33614953Skarels 	return (-1);
33713259Sroot }
33813259Sroot 
33914953Skarels #ifdef MSTATS
34014953Skarels /*
34114953Skarels  * mstats - print out statistics about malloc
34214953Skarels  *
34314953Skarels  * Prints two lines of numbers, one showing the length of the free list
34414953Skarels  * for each size category, the second showing the number of mallocs -
34514953Skarels  * frees for each size category.
34614953Skarels  */
34714953Skarels mstats(s)
34814953Skarels 	char *s;
34913259Sroot {
35014953Skarels   	register int i, j;
35114953Skarels   	register union overhead *p;
35214953Skarels   	int totfree = 0,
35314953Skarels   	totused = 0;
35414953Skarels 
35514953Skarels   	fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
35614953Skarels   	for (i = 0; i < NBUCKETS; i++) {
35714953Skarels   		for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
35814953Skarels   			;
35914953Skarels   		fprintf(stderr, " %d", j);
36014953Skarels   		totfree += j * (1 << (i + 3));
36114953Skarels   	}
36214953Skarels   	fprintf(stderr, "\nused:\t");
36314953Skarels   	for (i = 0; i < NBUCKETS; i++) {
36414953Skarels   		fprintf(stderr, " %d", nmalloc[i]);
36514953Skarels   		totused += nmalloc[i] * (1 << (i + 3));
36614953Skarels   	}
36715003Ssam   	fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
36815003Ssam 	    totused, totfree);
36913259Sroot }
37013259Sroot #endif
371