121347Sdist /* 2*25177Smckusick * 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*25177Smckusick static char sccsid[] = "@(#)malloc.c 5.2 (Berkeley) 10/11/85"; 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; 14917333Sralph bucket++; 15017333Sralph } 15117333Sralph /* 15214953Skarels * If nothing in hash bucket right now, 15314953Skarels * request more memory from the system. 15414953Skarels */ 15517333Sralph if ((op = nextf[bucket]) == NULL) { 15614953Skarels morecore(bucket); 15717333Sralph if ((op = nextf[bucket]) == NULL) 15817333Sralph return (NULL); 15917333Sralph } 16014953Skarels /* remove from linked list */ 16117333Sralph nextf[bucket] = op->ov_next; 16217333Sralph op->ov_magic = MAGIC; 16317333Sralph op->ov_index = bucket; 16414953Skarels #ifdef MSTATS 16514953Skarels nmalloc[bucket]++; 16614953Skarels #endif 16714953Skarels #ifdef RCHECK 16814953Skarels /* 16914953Skarels * Record allocated size of block and 17014953Skarels * bound space with magic numbers. 17114953Skarels */ 17217438Sralph op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 17317333Sralph op->ov_rmagic = RMAGIC; 17417438Sralph *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 17514953Skarels #endif 17617333Sralph return ((char *)(op + 1)); 17713259Sroot } 17813259Sroot 17914953Skarels /* 18014953Skarels * Allocate more memory to the indicated bucket. 18114953Skarels */ 18214953Skarels static 18314953Skarels morecore(bucket) 18417333Sralph int bucket; 18513259Sroot { 18614953Skarels register union overhead *op; 18717333Sralph register int sz; /* size of desired block */ 18817333Sralph register int amt; /* amount to allocate */ 18917333Sralph register int nblks; /* how many blocks we get */ 19013259Sroot 19117333Sralph sz = 1 << (bucket + 3); 19217333Sralph if (sz < pagesz) { 19317333Sralph amt = pagesz; 19417333Sralph nblks = amt / sz; 19517333Sralph } else { 19617333Sralph amt = sz + pagesz; 19717333Sralph nblks = 1; 19817333Sralph } 19917333Sralph op = (union overhead *)sbrk(amt); 20014953Skarels /* no more room! */ 20114953Skarels if ((int)op == -1) 20214953Skarels return; 20314953Skarels /* 20414953Skarels * Add new memory allocated to that on 20514953Skarels * free list for this hash bucket. 20614953Skarels */ 20714953Skarels nextf[bucket] = op; 20814953Skarels while (--nblks > 0) { 20917333Sralph op->ov_next = (union overhead *)((caddr_t)op + sz); 21017333Sralph op = (union overhead *)((caddr_t)op + sz); 21114953Skarels } 21213259Sroot } 21313259Sroot 21414953Skarels free(cp) 21514953Skarels char *cp; 21614953Skarels { 21714953Skarels register int size; 21814953Skarels register union overhead *op; 21913259Sroot 22014953Skarels if (cp == NULL) 22114953Skarels return; 22214953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 22317333Sralph #ifdef DEBUG 22414953Skarels ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ 22514953Skarels #else 22614953Skarels if (op->ov_magic != MAGIC) 22714953Skarels return; /* sanity */ 22814953Skarels #endif 22914953Skarels #ifdef RCHECK 23014953Skarels ASSERT(op->ov_rmagic == RMAGIC); 23117333Sralph ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); 23214953Skarels #endif 23314953Skarels size = op->ov_index; 23417333Sralph ASSERT(size < NBUCKETS); 23514953Skarels op->ov_next = nextf[size]; 23614953Skarels nextf[size] = op; 23714953Skarels #ifdef MSTATS 23814953Skarels nmalloc[size]--; 23914953Skarels #endif 24014953Skarels } 24114953Skarels 24214953Skarels /* 24314953Skarels * When a program attempts "storage compaction" as mentioned in the 24414953Skarels * old malloc man page, it realloc's an already freed block. Usually 24514953Skarels * this is the last block it freed; occasionally it might be farther 24614953Skarels * back. We have to search all the free lists for the block in order 24714953Skarels * to determine its bucket: 1st we make one pass thru the lists 24814953Skarels * checking only the first block in each; if that fails we search 24914953Skarels * ``realloc_srchlen'' blocks in each list for a match (the variable 25014953Skarels * is extern so the caller can modify it). If that fails we just copy 25114953Skarels * however many bytes was given to realloc() and hope it's not huge. 25214953Skarels */ 25315003Ssam int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ 25414953Skarels 25513259Sroot char * 25614953Skarels realloc(cp, nbytes) 25714953Skarels char *cp; 25814953Skarels unsigned nbytes; 25914953Skarels { 26017333Sralph register u_int onb, i; 26114953Skarels union overhead *op; 26214953Skarels char *res; 26314953Skarels int was_alloced = 0; 26414953Skarels 26514953Skarels if (cp == NULL) 26614953Skarels return (malloc(nbytes)); 26714953Skarels op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 26814953Skarels if (op->ov_magic == MAGIC) { 26914953Skarels was_alloced++; 27014953Skarels i = op->ov_index; 27115003Ssam } else { 27215003Ssam /* 27315003Ssam * Already free, doing "compaction". 27415003Ssam * 27515003Ssam * Search for the old block of memory on the 27615003Ssam * free list. First, check the most common 27715003Ssam * case (last element free'd), then (this failing) 27815003Ssam * the last ``realloc_srchlen'' items free'd. 27915003Ssam * If all lookups fail, then assume the size of 28015003Ssam * the memory block being realloc'd is the 28115003Ssam * smallest possible. 28215003Ssam */ 28314953Skarels if ((i = findbucket(op, 1)) < 0 && 28414953Skarels (i = findbucket(op, realloc_srchlen)) < 0) 28517333Sralph #ifndef RCHECK 28615003Ssam i = 0; 28717333Sralph #else 28817333Sralph i = 1; /* smallest possible w/ RCHECK */ 28917333Sralph #endif 29014953Skarels } 29117333Sralph onb = 1 << (i + 3); 29217333Sralph if (onb < pagesz) 29317333Sralph onb -= sizeof (*op) + RSLOP; 29417333Sralph else 29517333Sralph onb += pagesz - sizeof (*op) - RSLOP; 29615003Ssam /* avoid the copy if same size block */ 29717333Sralph if (was_alloced) { 29817333Sralph if (i) { 29917333Sralph i = 1 << (i + 2); 30017333Sralph if (i < pagesz) 30117333Sralph i -= sizeof (*op) + RSLOP; 30217333Sralph else 30317333Sralph i += pagesz - sizeof (*op) - RSLOP; 30417333Sralph } 30517333Sralph if (nbytes <= onb && nbytes > i) { 30617332Sralph #ifdef RCHECK 30717438Sralph op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 30817333Sralph *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 30917332Sralph #endif 31017333Sralph return(cp); 31117333Sralph } else 31217333Sralph free(cp); 31317332Sralph } 31414953Skarels if ((res = malloc(nbytes)) == NULL) 31514953Skarels return (NULL); 31614953Skarels if (cp != res) /* common optimization */ 31714953Skarels bcopy(cp, res, (nbytes < onb) ? nbytes : onb); 31814953Skarels return (res); 31914953Skarels } 32014953Skarels 32114953Skarels /* 32214953Skarels * Search ``srchlen'' elements of each free list for a block whose 32314953Skarels * header starts at ``freep''. If srchlen is -1 search the whole list. 32414953Skarels * Return bucket number, or -1 if not found. 32514953Skarels */ 32614953Skarels static 32714953Skarels findbucket(freep, srchlen) 32815003Ssam union overhead *freep; 32915003Ssam int srchlen; 33013259Sroot { 33114953Skarels register union overhead *p; 33214953Skarels register int i, j; 33313259Sroot 33415003Ssam for (i = 0; i < NBUCKETS; i++) { 33515003Ssam j = 0; 33615003Ssam for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { 33714953Skarels if (p == freep) 33814953Skarels return (i); 33915003Ssam j++; 34015003Ssam } 34115003Ssam } 34214953Skarels return (-1); 34313259Sroot } 34413259Sroot 34514953Skarels #ifdef MSTATS 34614953Skarels /* 34714953Skarels * mstats - print out statistics about malloc 34814953Skarels * 34914953Skarels * Prints two lines of numbers, one showing the length of the free list 35014953Skarels * for each size category, the second showing the number of mallocs - 35114953Skarels * frees for each size category. 35214953Skarels */ 35314953Skarels mstats(s) 35414953Skarels char *s; 35513259Sroot { 35614953Skarels register int i, j; 35714953Skarels register union overhead *p; 35814953Skarels int totfree = 0, 35914953Skarels totused = 0; 36014953Skarels 36114953Skarels fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); 36214953Skarels for (i = 0; i < NBUCKETS; i++) { 36314953Skarels for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) 36414953Skarels ; 36514953Skarels fprintf(stderr, " %d", j); 36614953Skarels totfree += j * (1 << (i + 3)); 36714953Skarels } 36814953Skarels fprintf(stderr, "\nused:\t"); 36914953Skarels for (i = 0; i < NBUCKETS; i++) { 37014953Skarels fprintf(stderr, " %d", nmalloc[i]); 37114953Skarels totused += nmalloc[i] * (1 << (i + 3)); 37214953Skarels } 37315003Ssam fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", 37415003Ssam totused, totfree); 37513259Sroot } 37613259Sroot #endif 377