1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  23 /*        All Rights Reserved   */
  24 
  25 
  26 #ident  "%Z%%M% %I%     %E% SMI"        /* SVr4.0 1.3   */
  27 #ifdef debug
  28 #define ASSERT(p) if(!(p))botch("p");else
  29 botch(s)
  30 char *s;
  31 {
  32         printf("assertion botched: %s\n",s);
  33         abort();
  34 }
  35 #else
  36 #define ASSERT(p)
  37 #endif
  38 
  39 /*      C storage allocator
  40  *      circular first-fit strategy
  41  *      works with noncontiguous, but monotonically linked, arena
  42  *      each block is preceded by a ptr to the (pointer of) 
  43  *      the next following block
  44  *      blocks are exact number of words long 
  45  *      aligned to the data type requirements of ALIGN
  46  *      pointers to blocks must have BUSY bit 0
  47  *      bit in ptr is 1 for busy, 0 for idle
  48  *      gaps in arena are merely noted as busy blocks
  49  *      last block of arena is empty and
  50  *      has a pointer to first
  51  *      idle blocks are coalesced during space search
  52  *
  53  *      a different implementation may need to redefine
  54  *      ALIGN, NALIGN, BLOCK, BUSY, INT
  55  *      where INT is integer type to which a pointer can be cast
  56 */
  57 #define INT int
  58 #define ALIGN int
  59 #define NALIGN 1
  60 #define WORD sizeof(union store)
  61 #define BLOCK 1024
  62 #define BUSY 1
  63 #define NULL 0
  64 #define testbusy(p) ((INT)(p)&BUSY)
  65 #define setbusy(p) (union store *)((INT)(p)|BUSY)
  66 #define clearbusy(p) (union store *)((INT)(p)&~BUSY)
  67 
  68 union store {
  69               union store *ptr;
  70               ALIGN dummy[NALIGN];
  71               int calloc;       /*calloc clears an array of integers*/
  72 };
  73 
  74 static  union store alloca;     /* initial arena */
  75 static  union store *allocb = &alloca;      /*arena base*/
  76 static  union store *allocp = &alloca;      /*search ptr*/
  77 static  union store *allocx;    /*for benefit of realloc*/
  78 extern  char *sbrk();
  79 
  80 char *
  81 malloc(nbytes)
  82 unsigned nbytes;
  83 {
  84         register union store *p, *q;
  85         register nw;
  86         register temp;
  87         register union store *r = 0;
  88 
  89         nw = (nbytes+WORD+WORD-1)/WORD + 1;     /*need one more than asked for*/
  90         ASSERT(allock(allocp));
  91         for(; ; ) {     /* done at most twice */
  92                 p = allocp;
  93                 if(alloca.ptr!=0)               /*C can't initialize union*/
  94                 for(temp=0; ; ) {
  95                         if(!testbusy(p->ptr)) {
  96                                 while(!testbusy((q=p->ptr)->ptr)) {
  97                                         ASSERT(q>p);
  98                                         p->ptr = q->ptr;
  99                                         allocp = p;
 100                                 }
 101                                 if(q>=p+nw && p+nw>=p)
 102                                         goto found;
 103                                 r = p;
 104                         }
 105                         q = p;
 106                         p = clearbusy(p->ptr);
 107                         if(p <= q) {
 108                                 ASSERT(p==allocb);
 109                                 if(p != allocb)
 110                                         return(NULL);
 111                                 if(++temp>1)
 112                                         break;
 113                         }
 114                 }
 115                 temp = nw;
 116                 p = (union store *)sbrk(0);
 117                 if (r && !testbusy(r->ptr) && r->ptr + 1 == p)
 118                         temp -= p - r - 1;
 119                 temp = ((temp+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD);
 120                 if(p+temp <= p)
 121                         return(NULL);
 122                 for(; ; ) {
 123                         q = (union store *)sbrk(temp*WORD);
 124                         if((INT)q != -1)
 125                                 break;
 126                         temp -= (temp-nw+1)/2;
 127                         if(temp <= nw)
 128                                 return(NULL);
 129                 }
 130                 ialloc((char *)q, (unsigned)temp*WORD);
 131         }
 132 found:
 133         allocp = p + nw;
 134         if(q>allocp) {
 135                 allocx = allocp->ptr;
 136                 allocp->ptr = p->ptr;
 137         }
 138         p->ptr = setbusy(allocp);
 139         return((char *)(p+1));
 140 }
 141 
 142 /*      freeing strategy tuned for LIFO allocation
 143 */
 144 free(ap)
 145 char *ap;
 146 {
 147         register union store *p = (union store *)ap;
 148 
 149         allocp = --p;
 150         ASSERT(allock(allocp));
 151         ASSERT(testbusy(p->ptr));
 152         p->ptr = clearbusy(p->ptr);
 153         ASSERT(p->ptr > allocp);
 154 }
 155 
 156 /* ialloc(q, nbytes) inserts a block that did not come
 157  * from malloc into the arena
 158  *
 159  * q points to new block
 160  * r points to last of new block
 161  * p points to last cell of arena before new block
 162  * s points to first cell of arena after new block
 163 */
 164 ialloc(qq, nbytes)
 165 char *qq;
 166 unsigned nbytes;
 167 {
 168         register union store *p, *q, *s;
 169         union store *r;
 170 
 171         q = (union store *)qq;
 172         r = q + (nbytes/WORD) - 1;
 173         q->ptr = r;
 174         if(alloca.ptr==0)               /*C can't initialize union*/
 175                 alloca.ptr = &alloca;
 176         for(p=allocb; ; p=s) {
 177                 s = clearbusy(p->ptr);
 178                 if(s==allocb)
 179                         break;
 180                 ASSERT(s>p);
 181                 if(s>r) {
 182                         if(p<q)
 183                                 break;
 184                         else
 185                                 ASSERT(p>r);
 186                 }
 187         }
 188         p->ptr = q==p+1? q: setbusy(q);
 189         r->ptr = s==r+1? s: setbusy(s);
 190         if(allocb > q)
 191                 allocb = q;
 192         allocp = allocb;
 193 }
 194 
 195 /*      realloc(p, nbytes) reallocates a block obtained from malloc()
 196  *      and freed since last call of malloc()
 197  *      to have new size nbytes, and old content
 198  *      returns new location, or 0 on failure
 199 */
 200 
 201 char *
 202 realloc(pp, nbytes)
 203 char *pp;
 204 unsigned nbytes;
 205 {
 206         register union store *q;
 207         register union store *p = (union store *)pp;
 208         union store *s, *t;
 209         register unsigned nw;
 210         unsigned onw;
 211 
 212         ASSERT(allock(p-1));
 213         if(testbusy(p[-1].ptr))
 214                 free((char *)p);
 215         onw = p[-1].ptr - p;
 216         q = (union store *)malloc(nbytes);
 217         if(q==NULL || q==p)
 218                 return((char *)q);
 219         ASSERT(q<p||q>p[-1].ptr);
 220         s = p;
 221         t = q;
 222         nw = (nbytes+WORD-1)/WORD;
 223         if(nw<onw)
 224                 onw = nw;
 225         while(onw--!=0)
 226                 *t++ = *s++;
 227         ASSERT(clearbusy(q[-1].ptr)-q==nw);
 228         if(q<p && q+nw>=p)
 229                 (q+(q+nw-p))->ptr = allocx;
 230         ASSERT(allock(q-1));
 231         return((char *)q);
 232 }
 233 
 234 #ifdef debug
 235 allock(q)
 236 union store *q;
 237 {
 238 #ifdef longdebug
 239         register union store *p, *r;
 240         int x;
 241         x = 0;
 242         p = allocb;
 243         if(alloca.ptr==0)
 244                 return(1);
 245         for( ; (r=clearbusy(p->ptr)) > p; p=r) {
 246                 if(p==q)
 247                         x++;
 248         }
 249         return(r==allocb&(x==1|p==q));
 250 #else
 251         return(q>=allocb);
 252 #endif
 253 }
 254 #endif
 255