Annotation of src/usr.bin/patch/malloc.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * from: @(#)nmalloc.c 1 (Caltech) 2/21/82
! 3: * $Id: malloc.c,v 1.2 1993/08/01 18:10:10 mycroft Exp $
! 4: *
! 5: * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs
! 6: *
! 7: * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD.
! 8: *
! 9: * This is a very fast storage allocator. It allocates blocks of a small
! 10: * number of different sizes, and keeps free lists of each size. Blocks
! 11: * that don't exactly fit are passed up to the next larger size. In this
! 12: * implementation, the available sizes are (2^n)-4 (or -16) bytes long.
! 13: * This is designed for use in a program that uses vast quantities of
! 14: * memory, but bombs when it runs out. To make it a little better, it
! 15: * warns the user when he starts to get near the end.
! 16: *
! 17: * June 84, ACT: modified rcheck code to check the range given to malloc,
! 18: * rather than the range determined by the 2-power used.
! 19: *
! 20: * Jan 85, RMS: calls malloc_warning to issue warning on nearly full.
! 21: * No longer Emacs-specific; can serve as all-purpose malloc for GNU.
! 22: * You should call malloc_init to reinitialize after loading dumped Emacs.
! 23: * Call malloc_stats to get info on memory stats if MSTATS turned on.
! 24: * realloc knows how to return same block given, just changing its size,
! 25: * if the power of 2 is correct.
! 26: */
! 27:
! 28: /*
! 29: * nextf[i] is the pointer to the next free block of size 2^(i+3). The
! 30: * smallest allocatable block is 8 bytes. The overhead information will
! 31: * go in the first int of the block, and the returned pointer will point
! 32: * to the second.
! 33: *
! 34: #ifdef MSTATS
! 35: * nmalloc[i] is the difference between the number of mallocs and frees
! 36: * for a given block size.
! 37: #endif /* MSTATS */
! 38: */
! 39:
! 40: #define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */
! 41: #define ISFREE ((char) 0x54) /* magic byte that implies free block */
! 42: /* this is for error checking only */
! 43:
! 44: extern char etext;
! 45:
! 46: /* end of the program; can be changed by calling init_malloc */
! 47: static char *endofpure = &etext;
! 48:
! 49: #ifdef MSTATS
! 50: static int nmalloc[30];
! 51: static int nmal, nfre;
! 52: #endif /* MSTATS */
! 53:
! 54: /* If range checking is not turned on, all we have is a flag indicating
! 55: whether memory is allocated, an index in nextf[], and a size field; to
! 56: realloc() memory we copy either size bytes or 1<<(index+3) bytes depending
! 57: on whether the former can hold the exact size (given the value of
! 58: 'index'). If range checking is on, we always need to know how much space
! 59: is allocated, so the 'size' field is never used. */
! 60:
! 61: struct mhead {
! 62: char mh_alloc; /* ISALLOC or ISFREE */
! 63: char mh_index; /* index in nextf[] */
! 64: /* Remainder are valid only when block is allocated */
! 65: unsigned short mh_size; /* size, if < 0x10000 */
! 66: #ifdef rcheck
! 67: unsigned mh_nbytes; /* number of bytes allocated */
! 68: int mh_magic4; /* should be == MAGIC4 */
! 69: #endif /* rcheck */
! 70: };
! 71:
! 72: /* Access free-list pointer of a block.
! 73: It is stored at block + 4.
! 74: This is not a field in the mhead structure
! 75: because we want sizeof (struct mhead)
! 76: to describe the overhead for when the block is in use,
! 77: and we do not want the free-list pointer to count in that. */
! 78:
! 79: #define CHAIN(a) \
! 80: (*(struct mhead **) (sizeof (char *) + (char *) (a)))
! 81:
! 82: #ifdef rcheck
! 83:
! 84: /* To implement range checking, we write magic values in at the beginning and
! 85: end of each allocated block, and make sure they are undisturbed whenever a
! 86: free or a realloc occurs. */
! 87: /* Written in each of the 4 bytes following the block's real space */
! 88: #define MAGIC1 0x55
! 89: /* Written in the 4 bytes before the block's real space */
! 90: #define MAGIC4 0x55555555
! 91: #define ASSERT(p) if (!(p)) botch("p"); else
! 92: static
! 93: botch(s)
! 94: char *s;
! 95: {
! 96:
! 97: printf("assertion botched: %s\n", s);
! 98: abort();
! 99: }
! 100: #define EXTRA 4 /* 4 bytes extra for MAGIC1s */
! 101: #else
! 102: #define ASSERT(p)
! 103: #define EXTRA 0
! 104: #endif /* rcheck */
! 105:
! 106: /* nextf[i] is free list of blocks of size 2**(i + 3) */
! 107:
! 108: static struct mhead *nextf[30];
! 109:
! 110: #ifdef M_WARN
! 111: /* Number of bytes of writable memory we can expect to be able to get */
! 112: static int lim_data;
! 113: /* Level number of warnings already issued.
! 114: 0 -- no warnings issued.
! 115: 1 -- 75% warning already issued.
! 116: 2 -- 85% warning already issued.
! 117: */
! 118: static int warnlevel;
! 119: #endif /* M_WARN */
! 120:
! 121: /* nonzero once initial bunch of free blocks made */
! 122: static int gotpool;
! 123:
! 124: /* Cause reinitialization based on job parameters;
! 125: also declare where the end of pure storage is. */
! 126: malloc_init (end)
! 127: char *end; {
! 128: endofpure = end;
! 129: #ifdef M_WARN
! 130: lim_data = 0;
! 131: warnlevel = 0;
! 132: #endif /* M_WARN */
! 133: }
! 134:
! 135: static
! 136: morecore (nu) /* ask system for more memory */
! 137: register int nu; { /* size index to get more of */
! 138: char *sbrk ();
! 139: register char *cp;
! 140: register int nblks;
! 141: register int siz;
! 142:
! 143: #ifdef M_WARN
! 144: #ifndef BSD42
! 145: #ifdef USG
! 146: extern long ulimit ();
! 147: if (lim_data == 0) /* find out how much we can get */
! 148: lim_data = ulimit (3, 0) - TEXT_START;
! 149: #else /*HMS: was endif */
! 150: if (lim_data == 0) /* find out how much we can get */
! 151: lim_data = vlimit (LIM_DATA, -1);
! 152: #endif /* USG */ /HMS:* was not here */
! 153: #else
! 154: if (lim_data == 0) {
! 155: struct rlimit XXrlimit;
! 156:
! 157: getrlimit (RLIMIT_DATA, &XXrlimit);
! 158: lim_data = XXrlimit.rlim_cur;} /* soft limit */
! 159: #endif /* BSD42 */
! 160: #endif /* M_WARN */
! 161:
! 162: /* On initial startup, get two blocks of each size up to 1k bytes */
! 163: if (!gotpool)
! 164: getpool (), getpool (), gotpool = 1;
! 165:
! 166: /* Find current end of memory and issue warning if getting near max */
! 167:
! 168: cp = sbrk (0);
! 169: siz = cp - endofpure;
! 170: #ifdef M_WARN
! 171: switch (warnlevel) {
! 172: case 0:
! 173: if (siz > (lim_data / 4) * 3) {
! 174: warnlevel++;
! 175: malloc_warning ("Warning: past 75% of memory limit");}
! 176: break;
! 177: case 1:
! 178: if (siz > (lim_data / 20) * 17) {
! 179: warnlevel++;
! 180: malloc_warning ("Warning: past 85% of memory limit");}
! 181: break;
! 182: case 2:
! 183: if (siz > (lim_data / 20) * 19) {
! 184: warnlevel++;
! 185: malloc_warning ("Warning: past 95% of memory limit");}
! 186: break;}
! 187: #endif /* M_WARN */
! 188:
! 189: if ((int) cp & 0x3ff) /* land on 1K boundaries */
! 190: sbrk (1024 - ((int) cp & 0x3ff));
! 191:
! 192: /* Take at least 2k, and figure out how many blocks of the desired size we're about to get */
! 193: nblks = 1;
! 194: if ((siz = nu) < 8)
! 195: nblks = 1 << ((siz = 8) - nu);
! 196:
! 197: if ((cp = sbrk (1 << (siz + 3))) == (char *) -1)
! 198: return; /* no more room! */
! 199: if ((int) cp & 7) { /* shouldn't happen, but just in case */
! 200: cp = (char *) (((int) cp + 8) & ~7);
! 201: nblks--;}
! 202:
! 203: /* save new header and link the nblks blocks together */
! 204: nextf[nu] = (struct mhead *) cp;
! 205: siz = 1 << (nu + 3);
! 206: while (1) {
! 207: ((struct mhead *) cp) -> mh_alloc = ISFREE;
! 208: ((struct mhead *) cp) -> mh_index = nu;
! 209: if (--nblks <= 0) break;
! 210: CHAIN ((struct mhead *) cp) = (struct mhead *) (cp + siz);
! 211: cp += siz;}
! 212: /* CHAIN ((struct mhead *) cp) = 0; /* since sbrk() returns cleared core, this is already set */
! 213: }
! 214:
! 215: static
! 216: getpool () {
! 217: register int nu;
! 218: register char *cp = sbrk (0);
! 219:
! 220: if ((int) cp & 0x3ff) /* land on 1K boundaries */
! 221: sbrk (1024 - ((int) cp & 0x3ff));
! 222:
! 223: /* Get 2k of storage */
! 224:
! 225: cp = sbrk (04000);
! 226: if (cp == (char *) -1)
! 227: return;
! 228:
! 229: /* Divide it into an initial 8-word block
! 230: plus one block of size 2**nu for nu = 3 ... 10. */
! 231:
! 232: CHAIN (cp) = nextf[0];
! 233: nextf[0] = (struct mhead *) cp;
! 234: ((struct mhead *) cp) -> mh_alloc = ISFREE;
! 235: ((struct mhead *) cp) -> mh_index = 0;
! 236: cp += 8;
! 237:
! 238: for (nu = 0; nu < 7; nu++) {
! 239: CHAIN (cp) = nextf[nu];
! 240: nextf[nu] = (struct mhead *) cp;
! 241: ((struct mhead *) cp) -> mh_alloc = ISFREE;
! 242: ((struct mhead *) cp) -> mh_index = nu;
! 243: cp += 8 << nu;}}
! 244:
! 245: char *
! 246: malloc (n) /* get a block */
! 247: unsigned n; {
! 248: register struct mhead *p;
! 249: register unsigned int nbytes;
! 250: register int nunits = 0;
! 251:
! 252: /* Figure out how many bytes are required, rounding up to the nearest
! 253: multiple of 4, then figure out which nextf[] area to use */
! 254: nbytes = (n + sizeof *p + EXTRA + 3) & ~3;
! 255: {
! 256: register unsigned int shiftr = (nbytes - 1) >> 2;
! 257:
! 258: while (shiftr >>= 1)
! 259: nunits++;
! 260: }
! 261:
! 262: /* If there are no blocks of the appropriate size, go get some */
! 263: /* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */
! 264: if (nextf[nunits] == 0)
! 265: morecore (nunits);
! 266:
! 267: /* Get one block off the list, and set the new list head */
! 268: if ((p = nextf[nunits]) == 0)
! 269: return 0;
! 270: nextf[nunits] = CHAIN (p);
! 271:
! 272: /* Check for free block clobbered */
! 273: /* If not for this check, we would gobble a clobbered free chain ptr */
! 274: /* and bomb out on the NEXT allocate of this size block */
! 275: if (p -> mh_alloc != ISFREE || p -> mh_index != nunits)
! 276: #ifdef rcheck
! 277: botch ("block on free list clobbered");
! 278: #else
! 279: abort ();
! 280: #endif /* rcheck */
! 281:
! 282: /* Fill in the info, and if range checking, set up the magic numbers */
! 283: p -> mh_alloc = ISALLOC;
! 284: #ifdef rcheck
! 285: p -> mh_nbytes = n;
! 286: p -> mh_magic4 = MAGIC4;
! 287: {
! 288: register char *m = (char *) (p + 1) + n;
! 289:
! 290: *m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1;
! 291: }
! 292: #else
! 293: p -> mh_size = n;
! 294: #endif /* rcheck */
! 295: #ifdef MSTATS
! 296: nmalloc[nunits]++;
! 297: nmal++;
! 298: #endif /* MSTATS */
! 299: return (char *) (p + 1);}
! 300:
! 301: free (mem)
! 302: char *mem; {
! 303: register struct mhead *p;
! 304: {
! 305: register char *ap = mem;
! 306:
! 307: ASSERT (ap != 0);
! 308: p = (struct mhead *) ap - 1;
! 309: ASSERT (p -> mh_alloc == ISALLOC);
! 310: #ifdef rcheck
! 311: ASSERT (p -> mh_magic4 == MAGIC4);
! 312: ap += p -> mh_nbytes;
! 313: ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1);
! 314: ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1);
! 315: #endif /* rcheck */
! 316: }
! 317: {
! 318: register int nunits = p -> mh_index;
! 319:
! 320: ASSERT (nunits <= 29);
! 321: p -> mh_alloc = ISFREE;
! 322: CHAIN (p) = nextf[nunits];
! 323: nextf[nunits] = p;
! 324: #ifdef MSTATS
! 325: nmalloc[nunits]--;
! 326: nfre++;
! 327: #endif /* MSTATS */
! 328: }
! 329: }
! 330:
! 331: char *
! 332: realloc (mem, n)
! 333: char *mem;
! 334: register unsigned n; {
! 335: register struct mhead *p;
! 336: register unsigned int tocopy;
! 337: register int nbytes;
! 338: register int nunits;
! 339:
! 340: if ((p = (struct mhead *) mem) == 0)
! 341: return malloc (n);
! 342: p--;
! 343: nunits = p -> mh_index;
! 344: ASSERT (p -> mh_alloc == ISALLOC);
! 345: #ifdef rcheck
! 346: ASSERT (p -> mh_magic4 == MAGIC4);
! 347: {
! 348: register char *m = mem + (tocopy = p -> mh_nbytes);
! 349: ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1);
! 350: ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1);
! 351: }
! 352: #else
! 353: if (p -> mh_index >= 13)
! 354: tocopy = (1 << (p -> mh_index + 3)) - sizeof *p;
! 355: else
! 356: tocopy = p -> mh_size;
! 357: #endif /* rcheck */
! 358:
! 359: /* See if desired size rounds to same power of 2 as actual size. */
! 360: nbytes = (n + sizeof *p + EXTRA + 7) & ~7;
! 361:
! 362: /* If ok, use the same block, just marking its size as changed. */
! 363: if (nbytes > (4 << nunits) && nbytes <= (8 << nunits)) {
! 364: #ifdef rcheck
! 365: register char *m = mem + tocopy;
! 366: *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0;
! 367: p-> mh_nbytes = n;
! 368: m = mem + n;
! 369: *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1;
! 370: #else
! 371: p -> mh_size = n;
! 372: #endif /* rcheck */
! 373: return mem;}
! 374:
! 375: if (n < tocopy)
! 376: tocopy = n;
! 377: {
! 378: register char *new;
! 379: void bcopy(); /*HMS: here? */
! 380:
! 381: if ((new = malloc (n)) == 0)
! 382: return 0;
! 383: bcopy (mem, new, tocopy);
! 384: free (mem);
! 385: return new;
! 386: }
! 387: }
! 388:
! 389: #ifdef MSTATS
! 390: /* Return statistics describing allocation of blocks of size 2**n. */
! 391:
! 392: struct mstats_value {
! 393: int blocksize;
! 394: int nfree;
! 395: int nused;
! 396: };
! 397:
! 398: struct mstats_value
! 399: malloc_stats (size)
! 400: int size; {
! 401: struct mstats_value v;
! 402: register int i;
! 403: register struct mhead *p;
! 404:
! 405: v.nfree = 0;
! 406:
! 407: if (size < 0 || size >= 30) {
! 408: v.blocksize = 0;
! 409: v.nused = 0;
! 410: return v;}
! 411:
! 412: v.blocksize = 1 << (size + 3);
! 413: v.nused = nmalloc[size];
! 414:
! 415: for (p = nextf[size]; p; p = CHAIN (p))
! 416: v.nfree++;
! 417:
! 418: return v;}
! 419: #endif
! 420:
! 421: /* how much space is available? */
! 422:
! 423: unsigned freespace() {
! 424: register int i, j;
! 425: register struct mhead *p;
! 426: register unsigned space = 0;
! 427: int local; /* address only is used */
! 428:
! 429: space = (char *)&local - sbrk(0); /* stack space */
! 430:
! 431: for (i = 0; i < 30; i++) {
! 432: for (j = 0, p = nextf[i]; p; p = CHAIN (p), j++) ;
! 433: space += j * (1 << (i + 3));}
! 434:
! 435: return(space);}
! 436:
! 437: /* How big is this cell? */
! 438:
! 439: unsigned mc_size(cp)
! 440: char *cp;{
! 441: register struct mhead *p;
! 442:
! 443: if ((p = (struct mhead *) cp) == 0) {
! 444: /*HMS? */
! 445: }
! 446: p--;
! 447: #ifdef rcheck
! 448: return p -> mh_nbytes;
! 449: #else
! 450: return (1 << (p -> mh_index + 3)) - sizeof *p;
! 451: /**/
! 452: /* if (p -> mh_index >= 13)
! 453: /* return (1 << (p -> mh_index + 3)) - sizeof *p;
! 454: /* else
! 455: /* return p -> mh_size;
! 456: /**/
! 457: #endif /* rcheck */
! 458: }
! 459:
! 460: /*HMS: Really should use memcpy, if available... */
! 461:
! 462: void bcopy(source, dest, len)
! 463: register char *source, *dest;
! 464: register len; {
! 465: register i;
! 466:
! 467: for (i = 0; i < len; i++)
! 468: *dest++ = *source++;}