xref: /original-bsd/bin/csh/alloc.c (revision c374ae69)
1 #ifndef lint
2 static	char *sccsid = "@(#)alloc.c 4.4 (Berkeley from Caltech) 12/13/84";
3 #endif
4 
5 /*
6  * malloc.c (Caltech) 2/21/82
7  * Chris Kingsley, kingsley@cit-20.
8  *
9  * This is a very fast storage allocator.  It allocates blocks of a small
10  * number of different sizes, and keeps free lists of each size.  Blocks that
11  * don't exactly fit are passed up to the next larger size.  In this
12  * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
13  * This is designed for use in a program that uses vast quantities of memory,
14  * but bombs when it runs out.
15  */
16 
17 #include <sys/types.h>
18 
19 #define	NULL 0
20 
21 /*
22  * The overhead on a block is at least 4 bytes.  When free, this space
23  * contains a pointer to the next free block, and the bottom two bits must
24  * be zero.  When in use, the first byte is set to MAGIC, and the second
25  * byte is the size index.  The remaining bytes are for alignment.
26  * If range checking is enabled and the size of the block fits
27  * in two bytes, then the top two bytes hold the size of the requested block
28  * plus the range checking words, and the header word MINUS ONE.
29  */
30 union	overhead {
31 	union	overhead *ov_next;	/* when free */
32 	struct {
33 		u_char	ovu_magic;	/* magic number */
34 		u_char	ovu_index;	/* bucket # */
35 #ifdef RCHECK
36 		u_short	ovu_size;	/* actual block size */
37 		u_int	ovu_rmagic;	/* range magic number */
38 #endif
39 	} ovu;
40 #define	ov_magic	ovu.ovu_magic
41 #define	ov_index	ovu.ovu_index
42 #define	ov_size		ovu.ovu_size
43 #define	ov_rmagic	ovu.ovu_rmagic
44 };
45 
46 #define	MAGIC		0xff		/* magic # on accounting info */
47 #define RMAGIC		0x55555555	/* magic # on range info */
48 #ifdef RCHECK
49 #define	RSLOP		sizeof (u_int)
50 #else
51 #define	RSLOP		0
52 #endif
53 
54 /*
55  * nextf[i] is the pointer to the next free block of size 2^(i+3).  The
56  * smallest allocatable block is 8 bytes.  The overhead information
57  * precedes the data area returned to the user.
58  */
59 #define	NBUCKETS 30
60 static	union overhead *nextf[NBUCKETS];
61 extern	char *sbrk();
62 
63 /*
64  * nmalloc[i] is the difference between the number of mallocs and frees
65  * for a given block size.
66  */
67 static	u_int nmalloc[NBUCKETS];
68 
69 #ifdef debug
70 #define	ASSERT(p)   if (!(p)) botch("p"); else
71 static
72 botch(s)
73 char *s;
74 {
75 	printf("assertion botched: %s\n",s);
76 	abort();
77 }
78 #else
79 #define	ASSERT(p)
80 #endif
81 
82 char *
83 malloc(nbytes)
84 	register unsigned nbytes;
85 {
86   	register union overhead *p;
87   	register int bucket = 0;
88   	register unsigned shiftr;
89 
90 	/*
91 	 * Convert amount of memory requested into
92 	 * closest block size stored in hash buckets
93 	 * which satisfies request.  Account for
94 	 * space used per block for accounting.
95 	 */
96   	nbytes += sizeof (union overhead) + RSLOP;
97   	nbytes = (nbytes + 3) &~ 3;
98   	shiftr = (nbytes - 1) >> 2;
99 	/* apart from this loop, this is O(1) */
100   	while (shiftr >>= 1)
101   		bucket++;
102 	/*
103 	 * If nothing in hash bucket right now,
104 	 * request more memory from the system.
105 	 */
106   	if (nextf[bucket] == NULL)
107   		morecore(bucket);
108   	if ((p = (union overhead *)nextf[bucket]) == NULL)
109   		return (NULL);
110 	/* remove from linked list */
111   	nextf[bucket] = nextf[bucket]->ov_next;
112 	p->ov_magic = MAGIC;
113 	p->ov_index= bucket;
114   	nmalloc[bucket]++;
115 #ifdef RCHECK
116 	/*
117 	 * Record allocated size of block and
118 	 * bound space with magic numbers.
119 	 */
120   	if (nbytes <= 0x10000)
121 		p->ov_size = nbytes - 1;
122 	p->ov_rmagic = RMAGIC;
123   	*((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC;
124 #endif
125   	return ((char *)(p + 1));
126 }
127 
128 /*
129  * Allocate more memory to the indicated bucket.
130  */
131 static
132 morecore(bucket)
133 	register bucket;
134 {
135   	register union overhead *op;
136   	register int rnu;       /* 2^rnu bytes will be requested */
137   	register int nblks;     /* become nblks blocks of the desired size */
138 	register int siz;
139 
140   	if (nextf[bucket])
141   		return;
142 	/*
143 	 * Insure memory is allocated
144 	 * on a page boundary.  Should
145 	 * make getpageize call?
146 	 */
147   	op = (union overhead *)sbrk(0);
148   	if ((int)op & 0x3ff)
149   		(void) sbrk(1024 - ((int)op & 0x3ff));
150 	/* take 2k unless the block is bigger than that */
151   	rnu = (bucket <= 8) ? 11 : bucket + 3;
152   	nblks = 1 << (rnu - (bucket + 3));  /* how many blocks to get */
153   	if (rnu < bucket)
154 		rnu = bucket;
155 	op = (union overhead *)sbrk(1 << rnu);
156 	/* no more room! */
157   	if ((int)op == -1)
158   		return;
159 	/*
160 	 * Round up to minimum allocation size boundary
161 	 * and deduct from block count to reflect.
162 	 */
163   	if ((int)op & 7) {
164   		op = (union overhead *)(((int)op + 8) &~ 7);
165   		nblks--;
166   	}
167 	/*
168 	 * Add new memory allocated to that on
169 	 * free list for this hash bucket.
170 	 */
171   	nextf[bucket] = op;
172   	siz = 1 << (bucket + 3);
173   	while (--nblks > 0) {
174 		op->ov_next = (union overhead *)((caddr_t)op + siz);
175 		op = (union overhead *)((caddr_t)op + siz);
176   	}
177 }
178 
179 free(cp)
180 	char *cp;
181 {
182   	register int size;
183 	register union overhead *op;
184 
185   	if (cp == NULL)
186   		return;
187 	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
188 
189 	/*
190 	** The following botch is because csh tries to free a free block
191 	** when processing the =~ or !~ operators. -- layer@ucbmonet
192 	*/
193 #ifdef CSHbotch /* was debug */
194   	ASSERT(op->ov_magic == MAGIC);		/* make sure it was in use */
195 #else
196 	if (op->ov_magic != MAGIC)
197 		return;				/* sanity */
198 #endif
199 
200 #ifdef RCHECK
201   	ASSERT(op->ov_rmagic == RMAGIC);
202 	if (op->ov_index <= 13)
203 		ASSERT(*(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC);
204 #endif
205   	ASSERT(op->ov_index < NBUCKETS);
206   	size = op->ov_index;
207 	op->ov_next = nextf[size];
208   	nextf[size] = op;
209   	nmalloc[size]--;
210 }
211 
212 /*
213  * When a program attempts "storage compaction" as mentioned in the
214  * old malloc man page, it realloc's an already freed block.  Usually
215  * this is the last block it freed; occasionally it might be farther
216  * back.  We have to search all the free lists for the block in order
217  * to determine its bucket: 1st we make one pass thru the lists
218  * checking only the first block in each; if that fails we search
219  * ``realloc_srchlen'' blocks in each list for a match (the variable
220  * is extern so the caller can modify it).  If that fails we just copy
221  * however many bytes was given to realloc() and hope it's not huge.
222  */
223 int realloc_srchlen = 4;	/* 4 should be plenty.  -1 means whole list */
224 
225 char *
226 realloc(cp, nbytes)
227 	char *cp;
228 	unsigned nbytes;
229 {
230   	register u_int onb;
231 	union overhead *op;
232   	char *res;
233 	register int i;
234 	int was_alloced = 0;
235 
236   	if (cp == NULL)
237   		return (malloc(nbytes));
238 	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
239 	if (op->ov_magic == MAGIC) {
240 		was_alloced++;
241 		i = op->ov_index;
242 	}
243 	else {		/* already free: he is doing "compaction" (tee hee) */
244 		if ((i = findbucket(op, 1)) < 0 &&
245 		    (i = findbucket(op, realloc_srchlen)) < 0)
246 			i = 0;		/* assume smallest possible */
247 	}
248 	onb = (1 << (i + 3)) - sizeof (*op) - RSLOP;
249 	if (was_alloced &&		/* avoid the copy if same size block */
250 	    nbytes <= onb && nbytes > (onb >> 1) - sizeof(*op) - RSLOP)
251 		return(cp);
252   	if ((res = malloc(nbytes)) == NULL)
253   		return (NULL);
254   	if (cp != res)			/* common optimization */
255 		bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
256   	if (was_alloced)
257 		free(cp);
258   	return (res);
259 }
260 
261 /*
262  * Search ``srchlen'' elements of each free list for a block whose
263  * header starts at ``freep''.  If srchlen is -1 search the whole list.
264  * Return bucket number, or -1 if not found.
265  */
266 static
267 findbucket(freep, srchlen)
268 union overhead *freep;
269 int srchlen;
270 {
271 	register union overhead *p;
272 	register int i, j;
273 
274 	for (i = 0; i < NBUCKETS; i++)
275 		for (j = 0, p = nextf[i]; p && j != srchlen; j++, p = p->ov_next)
276 			if (p == freep)
277 				return (i);
278 	return (-1);
279 }
280 
281 /*
282  * mstats - print out statistics about malloc
283  *
284  * Prints two lines of numbers, one showing the length of the free list
285  * for each size category, the second showing the number of mallocs -
286  * frees for each size category.
287  */
288 showall(s)
289 char **s;
290 {
291 	register int i, j;
292 	register union overhead *p;
293 	int totfree = 0,
294 	totused = 0;
295 
296 	if (s[1])
297 		printf("Memory allocation statistics %s\nfree:", s[1]);
298 	for (i = 0; i < NBUCKETS; i++) {
299 		for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
300 			;
301 
302 		if (s[1])
303 			printf(" %d", j);
304 		totfree += j * (1 << (i + 3));
305 	}
306 	if (s[1])
307 		printf("\nused:");
308 	for (i = 0; i < NBUCKETS; i++) {
309 		if (s[1])
310 			printf(" %d", nmalloc[i]);
311 		totused += nmalloc[i] * (1 << (i + 3));
312 	}
313 	if (s[1])
314 		printf("\n");
315 	printf("Total in use: %d, total free: %d\n", totused, totfree);
316 }
317