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