ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/malloc.c
Revision: 2.10
Committed: Fri Nov 10 17:04:15 1995 UTC (28 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.9: +0 -1 lines
Log Message:
removed memcpy() declaration

File Contents

# Content
1 /* Copyright (c) 1992 Regents of the University of California */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ LBL";
5 #endif
6
7 /*
8 * Fast malloc for memory hogs and VM environments.
9 * Performs a minimum of searching through free lists.
10 * malloc(), realloc() and free() use buckets
11 * containing blocks in powers of two, similar to CIT routines.
12 * bfree() puts memory from any source into malloc free lists.
13 * bmalloc() doesn't keep track of free lists -- it's usually
14 * just a buffered call to sbrk(2). However, bmalloc will
15 * call mscrounge() if sbrk fails.
16 * mscrounge() returns whatever free memory it can find to satisfy the
17 * request along with the number of bytes (modified).
18 * mcompact() takes the same arguments as mscrounge() (and is in fact
19 * called by mscrounge() under dire circumstances), but it uses
20 * memory compaction to return a larger block. (MCOMP)
21 *
22 * Greg Ward Lawrence Berkeley Laboratory
23 */
24
25 #include <errno.h>
26
27 #ifndef BSD
28 #define bcopy(s,d,n) (void)memcpy(d,s,n)
29 #define bzero(d,n) (void)memset(d,0,n)
30 #endif
31
32 #ifdef MSTATS
33 #include <stdio.h>
34 static unsigned b_nsbrked = 0;
35 static unsigned b_nalloced = 0;
36 static unsigned b_nfreed = 0;
37 static unsigned b_nscrounged = 0;
38 static unsigned b_ncompacted = 0;
39 static unsigned m_nalloced = 0;
40 static unsigned m_nfreed = 0;
41 static unsigned m_nwasted = 0;
42 #else
43 #define NULL 0
44 #endif
45
46 #ifndef ALIGN
47 #define ALIGN int /* align type */
48 #endif
49 #define BYTES_WORD sizeof(ALIGN)
50
51 #ifndef MAXINCR
52 #define MAXINCR (1<<16) /* largest sbrk(2) increment */
53 #endif
54 /* malloc free lists */
55 typedef union m_head {
56 union m_head *next;
57 struct {
58 short magic;
59 short bucket;
60 } a;
61 ALIGN dummy;
62 } M_HEAD;
63
64 #define MAGIC 0x1a2 /* magic number for allocated memory */
65
66 #define FIRSTBUCKET 3
67 #define NBUCKETS 30
68
69 static M_HEAD *free_list[NBUCKETS];
70
71 static ALIGN dummy_mem;
72
73 static char *memlim[2];
74
75 #define DUMMYLOC ((char *)&dummy_mem)
76
77 #define BADPTR(p) ((p) < memlim[0] | (p) >= memlim[1])
78
79 #ifdef MCOMP /* memory compaction routines */
80 static char seedtab[1024]; /* seed for compaction table */
81
82 static struct mblk {
83 char *ptr; /* pointer to memory block */
84 unsigned siz; /* size of memory block */
85 } cptab = {seedtab, sizeof(seedtab)};
86
87 #define mtab(mb) ((struct mblk *)(mb)->ptr)
88 #define mtablen(mb) ((mb)->siz/sizeof(struct mblk))
89
90
91 static int
92 mbcmp(m1, m2) /* compare memory block locations */
93 register struct mblk *m1, *m2;
94 {
95 /* put empty blocks at end */
96 if (m1->siz == 0)
97 return(m2->siz == 0 ? 0 : 1);
98 if (m2->siz == 0)
99 return(-1);
100 /* otherwise, ascending order */
101 return(m1->ptr - m2->ptr);
102 }
103
104
105 int
106 compactfree() /* compact free lists (moving to table) */
107 {
108 register struct mblk *tp;
109 register int bucket;
110 unsigned n, bsiz, ncomp;
111 /* fill table from free lists */
112 tp = mtab(&cptab); n = mtablen(&cptab);
113 bucket = NBUCKETS-1; bsiz = 1<<(NBUCKETS-1);
114 ncomp = 0;
115 for ( ; ; ) {
116 while (tp->siz) { /* find empty slot */
117 if (--n == 0)
118 goto loopexit;
119 tp++;
120 }
121 while (free_list[bucket] == NULL) { /* find block */
122 if (--bucket < FIRSTBUCKET)
123 goto loopexit;
124 bsiz >>= 1;
125 }
126 tp->ptr = (char *)free_list[bucket];
127 ncomp += (tp->siz = bsiz + sizeof(M_HEAD));
128 free_list[bucket] = free_list[bucket]->next;
129 }
130 loopexit:
131 if (ncomp == 0)
132 return(0); /* nothing new to compact */
133 #ifdef MSTATS
134 b_ncompacted += ncomp;
135 #endif
136 tp = mtab(&cptab); n = mtablen(&cptab); /* sort table */
137 qsort((char *)tp, n, sizeof(struct mblk), mbcmp);
138 ncomp = 0; /* collect blocks */
139 while (--n && tp[1].siz) {
140 if (tp[0].ptr + tp[0].siz == tp[1].ptr) {
141 tp[1].ptr = tp[0].ptr;
142 tp[1].siz += tp[0].siz;
143 ncomp += tp[0].siz;
144 tp[0].siz = 0;
145 }
146 tp++;
147 }
148 return(ncomp);
149 }
150
151
152 char *
153 mcompact(np) /* compact free lists to satisfy request */
154 unsigned *np;
155 {
156 struct mblk *tab;
157 unsigned tablen;
158 register struct mblk *bp, *big;
159
160 for ( ; ; ) {
161 /* compact free lists */
162 while (compactfree())
163 ;
164 /* find largest block */
165 tab = mtab(&cptab); tablen = mtablen(&cptab);
166 big = tab;
167 bp = tab + tablen;
168 while (--bp > tab)
169 if (bp->siz > big->siz)
170 big = bp;
171 if (big->siz >= *np) { /* big enough? */
172 *np = big->siz;
173 big->siz = 0; /* remove from table */
174 return(big->ptr); /* return it */
175 }
176 if (mtablen(big) <= tablen) {
177 *np = 0; /* cannot grow table */
178 return(NULL); /* report failure */
179 }
180 /* enlarge table, free old one */
181 mtab(big)[0].ptr = cptab.ptr;
182 mtab(big)[0].siz = cptab.siz;
183 cptab.ptr = big->ptr;
184 cptab.siz = big->siz;
185 big->siz = 0; /* clear and copy */
186 bcopy((char *)tab, (char *)(mtab(&cptab)+1),
187 tablen*sizeof(struct mblk));
188 bzero((char *)(mtab(&cptab)+tablen+1),
189 (mtablen(&cptab)-tablen-1)*sizeof(struct mblk));
190 } /* next round */
191 }
192 #endif /* MCOMP */
193
194
195 char *
196 mscrounge(np) /* search free lists to satisfy request */
197 register unsigned *np;
198 {
199 char *p;
200 register int bucket;
201 register unsigned bsiz;
202
203 for (bucket = FIRSTBUCKET, bsiz = 1<<FIRSTBUCKET;
204 bucket < NBUCKETS; bucket++, bsiz <<= 1)
205 if (free_list[bucket] != NULL && bsiz+sizeof(M_HEAD) >= *np) {
206 *np = bsiz+sizeof(M_HEAD);
207 p = (char *)free_list[bucket];
208 free_list[bucket] = free_list[bucket]->next;
209 #ifdef MSTATS
210 b_nscrounged += *np;
211 #endif
212 return(p);
213 }
214 #ifdef MCOMP
215 return(mcompact(np)); /* try compaction */
216 #else
217 *np = 0;
218 return(NULL);
219 #endif
220 }
221
222
223 char *
224 bmalloc(n) /* allocate a block of n bytes, sans header */
225 register unsigned n;
226 {
227 extern char *sbrk();
228 static unsigned pagesz = 0;
229 static unsigned amnt;
230 static char *bpos;
231 static unsigned nrem;
232 unsigned thisamnt;
233 register char *p;
234
235 if (pagesz == 0) { /* initialize */
236 pagesz = amnt = getpagesize();
237 nrem = (int)sbrk(0); /* page align break */
238 nrem = pagesz - (nrem&(pagesz-1));
239 bpos = sbrk(nrem);
240 if ((int)bpos == -1)
241 return(NULL);
242 #ifdef MSTATS
243 b_nsbrked += nrem;
244 #endif
245 bpos += nrem & (BYTES_WORD-1); /* align pointer */
246 nrem &= ~(BYTES_WORD-1);
247 memlim[0] = bpos;
248 memlim[1] = bpos + nrem;
249 }
250
251 n = (n+(BYTES_WORD-1))&~(BYTES_WORD-1); /* word align rqst. */
252
253 if (n > nrem) { /* need more core */
254 tryagain:
255 if (n > amnt) { /* big chunk */
256 thisamnt = (n+(pagesz-1))&~(pagesz-1);
257 if (thisamnt <= MAXINCR) /* increase amnt */
258 amnt = thisamnt;
259 } else
260 thisamnt = amnt;
261 p = sbrk(thisamnt);
262 if ((int)p == -1) { /* uh-oh, ENOMEM */
263 errno = 0; /* call cavalry */
264 if (thisamnt >= n+pagesz) {
265 amnt = pagesz; /* minimize request */
266 goto tryagain;
267 }
268 thisamnt = n;
269 p = mscrounge(&thisamnt); /* search free lists */
270 if (p == NULL) { /* we're really out */
271 errno = ENOMEM;
272 return(NULL);
273 }
274 }
275 #ifdef MSTATS
276 else b_nsbrked += thisamnt;
277 #endif
278 if (p != bpos+nrem) { /* not an increment */
279 bfree(bpos, nrem); /* free what we have */
280 bpos = p;
281 nrem = thisamnt;
282 } else /* otherwise tack on */
283 nrem += thisamnt;
284 if (bpos < memlim[0])
285 memlim[0] = bpos;
286 if (bpos + nrem > memlim[1])
287 memlim[1] = bpos + nrem;
288 }
289 p = bpos;
290 bpos += n; /* advance */
291 nrem -= n;
292 #ifdef MSTATS
293 b_nalloced += n;
294 #endif
295 return(p);
296 }
297
298
299 bfree(p, n) /* free n bytes of random memory */
300 register char *p;
301 register unsigned n;
302 {
303 register int bucket;
304 register unsigned bsiz;
305
306 if (n < 1<<FIRSTBUCKET)
307 return;
308 #ifdef MSTATS
309 b_nfreed += n;
310 #endif
311 /* align pointer */
312 bsiz = BYTES_WORD - ((unsigned)p&(BYTES_WORD-1));
313 if (bsiz < BYTES_WORD) {
314 p += bsiz;
315 n -= bsiz;
316 }
317 if (p < memlim[0])
318 memlim[0] = p;
319 if (p + n > memlim[1])
320 memlim[1] = p + n;
321 /* fill big buckets first */
322 for (bucket = NBUCKETS-1, bsiz = 1<<(NBUCKETS-1);
323 bucket >= FIRSTBUCKET; bucket--, bsiz >>= 1)
324 if (n >= bsiz+sizeof(M_HEAD)) {
325 ((M_HEAD *)p)->next = free_list[bucket];
326 free_list[bucket] = (M_HEAD *)p;
327 p += bsiz+sizeof(M_HEAD);
328 n -= bsiz+sizeof(M_HEAD);
329 }
330 }
331
332
333 char *
334 malloc(n) /* allocate n bytes of memory */
335 unsigned n;
336 {
337 register M_HEAD *mp;
338 register int bucket;
339 register unsigned bsiz;
340 /* don't return NULL on 0 request */
341 if (n == 0)
342 return(DUMMYLOC);
343 /* find first bucket that fits */
344 for (bucket = FIRSTBUCKET, bsiz = 1<<FIRSTBUCKET;
345 bucket < NBUCKETS; bucket++, bsiz <<= 1)
346 if (bsiz >= n)
347 break;
348 if (bucket >= NBUCKETS) {
349 errno = EINVAL;
350 return(NULL);
351 }
352 if (free_list[bucket] == NULL) { /* need more core */
353 mp = (M_HEAD *)bmalloc(bsiz+sizeof(M_HEAD));
354 if (mp == NULL)
355 return(NULL);
356 } else { /* got it */
357 mp = free_list[bucket];
358 free_list[bucket] = mp->next;
359 }
360 #ifdef MSTATS
361 m_nalloced += bsiz + sizeof(M_HEAD);
362 m_nwasted += bsiz + sizeof(M_HEAD) - n;
363 #endif
364 mp->a.magic = MAGIC; /* tag block */
365 mp->a.bucket = bucket;
366 return((char *)(mp+1));
367 }
368
369
370 char *
371 realloc(op, n) /* reallocate memory using malloc() */
372 register char *op;
373 unsigned n;
374 {
375 char *p;
376 register unsigned on;
377 /* get old size */
378 if (op != DUMMYLOC && !BADPTR(op) &&
379 ((M_HEAD *)op-1)->a.magic == MAGIC)
380 on = 1 << ((M_HEAD *)op-1)->a.bucket;
381 else
382 on = 0;
383 if (n <= on && (n > on>>1 || on == 1<<FIRSTBUCKET))
384 return(op); /* same bucket */
385 if ((p = malloc(n)) == NULL)
386 return(n<=on ? op : NULL);
387 if (on) {
388 bcopy(op, p, n>on ? on : n);
389 free(op);
390 }
391 return(p);
392 }
393
394
395 free(p) /* free memory allocated by malloc */
396 char *p;
397 {
398 register M_HEAD *mp;
399 register int bucket;
400
401 if (p == DUMMYLOC)
402 return(1);
403 if (BADPTR(p))
404 goto invalid;
405 mp = (M_HEAD *)p - 1;
406 if (mp->a.magic != MAGIC) /* sanity check */
407 goto invalid;
408 bucket = mp->a.bucket;
409 if (bucket < FIRSTBUCKET | bucket >= NBUCKETS)
410 goto invalid;
411 mp->next = free_list[bucket];
412 free_list[bucket] = mp;
413 #ifdef MSTATS
414 m_nfreed += (1 << bucket) + sizeof(M_HEAD);
415 #endif
416 return(1);
417 invalid:
418 errno = EINVAL;
419 return(0);
420 }
421
422
423 #ifdef MSTATS
424 printmemstats(fp) /* print memory statistics to stream */
425 FILE *fp;
426 {
427 register int i;
428 int n;
429 register M_HEAD *mp;
430 unsigned int total = 0;
431
432 fprintf(fp, "Memory statistics:\n");
433 fprintf(fp, "\tbmalloc: %d bytes from sbrk\n", b_nsbrked);
434 fprintf(fp, "\tbmalloc: %d bytes allocated\n", b_nalloced);
435 fprintf(fp, "\tbmalloc: %d bytes freed\n", b_nfreed);
436 fprintf(fp, "\tbmalloc: %d bytes scrounged\n", b_nscrounged);
437 fprintf(fp, "\tbmalloc: %d bytes compacted\n", b_ncompacted);
438 fprintf(fp, "\tmalloc: %d bytes allocated\n", m_nalloced);
439 fprintf(fp, "\tmalloc: %d bytes wasted (%.1f%%)\n", m_nwasted,
440 100.0*m_nwasted/m_nalloced);
441 fprintf(fp, "\tmalloc: %d bytes freed\n", m_nfreed);
442 for (i = NBUCKETS-1; i >= FIRSTBUCKET; i--) {
443 n = 0;
444 for (mp = free_list[i]; mp != NULL; mp = mp->next)
445 n++;
446 if (n) {
447 fprintf(fp, "\t%d * %u\n", n, 1<<i);
448 total += n * ((1<<i) + sizeof(M_HEAD));
449 }
450 }
451 fprintf(fp, "\t %u total bytes in free list\n", total);
452 }
453 #endif