|
|
1.1 root 1: /* compile-time features
2: IALLOC use all blocks given to ialloc, otherwise ignore unordered blocks
3: MSTATS enable statistics
4: debug enable assertion checking
5: longdebug full arena checks at every transaction
6: */
7: #ifdef longdebug
8: #define debug 1
9: #endif
10: #ifdef debug
11: #include <stdio.h>
12: #define ASSERT(p) if(!(p))botch(__LINE__);else
13: static
14: botch(n)
15: {
16: fprintf(stderr,"bad arena, malloc.c line %d\n" ,n);
17: abort();
18: }
19: #else
20: #define ASSERT(p)
21: #endif
22:
23: /* C storage allocator
24: * circular first-fit strategy
25: * works with noncontiguous, but monotonically linked, arena
26: * each block is preceded by a ptr to the (pointer of)
27: * the next following block
28: * blocks are exact number of words long
29: * aligned to the data type requirements of ALIGN
30: * pointers to blocks must have BUSY bit 0
31: * bit in ptr is 1 for busy, 0 for idle
32: * gaps in arena are merely noted as busy blocks
33: * last block of arena is empty and
34: * has a pointer to first
35: * idle blocks are coalesced during space search
36: *
37: * a different implementation may need to redefine
38: * ALIGN, NALIGN, BLOCK, BUSY, INT
39: * where INT is integer type to which a pointer can be cast
40: */
41: #define INT int
42: #define ALIGN int
43: #define NALIGN 1
44: #define WORD sizeof(union store)
45: #define BLOCK 4096
46: #define BUSY 1
47: #define NULL 0
48: #define testbusy(p) ((INT)(p)&BUSY)
49: #define setbusy(p) (union store *)((INT)(p)|BUSY)
50: #define clearbusy(p) (union store *)((INT)(p)&~BUSY)
51:
52: union store {
53: union store *ptr;
54: ALIGN dummy[NALIGN];
55: int calloc; /*calloc clears an array of integers*/
56: };
57:
58: /* alloca should have type union store.
59: * The funny business gets it initialized without complaint*/
60: #define addr(a) (union store*)&a
61: static char *alloca = (char*)&alloca + BUSY; /* initial arena */
62: static union store *allocb = addr(alloca); /*arena base*/
63: static union store *allocc = addr(alloca); /*all prev blocks known busy*/
64: static union store *allocp = addr(alloca); /*search ptr*/
65: static union store *alloct = addr(alloca); /*top cell*/
66: static union store *allocx; /*for benefit of realloc*/
67: extern char *sbrk();
68:
69: /* a cache list of frequently-used sizes is maintained. From each
70: * cache entry hangs a chain of available blocks
71: * malloc(0) shuts off caching (to keep freed data clean)
72: */
73: #define CACHEMAX 50 /* largest block to be cached (in words) */
74: #define CACHESIZ 13 /* number of entries (prime) */
75:
76: static union store *cache[CACHESIZ];
77: static int cachemax = CACHEMAX;
78: static char *stdmalloc();
79:
80: #ifdef MSTATS
81: #define Mstats(e) e
82: static long nmalloc, nrealloc, nfree; /* call statistics */
83: static long walloc, wfree; /* space statistics */
84: static long chit, ccoll, cdrain, cavail; /* cache statistics */
85: #else
86: #define Mstats(e)
87: #endif
88:
89: char *
90: malloc(nbytes)
91: unsigned nbytes;
92: {
93: register union store *p;
94: register nw;
95: register union store **cp;
96:
97: nw = (nbytes+WORD+WORD-1)/WORD;
98: Mstats((nmalloc++, walloc += nw));
99: if(nw<cachemax) {
100: if(nw>=2) {
101: cp = &cache[nw%CACHESIZ];
102: p = *cp;
103: if(p && nw==clearbusy(p->ptr)-p) {
104: ASSERT(testbusy(p->ptr));
105: *cp = (++p)->ptr;
106: Mstats((chit++, cavail--));
107: return (char*)p;
108: }
109: } else {
110: draincache();
111: cachemax = 0;
112: }
113: }
114: return stdmalloc(nw);
115: }
116:
117: static char *
118: stdmalloc(nw)
119: register nw;
120: {
121: register union store *p, *q;
122: register temp;
123: ASSERT(allock(allocp));
124: for(; ; ) { /* done at most thrice */
125: p = allocp;
126: for(temp=0; ; ) {
127: if(!testbusy(p->ptr)) {
128: allocp = p;
129: while(!testbusy((q=p->ptr)->ptr)) {
130: ASSERT(q>p);
131: p->ptr = q->ptr;
132: }
133: if(q>=p+nw && p+nw>=p)
134: goto found;
135: }
136: q = p;
137: p = clearbusy(p->ptr);
138: if(p <= q) {
139: ASSERT(p==allocb&&q==alloct);
140: if(++temp>1)
141: break;
142: ASSERT(allock(allocc));
143: p = allocc;
144: }
145: }
146: p = (union store *)sbrk(0);
147: temp = ((nw+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD);
148: do {
149: if(p+temp > p) { /*check wrap*/
150: q = (union store *)sbrk(temp*WORD);
151: if((INT)q != -1)
152: goto mextend;
153: }
154: temp -= (temp-nw)/2;
155: } while(temp-nw > 1);
156: if(draincache())
157: continue;
158: return(NULL);
159: mextend:
160: draincache();
161: ialloc((char *)q, (unsigned)temp*WORD);
162: }
163: found:
164: allocp += nw;
165: if(q>allocp) {
166: allocx = allocp->ptr;
167: allocp->ptr = p->ptr;
168: }
169: p->ptr = setbusy(allocp);
170: if(p<=allocc) {
171: ASSERT(p==allocc);
172: while(testbusy(allocc->ptr)
173: && (q=clearbusy(allocc->ptr))>allocc)
174: allocc = q;
175: }
176: return((char *)(p+1));
177: }
178:
179: free(ap)
180: char *ap;
181: {
182: register union store *p = (union store *)ap, *q;
183: register nw;
184: register union store **cp;
185:
186: if(p==NULL)
187: return;
188: --p;
189: ASSERT(allock(p));
190: ASSERT(testbusy(p->ptr));
191: ASSERT(!cached(p));
192: nw = clearbusy(p->ptr) - p;
193: Mstats((nfree++, wfree += nw));
194: ASSERT(nw>0);
195: if(nw<cachemax && nw>=2) {
196: cp = &cache[nw%CACHESIZ];
197: q = *cp;
198: if(!q || nw==clearbusy(q->ptr)-q) {
199: p[1].ptr = q;
200: *cp = p;
201: Mstats(cavail++);
202: return;
203: } else Mstats(q && ccoll++);
204: }
205: stdfree(p+1);
206: }
207:
208: /* freeing strategy tuned for LIFO allocation
209: */
210: static
211: stdfree(p)
212: register union store *p;
213: {
214: allocp = --p;
215: if(p < allocc)
216: allocc = p;
217: ASSERT(allock(allocp));
218: p->ptr = clearbusy(p->ptr);
219: ASSERT(p->ptr > allocp);
220: }
221:
222: static
223: draincache()
224: {
225: register union store **cp = cache+CACHESIZ;
226: register union store *q;
227: int anyfreed = 0;
228: while(--cp>=cache) {
229: while(q= *cp) {
230: ASSERT(testbusy(q->ptr));
231: ASSERT((clearbusy(q->ptr)-q)%CACHESIZ==cp-cache);
232: ASSERT(q>=allocb&&q<=alloct);
233: stdfree(++q);
234: anyfreed++;
235: *cp = q->ptr;
236: }
237: }
238: Mstats((cdrain+=anyfreed, cavail=0));
239: return anyfreed;
240: }
241:
242: /* ialloc(q, nbytes) inserts a block that did not come
243: * from malloc into the arena
244: *
245: * q points to new block
246: * r points to last of new block
247: * p points to last cell of arena before new block
248: * s points to first cell of arena after new block
249: */
250:
251: ialloc(qq, nbytes)
252: char *qq;
253: unsigned nbytes;
254: {
255: register union store *p, *q, *s;
256: union store *r;
257:
258: q = (union store *)qq;
259: r = q + (nbytes/WORD) - 1;
260: q->ptr = r;
261: if(q > alloct) {
262: p = alloct;
263: s = allocb;
264: alloct = r;
265: } else {
266: #ifdef IALLOC /* useful only in small address spaces */
267: for(p=allocb; ; p=s) {
268: s = clearbusy(p->ptr);
269: if(s==allocb)
270: break;
271: ASSERT(s>p);
272: if(s>r) {
273: if(p<q)
274: break;
275: else
276: ASSERT(p>r);
277: }
278: }
279: if(allocb > q)
280: allocb = q;
281: if(allocc > q)
282: allocc = q;
283: allocp = allocc;
284: #else
285: return;
286: #endif
287: }
288: p->ptr = q==p+1? q: setbusy(q);
289: r->ptr = s==r+1? s: setbusy(s);
290: while(testbusy(allocc->ptr))
291: allocc = clearbusy(allocc->ptr);
292: }
293:
294: /* realloc(p, nbytes) reallocates a block obtained from malloc()
295: * and freed since last call of malloc()
296: * to have new size nbytes, and old content
297: * returns new location, or 0 on failure
298: */
299:
300: char *
301: realloc(pp, nbytes)
302: char *pp;
303: unsigned nbytes;
304: {
305: register union store *p = (union store *)pp;
306: register union store *s, *t;
307: register union store *q;
308: register unsigned nw;
309: unsigned onw;
310:
311: Mstats(nrealloc++);
312: if(p==NULL)
313: return malloc(nbytes);
314: ASSERT(allock(p-1));
315: if(testbusy(p[-1].ptr))
316: stdfree(p);
317: onw = p[-1].ptr - p;
318: nw = (nbytes+WORD-1)/WORD;
319: q = (union store *)stdmalloc(nw+1);
320: if(q==NULL || q==p)
321: return((char *)q);
322: ASSERT(q<p||q>p[-1].ptr);
323: if(nw<onw) {
324: Mstats(wfree += onw-nw);
325: onw = nw;
326: } else Mstats(walloc += nw-onw);
327: for(s=p, t=q; onw--!=0; )
328: *t++ = *s++;
329: ASSERT(clearbusy(q[-1].ptr)-q==nw);
330: if(q<p && q+nw>=p)
331: (q+(q+nw-p))->ptr = allocx;
332: ASSERT(allock(q-1));
333: return((char *)q);
334: }
335:
336: #ifdef debug
337: static
338: allock(q)
339: union store *q;
340: {
341: #ifdef longdebug
342: register union store *p, *r;
343: register union store **cp;
344: int x, y;
345: for(cp=cache+CACHESIZ; --cp>=cache; ) {
346: if((p= *cp)==0)
347: continue;
348: x = clearbusy(p->ptr) - p;
349: ASSERT(x%CACHESIZ==cp-cache);
350: for( ; p; p = p[1].ptr) {
351: ASSERT(testbusy(p->ptr));
352: ASSERT(clearbusy(p->ptr)-p==x);
353: }
354: }
355: x = 0, y = 0;
356: p = allocb;
357: for( ; (r=clearbusy(p->ptr)) > p; p=r) {
358: if(p==allocc)
359: y++;
360: ASSERT(y||testbusy(p->ptr));
361: if(p==q)
362: x++;
363: }
364: ASSERT(r==allocb);
365: ASSERT(x==1||p==q);
366: ASSERT(y||p==allocc);
367: return(1);
368: #else
369: ASSERT((unsigned)q/WORD*WORD==(unsigned)q);
370: ASSERT(q>=allocb&&q<=alloct);
371: #endif
372: }
373: #endif
374:
375: mstats()
376: {
377: #ifdef MSTATS
378: #ifndef stderr
379: #include <stdio.h>
380: #endif
381: fprintf(stderr, "Malloc statistics, including overhead bytes\n");
382: fprintf(stderr, "Arena: bottom %ld, top %ld\n",
383: (long)clearbusy(alloca), (long)alloct);
384: fprintf(stderr, "Calls: malloc %ld, realloc %ld, free %ld\n",
385: nmalloc, nrealloc, nfree);
386: fprintf(stderr, "Bytes: allocated or extended %ld, ",
387: walloc*WORD);
388: fprintf(stderr, "freed or cut %ld\n", wfree*WORD);
389: fprintf(stderr,"Cache: hits %ld, collisions %ld, discards %ld, avail %ld\n",
390: chit, ccoll, cdrain, cavail);
391: #endif
392: }
393:
394: #ifdef debug
395: cached(p)
396: union store *p;
397: {
398: union store *q = cache[(clearbusy(p->ptr)-p)%CACHESIZ];
399: for( ; q; q=q[1].ptr)
400: ASSERT(p!=q);
401: return 0;
402: }
403: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.