|
|
1.1 ! root 1: #ifdef debug ! 2: #define ASSERT(p) if(!(p))botch("p");else ! 3: botch(s) ! 4: char *s; ! 5: { ! 6: printf("assertion botched: %s\n",s); ! 7: abort(); ! 8: } ! 9: #else ! 10: #define ASSERT(p) ! 11: #endif ! 12: ! 13: /* C storage allocator ! 14: * circular first-fit strategy ! 15: * works with noncontiguous, but monotonically linked, arena ! 16: * each block is preceded by a ptr to the (pointer of) ! 17: * the next following block ! 18: * blocks are exact number of words long ! 19: * aligned to the data type requirements of ALIGN ! 20: * pointers to blocks must have BUSY bit 0 ! 21: * bit in ptr is 1 for busy, 0 for idle ! 22: * gaps in arena are merely noted as busy blocks ! 23: * last block of arena is empty and ! 24: * has a pointer to first ! 25: * idle blocks are coalesced during space search ! 26: * ! 27: * a different implementation may need to redefine ! 28: * ALIGN, NALIGN, BLOCK, BUSY, INT ! 29: * where INT is integer type to which a pointer can be cast ! 30: */ ! 31: #define INT int ! 32: #define ALIGN int ! 33: #define NALIGN 1 ! 34: #define WORD sizeof(union store) ! 35: #define BLOCK 1024 ! 36: #define BUSY 1 ! 37: #define NULL 0 ! 38: #define testbusy(p) ((INT)(p)&BUSY) ! 39: #define setbusy(p) (union store *)((INT)(p)|BUSY) ! 40: #define clearbusy(p) (union store *)((INT)(p)&~BUSY) ! 41: ! 42: union store { ! 43: union store *ptr; ! 44: ALIGN dummy[NALIGN]; ! 45: int calloc; /*calloc clears an array of integers*/ ! 46: }; ! 47: ! 48: static union store alloca; /* initial arena */ ! 49: static union store *allocb = &alloca; /*arena base*/ ! 50: static union store *allocp = &alloca; /*search ptr*/ ! 51: static union store *alloct = &alloca; /*arena top; for speedier ialloc*/ ! 52: static union store allocx, allocy; /*for benefit of realloc*/ ! 53: extern char *sbrk(); ! 54: ! 55: /* a cache list of frequently-used sizes is maintained. From each ! 56: * cache entry hangs a chain of available blocks (marked busy ! 57: * to keep out of the first fit search) ! 58: */ ! 59: #define CACHEMAX 100 /* largest block to be cached (in words) */ ! 60: #ifndef pdp11 ! 61: #define CACHESIZ 13 /* number of entries (prime) */ ! 62: #else ! 63: #define CACHESIZ 0 ! 64: #endif ! 65: static struct cache { ! 66: int size; ! 67: union store *chain; ! 68: } cache[CACHESIZ]; ! 69: ! 70: char * ! 71: malloc(nbytes) ! 72: unsigned nbytes; ! 73: { ! 74: register union store *p, *q; ! 75: register nw; ! 76: register temp; ! 77: register struct cache *cp; ! 78: ! 79: ASSERT(allock(allocp)&1); ! 80: nw = (nbytes+WORD+WORD-1)/WORD; ! 81: if(CACHESIZ && nw<CACHEMAX && nw>2) { /* see note + below */ ! 82: cp = cache + nw%CACHESIZ; ! 83: p = cp->chain; ! 84: if(p && nw==cp->size) { ! 85: cp->chain = p[1].ptr; ! 86: ASSERT(testbusy(p->ptr)); ! 87: ASSERT(clearbusy(p->ptr)-p==nw); ! 88: return (char*)(p+1); ! 89: } ! 90: } ! 91: ASSERT(allock(allocp)&3); ! 92: for(; ; ) { /* done at most twice */ ! 93: p = allocp; ! 94: if(alloca.ptr!=0) /*C can't initialize union*/ ! 95: for(temp=0; ; ) { ! 96: if(!testbusy(p->ptr)) { ! 97: while(!testbusy((q=p->ptr)->ptr)) { ! 98: ASSERT(q>p); ! 99: p->ptr = q->ptr; ! 100: } ! 101: allocp = p; ! 102: if(q>=p+nw && p+nw>=p) /*room; no wrap*/ ! 103: goto found; ! 104: } ! 105: q = p; ! 106: p = clearbusy(p->ptr); ! 107: if(p <= q) { ! 108: ASSERT(p==allocb); ! 109: if(++temp>1) ! 110: break; ! 111: } ! 112: } ! 113: temp = nw; ! 114: p = (union store *)sbrk(0); ! 115: q = allocp->ptr; ! 116: if(!testbusy(q) && q+1 == p) ! 117: temp -= p - allocp; ! 118: temp = ((temp+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD); ! 119: if(p+temp <= p) ! 120: return(NULL); ! 121: for( ; ; ) { ! 122: register s; ! 123: q = (union store *)sbrk(temp*WORD); ! 124: if((INT)q != -1) ! 125: break; ! 126: for(cp=cache; cp<cache+CACHESIZ; cp++) { ! 127: for(p=cp->chain; p; p=p[1].ptr) { ! 128: ASSERT(testbusy(p->ptr)); ! 129: p->ptr = clearbusy(p->ptr); ! 130: ASSERT(p->ptr-p==cp->size); ! 131: } ! 132: cp->chain = 0; ! 133: } ! 134: s = (temp-nw)/2; ! 135: if(s <= 0) ! 136: return(NULL); ! 137: temp -= s; ! 138: } ! 139: ialloc((char *)q, (unsigned)temp*WORD); ! 140: } ! 141: found: ! 142: q = p + nw; ! 143: if(p->ptr > q) { ! 144: allocx = *q; ! 145: q->ptr = p->ptr; ! 146: } ! 147: p->ptr = setbusy(q); ! 148: ASSERT(allock(allocp)&7); ! 149: return((char *)(p+1)); ! 150: } ! 151: /* + note CACHESIZ is tested for conditional compilation; nw>2 assumes there's ! 152: * no point in caching smaller sizes (they are rarely used and always ! 153: * can be allocated) and protects against the old storage compaction trick: ! 154: * free(p); free(dummy);dummy=malloc(1);realloc(p) ! 155: * (otherwise free(dummy) would destroy allocy, which pertains to p) ! 156: */ ! 157: ! 158: /* freeing strategy tuned for LIFO allocation ! 159: */ ! 160: free(ap) ! 161: char *ap; ! 162: { ! 163: register union store *p = (union store *)ap, *q; ! 164: register nw; ! 165: register struct cache *cp; ! 166: ! 167: --p; ! 168: ASSERT(allock(p)); ! 169: ASSERT(testbusy(p->ptr)); ! 170: nw = clearbusy(p->ptr) - p; ! 171: ASSERT(nw>0); ! 172: if(CACHESIZ && nw<CACHEMAX && nw>2) { ! 173: cp = cache + nw%CACHESIZ; ! 174: if(nw != cp->size) { ! 175: q = cp->chain; ! 176: if(q) { ! 177: ASSERT(testbusy(q->ptr)); ! 178: q->ptr = clearbusy(q->ptr); ! 179: ASSERT(q->ptr-q==cp->size); ! 180: cp->chain = q[1].ptr; ! 181: } ! 182: if(!q) ! 183: cp->size = nw; ! 184: else ! 185: goto nocache; ! 186: } ! 187: allocy = p[1]; ! 188: p[1].ptr = cp->chain; ! 189: cp->chain = p; ! 190: ASSERT(allock(allocp)&017); ! 191: return; ! 192: } ! 193: nocache: ! 194: allocp = p; ! 195: p->ptr = clearbusy(p->ptr); ! 196: ASSERT(allock(allocp)&037); ! 197: ASSERT(p->ptr<=alloct); ! 198: } ! 199: ! 200: /* ialloc(q, nbytes) inserts a block that did not come ! 201: * from malloc into the arena ! 202: * ! 203: * q points to new block ! 204: * r points to last of new block ! 205: * p points to last cell of arena before new block ! 206: * s points to first cell of arena after new block ! 207: */ ! 208: ialloc(qq, nbytes) ! 209: char *qq; ! 210: unsigned nbytes; ! 211: { ! 212: register union store *p, *q, *s; ! 213: union store *r; ! 214: ! 215: q = (union store *)qq; ! 216: r = q + (nbytes/WORD) - 1; ! 217: q->ptr = r; ! 218: if(alloca.ptr==0) /*C can't initialize union*/ ! 219: alloca.ptr = &alloca; ! 220: if(q > alloct) { ! 221: p = alloct; ! 222: alloct = r; ! 223: } else ! 224: allocp = p = allocb; ! 225: for( ; ; p=s) { ! 226: s = clearbusy(p->ptr); ! 227: if(s==allocb) ! 228: break; ! 229: ASSERT(s>p); ! 230: if(s>r) { ! 231: if(p<q) ! 232: break; ! 233: else ! 234: ASSERT(p>r); ! 235: } ! 236: } ! 237: p->ptr = q==p+1? q: setbusy(q); ! 238: r->ptr = s==r+1? s: setbusy(s); ! 239: if(allocb > q) ! 240: allocp = allocb = q; ! 241: } ! 242: ! 243: /* realloc(p, nbytes) reallocates a block obtained from malloc() ! 244: * and freed since last call of malloc() ! 245: * to have new size nbytes, and old content ! 246: * returns new location, or 0 on failure ! 247: */ ! 248: ! 249: char * ! 250: realloc(pp, nbytes) ! 251: char *pp; ! 252: unsigned nbytes; ! 253: { ! 254: register union store *q; ! 255: register union store *p = (union store *)pp; ! 256: register union store *s, *t; ! 257: register unsigned nw; ! 258: struct cache *cp; ! 259: unsigned onw; ! 260: ! 261: q = p-1; ! 262: ASSERT(allock(q)); ! 263: if(testbusy(q->ptr)) { ! 264: allocp = q; ! 265: q->ptr = clearbusy(q->ptr); ! 266: if(CACHESIZ) { ! 267: nw = q->ptr - q; ! 268: cp = cache + nw%CACHESIZ; ! 269: if(cp->chain==q) { ! 270: ASSERT(cp->size==nw); ! 271: cp->chain = p->ptr; /*p->ptr==q[1].ptr*/ ! 272: *p = allocy; ! 273: } else ! 274: ASSERT(notonchain(q,cp->chain)); ! 275: } ! 276: } ! 277: onw = q->ptr - p; ! 278: q = (union store *)malloc(nbytes); ! 279: if(q==NULL || q==p) ! 280: return((char *)q); ! 281: ASSERT(q<p||q>p[-1].ptr); ! 282: s = p; ! 283: t = q; ! 284: nw = (nbytes+WORD-1)/WORD; ! 285: if(nw<onw) ! 286: onw = nw; ! 287: while(onw--!=0) ! 288: *t++ = *s++; ! 289: ASSERT(clearbusy(q[-1].ptr)-q==nw); ! 290: if(q<p && q+nw>=p) ! 291: q[q+nw-p] = allocx; ! 292: ASSERT(allock(q-1)); ! 293: return((char *)q); ! 294: } ! 295: ! 296: #ifdef debug ! 297: ! 298: notonchain(p,q) ! 299: union store *p, *q; ! 300: { ! 301: for(;q; q=clearbusy(q->ptr)) ! 302: if(q==p) ! 303: return 0; ! 304: return 1; ! 305: } ! 306: ! 307: allock(q) ! 308: union store *q; ! 309: { ! 310: #ifdef longdebug ! 311: register union store *p, *r; ! 312: int x; ! 313: for(x=0; x<CACHESIZ; x++) { ! 314: p = cache[x].chain; ! 315: if(p==0) ! 316: continue; ! 317: for( ; p; p=p[1].ptr) { ! 318: ASSERT(testbusy(p->ptr)); ! 319: ASSERT(clearbusy(p->ptr)-p==cache[x].size); ! 320: } ! 321: } ! 322: x = 0; ! 323: p = allocb; ! 324: if(alloca.ptr==0) ! 325: return(1); ! 326: for( ; (r=clearbusy(p->ptr)) > p; p=r) { ! 327: if(p==q) ! 328: x++; ! 329: } ! 330: return(r==allocb&(x==1|p==q)); ! 331: #else ! 332: return(q>=allocb); ! 333: #endif ! 334: } ! 335: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.