121347Sdist /* 225177Smckusick * Copyright (c) 1983 Regents of the University of California. 321347Sdist * All rights reserved. The Berkeley software License Agreement 421347Sdist * specifies the terms and conditions for redistribution. 521347Sdist */ 621347Sdist 713259Sroot #ifndef lint 8*25793Smckusick static char sccsid[] = "@(#)malloc.c 5.3 (Berkeley) 01/09/86"; 921347Sdist #endif not lint 1014953Skarels 1114953Skarels /* 1214953Skarels * malloc.c (Caltech) 2/21/82 1314953Skarels * Chris Kingsley, kingsley@cit-20. 1414953Skarels * 1514953Skarels * This is a very fast storage allocator. It allocates blocks of a small 1614953Skarels * number of different sizes, and keeps free lists of each size. Blocks that 1714953Skarels * don't exactly fit are passed up to the next larger size. In this 1814953Skarels * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long. 1914953Skarels * This is designed for use in a program that uses vast quantities of memory, 2014953Skarels * but bombs when it runs out. 2114953Skarels */ 2214953Skarels 2314953Skarels #include <sys/types.h> 2414953Skarels 2514953Skarels #define NULL 0 2614953Skarels 2714953Skarels /* 2814953Skarels * The overhead on a block is at least 4 bytes. When free, this space 2914953Skarels * contains a pointer to the next free block, and the bottom two bits must 3014953Skarels * be zero. When in use, the first byte is set to MAGIC, and the second 3114953Skarels * byte is the size index. The remaining bytes are for alignment. 3214953Skarels * If range checking is enabled and the size of the block fits 3314953Skarels * in two bytes, then the top two bytes hold the size of the requested block 3414953Skarels * plus the range checking words, and the header word MINUS ONE. 3514953Skarels */ 3614953Skarels union overhead { 3714953Skarels union overhead *ov_next; /* when free */ 3814953Skarels struct { 3917333Sralph #ifndef RCHECK 4014953Skarels u_char ovu_magic; /* magic number */ 4114953Skarels u_char ovu_index; /* bucket # */ 4217333Sralph #else 4317333Sralph u_int ovu_size; /* actual block size */ 4417333Sralph u_char ovu_magic; /* magic number */ 4517333Sralph u_char ovu_index; /* bucket # */ 4617333Sralph u_short ovu_rmagic; /* range magic number */ 4714953Skarels #endif 4814953Skarels } ovu; 4914953Skarels #define ov_magic ovu.ovu_magic 5014953Skarels #define ov_index ovu.ovu_index 5117333Sralph #define ov_rmagic ovu.ovu_rmagic 5214953Skarels #define ov_size ovu.ovu_size 5314953Skarels }; 5414953Skarels 5517333Sralph #define MAGIC 0xef /* magic # on accounting info */ 5617333Sralph #define RMAGIC 0x5555 /* magic # on range info */ 5717333Sralph 5814953Skarels #ifdef RCHECK 5917333Sralph #define RSLOP sizeof (u_short) 6014953Skarels #else 6114953Skarels #define RSLOP 0 6214953Skarels #endif 6314953Skarels 6414953Skarels /* 6514953Skarels * nextf[i] is the pointer to the next free block of size 2^(i+3). The 6614953Skarels * smallest allocatable block is 8 bytes. The overhead information 6714953Skarels * precedes the data area returned to the user. 6814953Skarels */ 6914953Skarels #define NBUCKETS 30 7014953Skarels static union overhead *nextf[NBUCKETS]; 7114953Skarels extern char *sbrk(); 7214953Skarels 7317333Sralph static int pagesz; /* page size */ 7417333Sralph static int pagebucket; /* page size bucket */ 7517333Sralph 7614953Skarels #ifdef MSTATS 7714953Skarels /* 7814953Skarels * nmalloc[i] is the difference between the number of mallocs and frees 7914953Skarels * for a given block size. 8014953Skarels */ 8114953Skarels static u_int nmalloc[NBUCKETS]; 8214953Skarels #include <stdio.h> 8314953Skarels #endif 8414953Skarels 8517333Sralph #ifdef DEBUG 8617333Sralph #define ASSERT(p) if (!(p)) botch("p") 8714953Skarels static 8813259Sroot botch(s) 8915003Ssam char *s; 9013259Sroot { 9115003Ssam 9215003Ssam printf("assertion botched: %s\n", s); 9313259Sroot abort(); 9413259Sroot } 9513259Sroot #else 9614953Skarels #define ASSERT(p) 9713259Sroot #endif 9813259Sroot 9913259Sroot char * 10013259Sroot malloc(nbytes) 10117333Sralph unsigned nbytes; 10213259Sroot { 10317333Sralph register union overhead *op; 10417333Sralph register int bucket; 10517333Sralph register unsigned amt, n; 10613259Sroot 10714953Skarels /* 10817333Sralph * First time malloc is called, setup page size and 10917333Sralph * align break pointer so all data will be page aligned. 11014953Skarels */ 11117333Sralph if (pagesz == 0) { 11217333Sralph pagesz = n = getpagesize(); 11317333Sralph op = (union overhead *)sbrk(0); 11417333Sralph n = n - sizeof (*op) - ((int)op & (n - 1)); 11517333Sralph if (n < 0) 11617333Sralph n += pagesz; 11717333Sralph if (n) { 11817333Sralph if (sbrk(n) == (char *)-1) 11917333Sralph return (NULL); 12017333Sralph } 12117333Sralph bucket = 0; 12217333Sralph amt = 8; 12317333Sralph while (pagesz > amt) { 12417333Sralph amt <<= 1; 12517333Sralph bucket++; 12617333Sralph } 12717333Sralph pagebucket = bucket; 12817333Sralph } 12914953Skarels /* 13017333Sralph * Convert amount of memory requested into closest block size 13117333Sralph * stored in hash buckets which satisfies request. 13217333Sralph * Account for space used per block for accounting. 13317333Sralph */ 13417333Sralph if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { 13517333Sralph #ifndef RCHECK 13617333Sralph amt = 8; /* size of first bucket */ 13717333Sralph bucket = 0; 13817333Sralph #else 13917333Sralph amt = 16; /* size of first bucket */ 14017333Sralph bucket = 1; 14117333Sralph #endif 14217333Sralph n = -(sizeof (*op) + RSLOP); 14317333Sralph } else { 14417333Sralph amt = pagesz; 14517333Sralph bucket = pagebucket; 14617333Sralph } 14717333Sralph while (nbytes > amt + n) { 14817333Sralph amt <<= 1; 149*25793Smckusick if (amt == 0) 150*25793Smckusick return (NULL); 15117333Sralph bucket++; 15217333Sralph } 15317333Sralph /* 15414953Skarels * If nothing in hash bucket right now, 15514953Skarels * request more memory from the system. 15614953Skarels */ 15717333Sralph if ((op = nextf[bucket]) == NULL) { 15814953Skarels morecore(bucket); 15917333Sralph if ((op = nextf[bucket]) == NULL) 16017333Sralph return (NULL); 16117333Sralph } 16214953Skarels /* remove from linked list */ 16317333Sralph nextf[bucket] = op->ov_next; 16417333Sralph op->ov_magic = MAGIC; 16517333Sralph op->ov_index = bucket; 16614953Skarels #ifdef MSTATS 16714953Skarels nmalloc[bucket]++; 16814953Skarels #endif 16914953Skarels #ifdef RCHECK 17014953Skarels /* 17114953Skarels * Record allocated size of block and 17214953Skarels * bound space with magic numbers. 17314953Skarels */ 17417438Sralph op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 17517333Sralph op->ov_rmagic = RMAGIC; 17617438Sralph *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 17714953Skarels #endif 17817333Sralph return ((char *)(op + 1)); 17913259Sroot } 18013259Sroot 18114953Skarels /* 18214953Skarels * Allocate more memory to the indicated bucket. 18314953Skarels */ 18414953Skarels morecore(bucket) 18517333Sralph int bucket; 18613259Sroot { 18714953Skarels register union overhead *op; 18817333Sralph register int sz; /* size of desired block */ 189*25793Smckusick int amt; /* amount to allocate */ 190*25793Smckusick int nblks; /* how many blocks we get */ 19113259Sroot 192*25793Smckusick /* 193*25793Smckusick * sbrk_size <= 0 only for big, FLUFFY, requests (about 194*25793Smckusick * 2^30 bytes on a VAX, I think) or for a negative arg. 195*25793Smckusick */ 19617333Sralph sz = 1 << (bucket + 3); 197*25793Smckusick if (sz <= 0) 198*25793Smckusick return; 19917333Sralph if (sz < pagesz) { 20017333Sralph amt = pagesz; 20117333Sralph nblks = amt / sz; 20217333Sralph } else { 20317333Sralph amt = sz + pagesz; 20417333Sralph nblks = 1; 20517333Sralph } 20617333Sralph op = (union overhead *)sbrk(amt); 20714953Skarels /* no more room! */ 20814953Skarels if ((int)op == -1) 20914953Skarels return; 21014953Skarels /* 21114953Skarels * Add new memory allocated to that on 21214953Skarels * free list for this hash bucket. 21314953Skarels */ 21414953Skarels nextf[bucket] = op; 21514953Skarels while (--nblks > 0) { 21617333Sralph op->ov_next = (union overhead *)((caddr_t)op + sz); 21717333Sralph op = (union overhead *)((caddr_t)op + sz); 21814953Skarels } 21913259Sroot } 22013259Sroot 22114953Skarels free(cp) 22214953Skarels char *cp; 22314953Skarels { 22414953Skarels register int size; 22514953Skarels register union overhead *op; 22613259Sroot 22714953Skarels if (cp == NULL) 22814953Skarels return; 22914953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 23017333Sralph #ifdef DEBUG 23114953Skarels ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ 23214953Skarels #else 23314953Skarels if (op->ov_magic != MAGIC) 23414953Skarels return; /* sanity */ 23514953Skarels #endif 23614953Skarels #ifdef RCHECK 23714953Skarels ASSERT(op->ov_rmagic == RMAGIC); 23817333Sralph ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); 23914953Skarels #endif 24014953Skarels size = op->ov_index; 24117333Sralph ASSERT(size < NBUCKETS); 24214953Skarels op->ov_next = nextf[size]; 24314953Skarels nextf[size] = op; 24414953Skarels #ifdef MSTATS 24514953Skarels nmalloc[size]--; 24614953Skarels #endif 24714953Skarels } 24814953Skarels 24914953Skarels /* 25014953Skarels * When a program attempts "storage compaction" as mentioned in the 25114953Skarels * old malloc man page, it realloc's an already freed block. Usually 25214953Skarels * this is the last block it freed; occasionally it might be farther 25314953Skarels * back. We have to search all the free lists for the block in order 25414953Skarels * to determine its bucket: 1st we make one pass thru the lists 25514953Skarels * checking only the first block in each; if that fails we search 25614953Skarels * ``realloc_srchlen'' blocks in each list for a match (the variable 25714953Skarels * is extern so the caller can modify it). If that fails we just copy 25814953Skarels * however many bytes was given to realloc() and hope it's not huge. 25914953Skarels */ 26015003Ssam int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ 26114953Skarels 26213259Sroot char * 26314953Skarels realloc(cp, nbytes) 26414953Skarels char *cp; 26514953Skarels unsigned nbytes; 26614953Skarels { 26717333Sralph register u_int onb, i; 26814953Skarels union overhead *op; 26914953Skarels char *res; 27014953Skarels int was_alloced = 0; 27114953Skarels 27214953Skarels if (cp == NULL) 27314953Skarels return (malloc(nbytes)); 27414953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 27514953Skarels if (op->ov_magic == MAGIC) { 27614953Skarels was_alloced++; 27714953Skarels i = op->ov_index; 27815003Ssam } else { 27915003Ssam /* 28015003Ssam * Already free, doing "compaction". 28115003Ssam * 28215003Ssam * Search for the old block of memory on the 28315003Ssam * free list. First, check the most common 28415003Ssam * case (last element free'd), then (this failing) 28515003Ssam * the last ``realloc_srchlen'' items free'd. 28615003Ssam * If all lookups fail, then assume the size of 28715003Ssam * the memory block being realloc'd is the 28815003Ssam * smallest possible. 28915003Ssam */ 29014953Skarels if ((i = findbucket(op, 1)) < 0 && 29114953Skarels (i = findbucket(op, realloc_srchlen)) < 0) 29217333Sralph #ifndef RCHECK 29315003Ssam i = 0; 29417333Sralph #else 29517333Sralph i = 1; /* smallest possible w/ RCHECK */ 29617333Sralph #endif 29714953Skarels } 29817333Sralph onb = 1 << (i + 3); 29917333Sralph if (onb < pagesz) 30017333Sralph onb -= sizeof (*op) + RSLOP; 30117333Sralph else 30217333Sralph onb += pagesz - sizeof (*op) - RSLOP; 30315003Ssam /* avoid the copy if same size block */ 30417333Sralph if (was_alloced) { 30517333Sralph if (i) { 30617333Sralph i = 1 << (i + 2); 30717333Sralph if (i < pagesz) 30817333Sralph i -= sizeof (*op) + RSLOP; 30917333Sralph else 31017333Sralph i += pagesz - sizeof (*op) - RSLOP; 31117333Sralph } 31217333Sralph if (nbytes <= onb && nbytes > i) { 31317332Sralph #ifdef RCHECK 31417438Sralph op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 31517333Sralph *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 31617332Sralph #endif 31717333Sralph return(cp); 31817333Sralph } else 31917333Sralph free(cp); 32017332Sralph } 32114953Skarels if ((res = malloc(nbytes)) == NULL) 32214953Skarels return (NULL); 32314953Skarels if (cp != res) /* common optimization */ 32414953Skarels bcopy(cp, res, (nbytes < onb) ? nbytes : onb); 32514953Skarels return (res); 32614953Skarels } 32714953Skarels 32814953Skarels /* 32914953Skarels * Search ``srchlen'' elements of each free list for a block whose 33014953Skarels * header starts at ``freep''. If srchlen is -1 search the whole list. 33114953Skarels * Return bucket number, or -1 if not found. 33214953Skarels */ 33314953Skarels static 33414953Skarels findbucket(freep, srchlen) 33515003Ssam union overhead *freep; 33615003Ssam int srchlen; 33713259Sroot { 33814953Skarels register union overhead *p; 33914953Skarels register int i, j; 34013259Sroot 34115003Ssam for (i = 0; i < NBUCKETS; i++) { 34215003Ssam j = 0; 34315003Ssam for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { 34414953Skarels if (p == freep) 34514953Skarels return (i); 34615003Ssam j++; 34715003Ssam } 34815003Ssam } 34914953Skarels return (-1); 35013259Sroot } 35113259Sroot 35214953Skarels #ifdef MSTATS 35314953Skarels /* 35414953Skarels * mstats - print out statistics about malloc 35514953Skarels * 35614953Skarels * Prints two lines of numbers, one showing the length of the free list 35714953Skarels * for each size category, the second showing the number of mallocs - 35814953Skarels * frees for each size category. 35914953Skarels */ 36014953Skarels mstats(s) 36114953Skarels char *s; 36213259Sroot { 36314953Skarels register int i, j; 36414953Skarels register union overhead *p; 36514953Skarels int totfree = 0, 36614953Skarels totused = 0; 36714953Skarels 36814953Skarels fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); 36914953Skarels for (i = 0; i < NBUCKETS; i++) { 37014953Skarels for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) 37114953Skarels ; 37214953Skarels fprintf(stderr, " %d", j); 37314953Skarels totfree += j * (1 << (i + 3)); 37414953Skarels } 37514953Skarels fprintf(stderr, "\nused:\t"); 37614953Skarels for (i = 0; i < NBUCKETS; i++) { 37714953Skarels fprintf(stderr, " %d", nmalloc[i]); 37814953Skarels totused += nmalloc[i] * (1 << (i + 3)); 37914953Skarels } 38015003Ssam fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", 38115003Ssam totused, totfree); 38213259Sroot } 38313259Sroot #endif 384