113259Sroot #ifndef lint 2*14953Skarels static char sccsid[] = "@(#)malloc.c 4.2 (Berkeley) 09/11/83"; 313259Sroot #endif 4*14953Skarels 5*14953Skarels /* 6*14953Skarels * malloc.c (Caltech) 2/21/82 7*14953Skarels * Chris Kingsley, kingsley@cit-20. 8*14953Skarels * 9*14953Skarels * This is a very fast storage allocator. It allocates blocks of a small 10*14953Skarels * number of different sizes, and keeps free lists of each size. Blocks that 11*14953Skarels * don't exactly fit are passed up to the next larger size. In this 12*14953Skarels * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long. 13*14953Skarels * This is designed for use in a program that uses vast quantities of memory, 14*14953Skarels * but bombs when it runs out. 15*14953Skarels */ 16*14953Skarels 17*14953Skarels #include <sys/types.h> 18*14953Skarels 19*14953Skarels #define NULL 0 20*14953Skarels 21*14953Skarels /* 22*14953Skarels * The overhead on a block is at least 4 bytes. When free, this space 23*14953Skarels * contains a pointer to the next free block, and the bottom two bits must 24*14953Skarels * be zero. When in use, the first byte is set to MAGIC, and the second 25*14953Skarels * byte is the size index. The remaining bytes are for alignment. 26*14953Skarels * If range checking is enabled and the size of the block fits 27*14953Skarels * in two bytes, then the top two bytes hold the size of the requested block 28*14953Skarels * plus the range checking words, and the header word MINUS ONE. 29*14953Skarels */ 30*14953Skarels union overhead { 31*14953Skarels union overhead *ov_next; /* when free */ 32*14953Skarels struct { 33*14953Skarels u_char ovu_magic; /* magic number */ 34*14953Skarels u_char ovu_index; /* bucket # */ 35*14953Skarels #ifdef RCHECK 36*14953Skarels u_short ovu_size; /* actual block size */ 37*14953Skarels u_int ovu_rmagic; /* range magic number */ 38*14953Skarels #endif 39*14953Skarels } ovu; 40*14953Skarels #define ov_magic ovu.ovu_magic 41*14953Skarels #define ov_index ovu.ovu_index 42*14953Skarels #define ov_size ovu.ovu_size 43*14953Skarels #define ov_rmagic ovu.ovu_rmagic 44*14953Skarels }; 45*14953Skarels 46*14953Skarels #define MAGIC 0xff /* magic # on accounting info */ 47*14953Skarels #define RMAGIC 0x55555555 /* magic # on range info */ 48*14953Skarels #ifdef RCHECK 49*14953Skarels #define RSLOP sizeof (u_int) 50*14953Skarels #else 51*14953Skarels #define RSLOP 0 52*14953Skarels #endif 53*14953Skarels 54*14953Skarels /* 55*14953Skarels * nextf[i] is the pointer to the next free block of size 2^(i+3). The 56*14953Skarels * smallest allocatable block is 8 bytes. The overhead information 57*14953Skarels * precedes the data area returned to the user. 58*14953Skarels */ 59*14953Skarels #define NBUCKETS 30 60*14953Skarels static union overhead *nextf[NBUCKETS]; 61*14953Skarels extern char *sbrk(); 62*14953Skarels 63*14953Skarels #ifdef MSTATS 64*14953Skarels /* 65*14953Skarels * nmalloc[i] is the difference between the number of mallocs and frees 66*14953Skarels * for a given block size. 67*14953Skarels */ 68*14953Skarels static u_int nmalloc[NBUCKETS]; 69*14953Skarels #include <stdio.h> 70*14953Skarels #endif 71*14953Skarels 7213259Sroot #ifdef debug 73*14953Skarels #define ASSERT(p) if (!(p)) botch("p"); else 74*14953Skarels static 7513259Sroot botch(s) 7613259Sroot char *s; 7713259Sroot { 7813259Sroot printf("assertion botched: %s\n",s); 7913259Sroot abort(); 8013259Sroot } 8113259Sroot #else 82*14953Skarels #define ASSERT(p) 8313259Sroot #endif 8413259Sroot 8513259Sroot char * 8613259Sroot malloc(nbytes) 87*14953Skarels register unsigned nbytes; 8813259Sroot { 89*14953Skarels register union overhead *p; 90*14953Skarels register int bucket = 0; 91*14953Skarels register unsigned shiftr; 9213259Sroot 93*14953Skarels /* 94*14953Skarels * Convert amount of memory requested into 95*14953Skarels * closest block size stored in hash buckets 96*14953Skarels * which satisfies request. Account for 97*14953Skarels * space used per block for accounting. 98*14953Skarels */ 99*14953Skarels nbytes += sizeof (union overhead) + RSLOP; 100*14953Skarels nbytes = (nbytes + 3) &~ 3; 101*14953Skarels shiftr = (nbytes - 1) >> 2; 102*14953Skarels /* apart from this loop, this is O(1) */ 103*14953Skarels while (shiftr >>= 1) 104*14953Skarels bucket++; 105*14953Skarels /* 106*14953Skarels * If nothing in hash bucket right now, 107*14953Skarels * request more memory from the system. 108*14953Skarels */ 109*14953Skarels if (nextf[bucket] == NULL) 110*14953Skarels morecore(bucket); 111*14953Skarels if ((p = (union overhead *)nextf[bucket]) == NULL) 112*14953Skarels return (NULL); 113*14953Skarels /* remove from linked list */ 114*14953Skarels nextf[bucket] = nextf[bucket]->ov_next; 115*14953Skarels p->ov_magic = MAGIC; 116*14953Skarels p->ov_index= bucket; 117*14953Skarels #ifdef MSTATS 118*14953Skarels nmalloc[bucket]++; 119*14953Skarels #endif 120*14953Skarels #ifdef RCHECK 121*14953Skarels /* 122*14953Skarels * Record allocated size of block and 123*14953Skarels * bound space with magic numbers. 124*14953Skarels */ 125*14953Skarels if (nbytes <= 0x10000) 126*14953Skarels p->ov_size = nbytes - 1; 127*14953Skarels p->ov_rmagic = RMAGIC; 128*14953Skarels *((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC; 129*14953Skarels #endif 130*14953Skarels return ((char *)(p + 1)); 13113259Sroot } 13213259Sroot 133*14953Skarels /* 134*14953Skarels * Allocate more memory to the indicated bucket. 135*14953Skarels */ 136*14953Skarels static 137*14953Skarels morecore(bucket) 138*14953Skarels register bucket; 13913259Sroot { 140*14953Skarels register union overhead *op; 141*14953Skarels register int rnu; /* 2^rnu bytes will be requested */ 142*14953Skarels register int nblks; /* become nblks blocks of the desired size */ 143*14953Skarels register int siz; 14413259Sroot 145*14953Skarels if (nextf[bucket]) 146*14953Skarels return; 147*14953Skarels /* 148*14953Skarels * Insure memory is allocated 149*14953Skarels * on a page boundary. Should 150*14953Skarels * make getpageize call? 151*14953Skarels */ 152*14953Skarels op = (union overhead *)sbrk(0); 153*14953Skarels if ((int)op & 0x3ff) 154*14953Skarels sbrk(1024 - ((int)op & 0x3ff)); 155*14953Skarels /* take 2k unless the block is bigger than that */ 156*14953Skarels rnu = (bucket <= 8) ? 11 : bucket + 3; 157*14953Skarels nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */ 158*14953Skarels if (rnu < bucket) 159*14953Skarels rnu = bucket; 160*14953Skarels op = (union overhead *)sbrk(1 << rnu); 161*14953Skarels /* no more room! */ 162*14953Skarels if ((int)op == -1) 163*14953Skarels return; 164*14953Skarels /* 165*14953Skarels * Round up to minimum allocation size boundary 166*14953Skarels * and deduct from block count to reflect. 167*14953Skarels */ 168*14953Skarels if ((int)op & 7) { 169*14953Skarels op = (union overhead *)(((int)op + 8) &~ 7); 170*14953Skarels nblks--; 171*14953Skarels } 172*14953Skarels /* 173*14953Skarels * Add new memory allocated to that on 174*14953Skarels * free list for this hash bucket. 175*14953Skarels */ 176*14953Skarels nextf[bucket] = op; 177*14953Skarels siz = 1 << (bucket + 3); 178*14953Skarels while (--nblks > 0) { 179*14953Skarels op->ov_next = (union overhead *)((caddr_t)op + siz); 180*14953Skarels op = (union overhead *)((caddr_t)op + siz); 181*14953Skarels } 18213259Sroot } 18313259Sroot 184*14953Skarels free(cp) 185*14953Skarels char *cp; 186*14953Skarels { 187*14953Skarels register int size; 188*14953Skarels register union overhead *op; 18913259Sroot 190*14953Skarels if (cp == NULL) 191*14953Skarels return; 192*14953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 193*14953Skarels #ifdef debug 194*14953Skarels ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ 195*14953Skarels #else 196*14953Skarels if (op->ov_magic != MAGIC) 197*14953Skarels return; /* sanity */ 198*14953Skarels #endif 199*14953Skarels #ifdef RCHECK 200*14953Skarels ASSERT(op->ov_rmagic == RMAGIC); 201*14953Skarels if (op->ov_index <= 13) 202*14953Skarels ASSERT(*(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC); 203*14953Skarels #endif 204*14953Skarels ASSERT(op->ov_index < NBUCKETS); 205*14953Skarels size = op->ov_index; 206*14953Skarels op->ov_next = nextf[size]; 207*14953Skarels nextf[size] = op; 208*14953Skarels #ifdef MSTATS 209*14953Skarels nmalloc[size]--; 210*14953Skarels #endif 211*14953Skarels } 212*14953Skarels 213*14953Skarels /* 214*14953Skarels * When a program attempts "storage compaction" as mentioned in the 215*14953Skarels * old malloc man page, it realloc's an already freed block. Usually 216*14953Skarels * this is the last block it freed; occasionally it might be farther 217*14953Skarels * back. We have to search all the free lists for the block in order 218*14953Skarels * to determine its bucket: 1st we make one pass thru the lists 219*14953Skarels * checking only the first block in each; if that fails we search 220*14953Skarels * ``realloc_srchlen'' blocks in each list for a match (the variable 221*14953Skarels * is extern so the caller can modify it). If that fails we just copy 222*14953Skarels * however many bytes was given to realloc() and hope it's not huge. 223*14953Skarels */ 224*14953Skarels int realloc_srchlen = 4; /* 4 should be plenty. -1 means whole list */ 225*14953Skarels 22613259Sroot char * 227*14953Skarels realloc(cp, nbytes) 228*14953Skarels char *cp; 229*14953Skarels unsigned nbytes; 230*14953Skarels { 231*14953Skarels register u_int onb; 232*14953Skarels union overhead *op; 233*14953Skarels char *res; 234*14953Skarels register int i; 235*14953Skarels int was_alloced = 0; 236*14953Skarels 237*14953Skarels if (cp == NULL) 238*14953Skarels return (malloc(nbytes)); 239*14953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 240*14953Skarels if (op->ov_magic == MAGIC) { 241*14953Skarels was_alloced++; 242*14953Skarels i = op->ov_index; 243*14953Skarels } 244*14953Skarels else { /* already free: he is doing "compaction" (tee hee) */ 245*14953Skarels if ((i = findbucket(op, 1)) < 0 && 246*14953Skarels (i = findbucket(op, realloc_srchlen)) < 0) 247*14953Skarels i = 0; /* assume shortest possible */ 248*14953Skarels } 249*14953Skarels onb = (1 << (i + 3)) - sizeof (*op) - RSLOP; 250*14953Skarels if (was_alloced && /* avoid the copy if same size block */ 251*14953Skarels nbytes <= onb && nbytes > (onb >> 1) - sizeof(*op) - RSLOP) 252*14953Skarels return(cp); 253*14953Skarels if ((res = malloc(nbytes)) == NULL) 254*14953Skarels return (NULL); 255*14953Skarels if (cp != res) /* common optimization */ 256*14953Skarels bcopy(cp, res, (nbytes < onb) ? nbytes : onb); 257*14953Skarels if (was_alloced) 258*14953Skarels free(cp); 259*14953Skarels return (res); 260*14953Skarels } 261*14953Skarels 262*14953Skarels /* 263*14953Skarels * Search ``srchlen'' elements of each free list for a block whose 264*14953Skarels * header starts at ``freep''. If srchlen is -1 search the whole list. 265*14953Skarels * Return bucket number, or -1 if not found. 266*14953Skarels */ 267*14953Skarels static 268*14953Skarels findbucket(freep, srchlen) 269*14953Skarels union overhead *freep; 270*14953Skarels int srchlen; 27113259Sroot { 272*14953Skarels register union overhead *p; 273*14953Skarels register int i, j; 27413259Sroot 275*14953Skarels for (i = 0; i < NBUCKETS; i++) 276*14953Skarels for (j = 0, p = nextf[i]; p && j != srchlen; j++, p = p->ov_next) 277*14953Skarels if (p == freep) 278*14953Skarels return (i); 279*14953Skarels return (-1); 28013259Sroot } 28113259Sroot 282*14953Skarels #ifdef MSTATS 283*14953Skarels /* 284*14953Skarels * mstats - print out statistics about malloc 285*14953Skarels * 286*14953Skarels * Prints two lines of numbers, one showing the length of the free list 287*14953Skarels * for each size category, the second showing the number of mallocs - 288*14953Skarels * frees for each size category. 289*14953Skarels */ 290*14953Skarels mstats(s) 291*14953Skarels char *s; 29213259Sroot { 293*14953Skarels register int i, j; 294*14953Skarels register union overhead *p; 295*14953Skarels int totfree = 0, 296*14953Skarels totused = 0; 297*14953Skarels 298*14953Skarels fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); 299*14953Skarels for (i = 0; i < NBUCKETS; i++) { 300*14953Skarels for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) 301*14953Skarels ; 302*14953Skarels fprintf(stderr, " %d", j); 303*14953Skarels totfree += j * (1 << (i + 3)); 304*14953Skarels } 305*14953Skarels fprintf(stderr, "\nused:\t"); 306*14953Skarels for (i = 0; i < NBUCKETS; i++) { 307*14953Skarels fprintf(stderr, " %d", nmalloc[i]); 308*14953Skarels totused += nmalloc[i] * (1 << (i + 3)); 309*14953Skarels } 310*14953Skarels fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", totused, totfree); 31113259Sroot } 31213259Sroot #endif 313