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*25799Slepreau static char sccsid[] = "@(#)malloc.c 5.4 (Berkeley) 01/10/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 85*25799Slepreau #if defined(DEBUG) || defined(RCHECK) 8617333Sralph #define ASSERT(p) if (!(p)) botch("p") 87*25799Slepreau #include <stdio.h> 8814953Skarels static 8913259Sroot botch(s) 9015003Ssam char *s; 9113259Sroot { 92*25799Slepreau fprintf(stderr, "\r\nassertion botched: %s\r\n", s); 93*25799Slepreau (void) fflush(stderr); /* just in case user buffered it */ 9413259Sroot abort(); 9513259Sroot } 9613259Sroot #else 9714953Skarels #define ASSERT(p) 9813259Sroot #endif 9913259Sroot 10013259Sroot char * 10113259Sroot malloc(nbytes) 10217333Sralph unsigned nbytes; 10313259Sroot { 10417333Sralph register union overhead *op; 10517333Sralph register int bucket; 10617333Sralph register unsigned amt, n; 10713259Sroot 10814953Skarels /* 10917333Sralph * First time malloc is called, setup page size and 11017333Sralph * align break pointer so all data will be page aligned. 11114953Skarels */ 11217333Sralph if (pagesz == 0) { 11317333Sralph pagesz = n = getpagesize(); 11417333Sralph op = (union overhead *)sbrk(0); 11517333Sralph n = n - sizeof (*op) - ((int)op & (n - 1)); 11617333Sralph if (n < 0) 11717333Sralph n += pagesz; 11817333Sralph if (n) { 11917333Sralph if (sbrk(n) == (char *)-1) 12017333Sralph return (NULL); 12117333Sralph } 12217333Sralph bucket = 0; 12317333Sralph amt = 8; 12417333Sralph while (pagesz > amt) { 12517333Sralph amt <<= 1; 12617333Sralph bucket++; 12717333Sralph } 12817333Sralph pagebucket = bucket; 12917333Sralph } 13014953Skarels /* 13117333Sralph * Convert amount of memory requested into closest block size 13217333Sralph * stored in hash buckets which satisfies request. 13317333Sralph * Account for space used per block for accounting. 13417333Sralph */ 13517333Sralph if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { 13617333Sralph #ifndef RCHECK 13717333Sralph amt = 8; /* size of first bucket */ 13817333Sralph bucket = 0; 13917333Sralph #else 14017333Sralph amt = 16; /* size of first bucket */ 14117333Sralph bucket = 1; 14217333Sralph #endif 14317333Sralph n = -(sizeof (*op) + RSLOP); 14417333Sralph } else { 14517333Sralph amt = pagesz; 14617333Sralph bucket = pagebucket; 14717333Sralph } 14817333Sralph while (nbytes > amt + n) { 14917333Sralph amt <<= 1; 15025793Smckusick if (amt == 0) 15125793Smckusick return (NULL); 15217333Sralph bucket++; 15317333Sralph } 15417333Sralph /* 15514953Skarels * If nothing in hash bucket right now, 15614953Skarels * request more memory from the system. 15714953Skarels */ 15817333Sralph if ((op = nextf[bucket]) == NULL) { 15914953Skarels morecore(bucket); 16017333Sralph if ((op = nextf[bucket]) == NULL) 16117333Sralph return (NULL); 16217333Sralph } 16314953Skarels /* remove from linked list */ 16417333Sralph nextf[bucket] = op->ov_next; 16517333Sralph op->ov_magic = MAGIC; 16617333Sralph op->ov_index = bucket; 16714953Skarels #ifdef MSTATS 16814953Skarels nmalloc[bucket]++; 16914953Skarels #endif 17014953Skarels #ifdef RCHECK 17114953Skarels /* 17214953Skarels * Record allocated size of block and 17314953Skarels * bound space with magic numbers. 17414953Skarels */ 17517438Sralph op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 17617333Sralph op->ov_rmagic = RMAGIC; 17717438Sralph *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 17814953Skarels #endif 17917333Sralph return ((char *)(op + 1)); 18013259Sroot } 18113259Sroot 18214953Skarels /* 18314953Skarels * Allocate more memory to the indicated bucket. 18414953Skarels */ 18514953Skarels morecore(bucket) 18617333Sralph int bucket; 18713259Sroot { 18814953Skarels register union overhead *op; 18917333Sralph register int sz; /* size of desired block */ 19025793Smckusick int amt; /* amount to allocate */ 19125793Smckusick int nblks; /* how many blocks we get */ 19213259Sroot 19325793Smckusick /* 19425793Smckusick * sbrk_size <= 0 only for big, FLUFFY, requests (about 19525793Smckusick * 2^30 bytes on a VAX, I think) or for a negative arg. 19625793Smckusick */ 19717333Sralph sz = 1 << (bucket + 3); 19825793Smckusick if (sz <= 0) 19925793Smckusick return; 20017333Sralph if (sz < pagesz) { 20117333Sralph amt = pagesz; 20217333Sralph nblks = amt / sz; 20317333Sralph } else { 20417333Sralph amt = sz + pagesz; 20517333Sralph nblks = 1; 20617333Sralph } 20717333Sralph op = (union overhead *)sbrk(amt); 20814953Skarels /* no more room! */ 20914953Skarels if ((int)op == -1) 21014953Skarels return; 21114953Skarels /* 21214953Skarels * Add new memory allocated to that on 21314953Skarels * free list for this hash bucket. 21414953Skarels */ 21514953Skarels nextf[bucket] = op; 21614953Skarels while (--nblks > 0) { 21717333Sralph op->ov_next = (union overhead *)((caddr_t)op + sz); 21817333Sralph op = (union overhead *)((caddr_t)op + sz); 21914953Skarels } 22013259Sroot } 22113259Sroot 22214953Skarels free(cp) 22314953Skarels char *cp; 22414953Skarels { 22514953Skarels register int size; 22614953Skarels register union overhead *op; 22713259Sroot 22814953Skarels if (cp == NULL) 22914953Skarels return; 23014953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 23117333Sralph #ifdef DEBUG 23214953Skarels ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ 23314953Skarels #else 23414953Skarels if (op->ov_magic != MAGIC) 23514953Skarels return; /* sanity */ 23614953Skarels #endif 23714953Skarels #ifdef RCHECK 23814953Skarels ASSERT(op->ov_rmagic == RMAGIC); 23917333Sralph ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); 24014953Skarels #endif 24114953Skarels size = op->ov_index; 24217333Sralph ASSERT(size < NBUCKETS); 24314953Skarels op->ov_next = nextf[size]; 24414953Skarels nextf[size] = op; 24514953Skarels #ifdef MSTATS 24614953Skarels nmalloc[size]--; 24714953Skarels #endif 24814953Skarels } 24914953Skarels 25014953Skarels /* 25114953Skarels * When a program attempts "storage compaction" as mentioned in the 25214953Skarels * old malloc man page, it realloc's an already freed block. Usually 25314953Skarels * this is the last block it freed; occasionally it might be farther 25414953Skarels * back. We have to search all the free lists for the block in order 25514953Skarels * to determine its bucket: 1st we make one pass thru the lists 25614953Skarels * checking only the first block in each; if that fails we search 25714953Skarels * ``realloc_srchlen'' blocks in each list for a match (the variable 25814953Skarels * is extern so the caller can modify it). If that fails we just copy 25914953Skarels * however many bytes was given to realloc() and hope it's not huge. 26014953Skarels */ 26115003Ssam int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ 26214953Skarels 26313259Sroot char * 26414953Skarels realloc(cp, nbytes) 26514953Skarels char *cp; 26614953Skarels unsigned nbytes; 26714953Skarels { 26817333Sralph register u_int onb, i; 26914953Skarels union overhead *op; 27014953Skarels char *res; 27114953Skarels int was_alloced = 0; 27214953Skarels 27314953Skarels if (cp == NULL) 27414953Skarels return (malloc(nbytes)); 27514953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 27614953Skarels if (op->ov_magic == MAGIC) { 27714953Skarels was_alloced++; 27814953Skarels i = op->ov_index; 27915003Ssam } else { 28015003Ssam /* 28115003Ssam * Already free, doing "compaction". 28215003Ssam * 28315003Ssam * Search for the old block of memory on the 28415003Ssam * free list. First, check the most common 28515003Ssam * case (last element free'd), then (this failing) 28615003Ssam * the last ``realloc_srchlen'' items free'd. 28715003Ssam * If all lookups fail, then assume the size of 28815003Ssam * the memory block being realloc'd is the 289*25799Slepreau * largest possible (so that all "nbytes" of new 290*25799Slepreau * memory are copied into). Note that this could cause 291*25799Slepreau * a memory fault if the old area was tiny, and the moon 292*25799Slepreau * is gibbous. However, that is very unlikely. 29315003Ssam */ 29414953Skarels if ((i = findbucket(op, 1)) < 0 && 29514953Skarels (i = findbucket(op, realloc_srchlen)) < 0) 296*25799Slepreau i = NBUCKETS; 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); 323*25799Slepreau if (cp != res) /* common optimization if "compacting" */ 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