|
|
researchv10 Norman
#ifdef debug
#define ASSERT(p) if(!(p))botch("p");else
botch(s)
char *s;
{
printf("assertion botched: %s\n",s);
abort();
}
#else
#define ASSERT(p)
#endif
/* C storage allocator
* circular first-fit strategy
* works with noncontiguous, but monotonically linked, arena
* each block is preceded by a ptr to the (pointer of)
* the next following block
* blocks are exact number of words long
* aligned to the data type requirements of ALIGN
* pointers to blocks must have BUSY bit 0
* bit in ptr is 1 for busy, 0 for idle
* gaps in arena are merely noted as busy blocks
* last block of arena is empty and
* has a pointer to first
* idle blocks are coalesced during space search
*
* a different implementation may need to redefine
* ALIGN, NALIGN, BLOCK, BUSY, INT
* where INT is integer type to which a pointer can be cast
*/
#define INT int
#define ALIGN int
#define NALIGN 1
#define WORD sizeof(union store)
#define BLOCK 1024
#define BUSY 1
#define NULL 0
#define testbusy(p) ((INT)(p)&BUSY)
#define setbusy(p) (union store *)((INT)(p)|BUSY)
#define clearbusy(p) (union store *)((INT)(p)&~BUSY)
union store {
union store *ptr;
ALIGN dummy[NALIGN];
int calloc; /*calloc clears an array of integers*/
};
static union store alloca; /* initial arena */
static union store *allocb = &alloca; /*arena base*/
static union store *allocp = &alloca; /*search ptr*/
static union store *alloct = &alloca; /*arena top; for speedier ialloc*/
static union store allocx, allocy; /*for benefit of realloc*/
extern char *sbrk();
/* a cache list of frequently-used sizes is maintained. From each
* cache entry hangs a chain of available blocks (marked busy
* to keep out of the first fit search)
*/
#define CACHEMAX 100 /* largest block to be cached (in words) */
#ifndef pdp11
#define CACHESIZ 13 /* number of entries (prime) */
#else
#define CACHESIZ 0
#endif
static struct cache {
int size;
union store *chain;
} cache[CACHESIZ];
char *
malloc(nbytes)
unsigned nbytes;
{
register union store *p, *q;
register nw;
register temp;
register struct cache *cp;
ASSERT(allock(allocp)&1);
nw = (nbytes+WORD+WORD-1)/WORD;
if(CACHESIZ && nw<CACHEMAX && nw>2) { /* see note + below */
cp = cache + nw%CACHESIZ;
p = cp->chain;
if(p && nw==cp->size) {
cp->chain = p[1].ptr;
ASSERT(testbusy(p->ptr));
ASSERT(clearbusy(p->ptr)-p==nw);
return (char*)(p+1);
}
}
ASSERT(allock(allocp)&3);
for(; ; ) { /* done at most twice */
p = allocp;
if(alloca.ptr!=0) /*C can't initialize union*/
for(temp=0; ; ) {
if(!testbusy(p->ptr)) {
while(!testbusy((q=p->ptr)->ptr)) {
ASSERT(q>p);
p->ptr = q->ptr;
}
allocp = p;
if(q>=p+nw && p+nw>=p) /*room; no wrap*/
goto found;
}
q = p;
p = clearbusy(p->ptr);
if(p <= q) {
ASSERT(p==allocb);
if(++temp>1)
break;
}
}
temp = nw;
p = (union store *)sbrk(0);
q = allocp->ptr;
if(!testbusy(q) && q+1 == p)
temp -= p - allocp;
temp = ((temp+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD);
if(p+temp <= p)
return(NULL);
for( ; ; ) {
register s;
q = (union store *)sbrk(temp*WORD);
if((INT)q != -1)
break;
for(cp=cache; cp<cache+CACHESIZ; cp++) {
for(p=cp->chain; p; p=p[1].ptr) {
ASSERT(testbusy(p->ptr));
p->ptr = clearbusy(p->ptr);
ASSERT(p->ptr-p==cp->size);
}
cp->chain = 0;
}
s = (temp-nw)/2;
if(s <= 0)
return(NULL);
temp -= s;
}
ialloc((char *)q, (unsigned)temp*WORD);
}
found:
q = p + nw;
if(p->ptr > q) {
allocx = *q;
q->ptr = p->ptr;
}
p->ptr = setbusy(q);
ASSERT(allock(allocp)&7);
return((char *)(p+1));
}
/* + note CACHESIZ is tested for conditional compilation; nw>2 assumes there's
* no point in caching smaller sizes (they are rarely used and always
* can be allocated) and protects against the old storage compaction trick:
* free(p); free(dummy);dummy=malloc(1);realloc(p)
* (otherwise free(dummy) would destroy allocy, which pertains to p)
*/
/* freeing strategy tuned for LIFO allocation
*/
free(ap)
char *ap;
{
register union store *p = (union store *)ap, *q;
register nw;
register struct cache *cp;
--p;
ASSERT(allock(p));
ASSERT(testbusy(p->ptr));
nw = clearbusy(p->ptr) - p;
ASSERT(nw>0);
if(CACHESIZ && nw<CACHEMAX && nw>2) {
cp = cache + nw%CACHESIZ;
if(nw != cp->size) {
q = cp->chain;
if(q) {
ASSERT(testbusy(q->ptr));
q->ptr = clearbusy(q->ptr);
ASSERT(q->ptr-q==cp->size);
cp->chain = q[1].ptr;
}
if(!q)
cp->size = nw;
else
goto nocache;
}
allocy = p[1];
p[1].ptr = cp->chain;
cp->chain = p;
ASSERT(allock(allocp)&017);
return;
}
nocache:
allocp = p;
p->ptr = clearbusy(p->ptr);
ASSERT(allock(allocp)&037);
ASSERT(p->ptr<=alloct);
}
/* ialloc(q, nbytes) inserts a block that did not come
* from malloc into the arena
*
* q points to new block
* r points to last of new block
* p points to last cell of arena before new block
* s points to first cell of arena after new block
*/
ialloc(qq, nbytes)
char *qq;
unsigned nbytes;
{
register union store *p, *q, *s;
union store *r;
q = (union store *)qq;
r = q + (nbytes/WORD) - 1;
q->ptr = r;
if(alloca.ptr==0) /*C can't initialize union*/
alloca.ptr = &alloca;
if(q > alloct) {
p = alloct;
alloct = r;
} else
allocp = p = allocb;
for( ; ; p=s) {
s = clearbusy(p->ptr);
if(s==allocb)
break;
ASSERT(s>p);
if(s>r) {
if(p<q)
break;
else
ASSERT(p>r);
}
}
p->ptr = q==p+1? q: setbusy(q);
r->ptr = s==r+1? s: setbusy(s);
if(allocb > q)
allocp = allocb = q;
}
/* realloc(p, nbytes) reallocates a block obtained from malloc()
* and freed since last call of malloc()
* to have new size nbytes, and old content
* returns new location, or 0 on failure
*/
char *
realloc(pp, nbytes)
char *pp;
unsigned nbytes;
{
register union store *q;
register union store *p = (union store *)pp;
register union store *s, *t;
register unsigned nw;
struct cache *cp;
unsigned onw;
q = p-1;
ASSERT(allock(q));
if(testbusy(q->ptr)) {
allocp = q;
q->ptr = clearbusy(q->ptr);
if(CACHESIZ) {
nw = q->ptr - q;
cp = cache + nw%CACHESIZ;
if(cp->chain==q) {
ASSERT(cp->size==nw);
cp->chain = p->ptr; /*p->ptr==q[1].ptr*/
*p = allocy;
} else
ASSERT(notonchain(q,cp->chain));
}
}
onw = q->ptr - p;
q = (union store *)malloc(nbytes);
if(q==NULL || q==p)
return((char *)q);
ASSERT(q<p||q>p[-1].ptr);
s = p;
t = q;
nw = (nbytes+WORD-1)/WORD;
if(nw<onw)
onw = nw;
while(onw--!=0)
*t++ = *s++;
ASSERT(clearbusy(q[-1].ptr)-q==nw);
if(q<p && q+nw>=p)
q[q+nw-p] = allocx;
ASSERT(allock(q-1));
return((char *)q);
}
#ifdef debug
notonchain(p,q)
union store *p, *q;
{
for(;q; q=clearbusy(q->ptr))
if(q==p)
return 0;
return 1;
}
allock(q)
union store *q;
{
#ifdef longdebug
register union store *p, *r;
int x;
for(x=0; x<CACHESIZ; x++) {
p = cache[x].chain;
if(p==0)
continue;
for( ; p; p=p[1].ptr) {
ASSERT(testbusy(p->ptr));
ASSERT(clearbusy(p->ptr)-p==cache[x].size);
}
}
x = 0;
p = allocb;
if(alloca.ptr==0)
return(1);
for( ; (r=clearbusy(p->ptr)) > p; p=r) {
if(p==q)
x++;
}
return(r==allocb&(x==1|p==q));
#else
return(q>=allocb);
#endif
}
#endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.