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