alloc.c revision 1.2 1 /*-
2 * Copyright (c) 1983, 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char sccsid[] = "@(#)alloc.c 5.8 (Berkeley) 6/8/91";
36 static char rcsid[] = "$Id: alloc.c,v 1.2 1993/03/22 08:04:00 cgd Exp $";
37 #endif /* not lint */
38
39 /*
40 * tc.alloc.c from malloc.c (Caltech) 2/21/82
41 * Chris Kingsley, kingsley@cit-20.
42 *
43 * This is a very fast storage allocator. It allocates blocks of a small
44 * number of different sizes, and keeps free lists of each size. Blocks that
45 * don't exactly fit are passed up to the next larger size. In this
46 * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
47 * This is designed for use in a program that uses vast quantities of memory,
48 * but bombs when it runs out.
49 */
50
51 #include <sys/types.h>
52 #include <unistd.h>
53 #include <string.h>
54 #if __STDC__
55 # include <stdarg.h>
56 #else
57 # include <varargs.h>
58 #endif
59
60 #include "csh.h"
61 #include "extern.h"
62
63 char *memtop = NULL; /* PWP: top of current memory */
64 char *membot = NULL; /* PWP: bottom of allocatable memory */
65
66 #ifndef SYSMALLOC
67
68 #undef RCHECK
69 #undef DEBUG
70
71
72 #ifndef NULL
73 #define NULL 0
74 #endif
75
76
77 /*
78 * The overhead on a block is at least 4 bytes. When free, this space
79 * contains a pointer to the next free block, and the bottom two bits must
80 * be zero. When in use, the first byte is set to MAGIC, and the second
81 * byte is the size index. The remaining bytes are for alignment.
82 * If range checking is enabled and the size of the block fits
83 * in two bytes, then the top two bytes hold the size of the requested block
84 * plus the range checking words, and the header word MINUS ONE.
85 */
86
87 #define ROUNDUP 7
88
89 #define ALIGN(a) (((a) + ROUNDUP) & ~ROUNDUP)
90
91 union overhead {
92 union overhead *ov_next; /* when free */
93 struct {
94 u_char ovu_magic; /* magic number */
95 u_char ovu_index; /* bucket # */
96 #ifdef RCHECK
97 u_short ovu_size; /* actual block size */
98 u_int ovu_rmagic; /* range magic number */
99 #endif
100 } ovu;
101 #define ov_magic ovu.ovu_magic
102 #define ov_index ovu.ovu_index
103 #define ov_size ovu.ovu_size
104 #define ov_rmagic ovu.ovu_rmagic
105 };
106
107 #define MAGIC 0xfd /* magic # on accounting info */
108 #define RMAGIC 0x55555555 /* magic # on range info */
109 #ifdef RCHECK
110 #define RSLOP sizeof (u_int)
111 #else
112 #define RSLOP 0
113 #endif
114
115 /*
116 * nextf[i] is the pointer to the next free block of size 2^(i+3). The
117 * smallest allocatable block is 8 bytes. The overhead information
118 * precedes the data area returned to the user.
119 */
120 #define NBUCKETS 30
121 static union overhead *nextf[NBUCKETS];
122
123 static int findbucket __P((union overhead *, int));
124 static void morecore __P((int));
125
126 /*
127 * nmalloc[i] is the difference between the number of mallocs and frees
128 * for a given block size.
129 */
130 static u_int nmalloc[NBUCKETS];
131
132
133 #ifdef DEBUG
134 #define CHECK(a, str, p) \
135 if (a) { \
136 xprintf(str, p); \
137 xprintf("memtop = %lx membot = %lx.\n", memtop, membot); \
138 abort(); \
139 } \
140 else
141 #else
142 #define CHECK(a, str, p) \
143 if (a) { \
144 xprintf(str, p); \
145 xprintf("memtop = %lx membot = %lx.\n", memtop, membot); \
146 return; \
147 } \
148 else
149 #endif
150
151 ptr_t
152 malloc(nbytes)
153 register size_t nbytes;
154 {
155 #ifndef lint
156 register union overhead *p;
157 register int bucket = 0;
158 register unsigned shiftr;
159
160 /*
161 * Convert amount of memory requested into closest block size stored in
162 * hash buckets which satisfies request. Account for space used per block
163 * for accounting.
164 */
165 nbytes = ALIGN(ALIGN(sizeof(union overhead)) + nbytes + RSLOP);
166 shiftr = (nbytes - 1) >> 2;
167
168 /* apart from this loop, this is O(1) */
169 while (shiftr >>= 1)
170 bucket++;
171 /*
172 * If nothing in hash bucket right now, request more memory from the
173 * system.
174 */
175 if (nextf[bucket] == NULL)
176 morecore(bucket);
177 if ((p = (union overhead *) nextf[bucket]) == NULL) {
178 child++;
179 #ifndef DEBUG
180 stderror(ERR_NOMEM);
181 #else
182 showall();
183 xprintf("nbytes=%d: Out of memory\n", nbytes);
184 abort();
185 #endif
186 /* fool lint */
187 return ((ptr_t) 0);
188 }
189 /* remove from linked list */
190 nextf[bucket] = nextf[bucket]->ov_next;
191 p->ov_magic = MAGIC;
192 p->ov_index = bucket;
193 nmalloc[bucket]++;
194 #ifdef RCHECK
195 /*
196 * Record allocated size of block and bound space with magic numbers.
197 */
198 if (nbytes <= 0x10000)
199 p->ov_size = nbytes - 1;
200 p->ov_rmagic = RMAGIC;
201 *((u_int *) (((caddr_t) p) + nbytes - RSLOP)) = RMAGIC;
202 #endif
203 return ((ptr_t) (((caddr_t) p) + ALIGN(sizeof(union overhead))));
204 #else
205 if (nbytes)
206 return ((ptr_t) 0);
207 else
208 return ((ptr_t) 0);
209 #endif /* !lint */
210 }
211
212 #ifndef lint
213 /*
214 * Allocate more memory to the indicated bucket.
215 */
216 static void
217 morecore(bucket)
218 register int bucket;
219 {
220 register union overhead *op;
221 register int rnu; /* 2^rnu bytes will be requested */
222 register int nblks; /* become nblks blocks of the desired size */
223 register int siz;
224
225 if (nextf[bucket])
226 return;
227 /*
228 * Insure memory is allocated on a page boundary. Should make getpageize
229 * call?
230 */
231 op = (union overhead *) sbrk(0);
232 memtop = (char *) op;
233 if (membot == NULL)
234 membot = memtop;
235 if ((int) op & 0x3ff) {
236 memtop = (char *) sbrk(1024 - ((int) op & 0x3ff));
237 memtop += 1024 - ((int) op & 0x3ff);
238 }
239
240 /* take 2k unless the block is bigger than that */
241 rnu = (bucket <= 8) ? 11 : bucket + 3;
242 nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */
243 if (rnu < bucket)
244 rnu = bucket;
245 memtop = (char *) sbrk(1 << rnu); /* PWP */
246 op = (union overhead *) memtop;
247 memtop += 1 << rnu;
248 /* no more room! */
249 if ((int) op == -1)
250 return;
251 /*
252 * Round up to minimum allocation size boundary and deduct from block count
253 * to reflect.
254 */
255 if (((u_int) op) & ROUNDUP) {
256 op = (union overhead *) (((u_int) op + (ROUNDUP + 1)) & ~ROUNDUP);
257 nblks--;
258 }
259 /*
260 * Add new memory allocated to that on free list for this hash bucket.
261 */
262 nextf[bucket] = op;
263 siz = 1 << (bucket + 3);
264 while (--nblks > 0) {
265 op->ov_next = (union overhead *) (((caddr_t) op) + siz);
266 op = (union overhead *) (((caddr_t) op) + siz);
267 }
268 }
269
270 #endif
271
272 #ifdef sun
273 int
274 #else
275 void
276 #endif
277 free(cp)
278 ptr_t cp;
279 {
280 #ifndef lint
281 register int size;
282 register union overhead *op;
283
284 if (cp == NULL)
285 return;
286 CHECK(!memtop || !membot, "free(%lx) called before any allocations.", cp);
287 CHECK(cp > (ptr_t) memtop, "free(%lx) above top of memory.", cp);
288 CHECK(cp < (ptr_t) membot, "free(%lx) above top of memory.", cp);
289 op = (union overhead *) (((caddr_t) cp) - ALIGN(sizeof(union overhead)));
290 CHECK(op->ov_magic != MAGIC, "free(%lx) bad block.", cp);
291
292 #ifdef RCHECK
293 if (op->ov_index <= 13)
294 CHECK(*(u_int *) ((caddr_t) op + op->ov_size + 1 - RSLOP) != RMAGIC,
295 "free(%lx) bad range check.", cp);
296 #endif
297 CHECK(op->ov_index >= NBUCKETS, "free(%lx) bad block index.", cp);
298 size = op->ov_index;
299 op->ov_next = nextf[size];
300 nextf[size] = op;
301
302 nmalloc[size]--;
303
304 #else
305 if (cp == NULL)
306 return;
307 #endif
308 }
309
310 ptr_t
311 calloc(i, j)
312 size_t i, j;
313 {
314 #ifndef lint
315 register char *cp, *scp;
316
317 i *= j;
318 scp = cp = (char *) xmalloc((size_t) i);
319 if (i != 0)
320 do
321 *cp++ = 0;
322 while (--i);
323
324 return (scp);
325 #else
326 if (i && j)
327 return ((ptr_t) 0);
328 else
329 return ((ptr_t) 0);
330 #endif
331 }
332
333 /*
334 * When a program attempts "storage compaction" as mentioned in the
335 * old malloc man page, it realloc's an already freed block. Usually
336 * this is the last block it freed; occasionally it might be farther
337 * back. We have to search all the free lists for the block in order
338 * to determine its bucket: 1st we make one pass thru the lists
339 * checking only the first block in each; if that fails we search
340 * ``realloc_srchlen'' blocks in each list for a match (the variable
341 * is extern so the caller can modify it). If that fails we just copy
342 * however many bytes was given to realloc() and hope it's not huge.
343 */
344 #ifndef lint
345 int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
346
347 #endif /* lint */
348
349 ptr_t
350 realloc(cp, nbytes)
351 ptr_t cp;
352 size_t nbytes;
353 {
354 #ifndef lint
355 register u_int onb;
356 union overhead *op;
357 char *res;
358 register int i;
359 int was_alloced = 0;
360
361 if (cp == NULL)
362 return (malloc(nbytes));
363 op = (union overhead *) (((caddr_t) cp) - ALIGN(sizeof(union overhead)));
364 if (op->ov_magic == MAGIC) {
365 was_alloced++;
366 i = op->ov_index;
367 }
368 else
369 /*
370 * Already free, doing "compaction".
371 *
372 * Search for the old block of memory on the free list. First, check the
373 * most common case (last element free'd), then (this failing) the last
374 * ``realloc_srchlen'' items free'd. If all lookups fail, then assume
375 * the size of the memory block being realloc'd is the smallest
376 * possible.
377 */
378 if ((i = findbucket(op, 1)) < 0 &&
379 (i = findbucket(op, realloc_srchlen)) < 0)
380 i = 0;
381
382 onb = ALIGN(nbytes + ALIGN(sizeof(union overhead)) + RSLOP);
383
384 /* avoid the copy if same size block */
385 if (was_alloced && (onb < (1 << (i + 3))) && (onb >= (1 << (i + 2))))
386 return ((ptr_t) cp);
387 if ((res = malloc(nbytes)) == NULL)
388 return ((ptr_t) 0);
389 if (cp != res) /* common optimization */
390 bcopy(cp, res, nbytes);
391 if (was_alloced)
392 free(cp);
393 return (res);
394 #else
395 if (cp && nbytes)
396 return ((ptr_t) 0);
397 else
398 return ((ptr_t) 0);
399 #endif /* !lint */
400 }
401
402
403
404 #ifndef lint
405 /*
406 * Search ``srchlen'' elements of each free list for a block whose
407 * header starts at ``freep''. If srchlen is -1 search the whole list.
408 * Return bucket number, or -1 if not found.
409 */
410 static int
411 findbucket(freep, srchlen)
412 union overhead *freep;
413 int srchlen;
414 {
415 register union overhead *p;
416 register int i, j;
417
418 for (i = 0; i < NBUCKETS; i++) {
419 j = 0;
420 for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
421 if (p == freep)
422 return (i);
423 j++;
424 }
425 }
426 return (-1);
427 }
428
429 #endif
430
431
432 #else /* SYSMALLOC */
433
434 /**
435 ** ``Protected versions'' of malloc, realloc, calloc, and free
436 **
437 ** On many systems:
438 **
439 ** 1. malloc(0) is bad
440 ** 2. free(0) is bad
441 ** 3. realloc(0, n) is bad
442 ** 4. realloc(n, 0) is bad
443 **
444 ** Also we call our error routine if we run out of memory.
445 **/
446 char *
447 Malloc(n)
448 size_t n;
449 {
450 ptr_t ptr;
451
452 n = n ? n : 1;
453
454 if ((ptr = malloc(n)) == (ptr_t) 0) {
455 child++;
456 stderror(ERR_NOMEM);
457 }
458 return ((char *) ptr);
459 }
460
461 char *
462 Realloc(p, n)
463 ptr_t p;
464 size_t n;
465 {
466 ptr_t ptr;
467
468 n = n ? n : 1;
469 if ((ptr = (p ? realloc(p, n) : malloc(n))) == (ptr_t) 0) {
470 child++;
471 stderror(ERR_NOMEM);
472 }
473 return ((char *) ptr);
474 }
475
476 char *
477 Calloc(s, n)
478 size_t s, n;
479 {
480 char *sptr;
481 ptr_t ptr;
482
483 n *= s;
484 n = n ? n : 1;
485 if ((ptr = malloc(n)) == (ptr_t) 0) {
486 child++;
487 stderror(ERR_NOMEM);
488 }
489
490 sptr = (char *) ptr;
491 if (n != 0)
492 do
493 *sptr++ = 0;
494 while (--n);
495
496 return ((char *) ptr);
497 }
498
499 void
500 Free(p)
501 ptr_t p;
502 {
503 if (p)
504 free(p);
505 }
506
507 #endif /* SYSMALLOC */
508
509 /*
510 * mstats - print out statistics about malloc
511 *
512 * Prints two lines of numbers, one showing the length of the free list
513 * for each size category, the second showing the number of mallocs -
514 * frees for each size category.
515 */
516 void
517 showall()
518 {
519 #ifndef SYSMALLOC
520 register int i, j;
521 register union overhead *p;
522 int totfree = 0, totused = 0;
523
524 xprintf("csh current memory allocation:\nfree:\t");
525 for (i = 0; i < NBUCKETS; i++) {
526 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++);
527 xprintf(" %4d", j);
528 totfree += j * (1 << (i + 3));
529 }
530 xprintf("\nused:\t");
531 for (i = 0; i < NBUCKETS; i++) {
532 xprintf(" %4d", nmalloc[i]);
533 totused += nmalloc[i] * (1 << (i + 3));
534 }
535 xprintf("\n\tTotal in use: %d, total free: %d\n",
536 totused, totfree);
537 xprintf("\tAllocated memory from 0x%lx to 0x%lx. Real top at 0x%lx\n",
538 membot, memtop, (char *) sbrk(0));
539 #else
540 xprintf("Allocated memory from 0x%lx to 0x%lx (%ld).\n",
541 membot, memtop = (char *) sbrk(0), memtop - membot);
542 #endif /* SYSMALLOC */
543 }
544