diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libbsdmalloc/common/malloc.bsd43.c | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libbsdmalloc/common/malloc.bsd43.c')
-rw-r--r-- | usr/src/lib/libbsdmalloc/common/malloc.bsd43.c | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/usr/src/lib/libbsdmalloc/common/malloc.bsd43.c b/usr/src/lib/libbsdmalloc/common/malloc.bsd43.c new file mode 100644 index 0000000000..621716a7d2 --- /dev/null +++ b/usr/src/lib/libbsdmalloc/common/malloc.bsd43.c @@ -0,0 +1,344 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" +/* + * wizard:/space/4.3reno/usr/src/lib/libc/stdlib/malloc.c + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * malloc.c (Caltech) 2/21/82 + * Chris Kingsley, kingsley@cit-20. + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks that + * don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are 2^n-4 bytes long (ILP32) + * or 2^n-8 bytes long (LP64). + */ + +/*LINTLIBRARY*/ +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> + +/* + * The overhead on a block is at least 4 bytes. When free, this space + * contains a pointer to the next free block, and the bottom two bits must + * be zero. When in use, the first byte is set to MAGIC, and the second + * byte is the size index. The remaining bytes are for alignment. + * The order of elements is critical: ov_magic must overlay the low order + * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. + * Overhead is 4 bytes for ILP32, 8 bytes for LP64 + */ +union overhead { + union overhead *ov_next; /* when free */ + struct { +#if defined(_LITTLE_ENDIAN) + uchar_t ovu_magic; /* magic number */ + uchar_t ovu_index; /* bucket # */ + uchar_t ovu_pad[sizeof (union overhead *) - 2]; +#elif defined(_BIG_ENDIAN) + uchar_t ovu_pad[sizeof (union overhead *) - 2]; + uchar_t ovu_index; /* bucket # */ + uchar_t ovu_magic; /* magic number */ +#else +#error "Endianness is not defined" +#endif + } ovu; +}; + +#define ov_magic ovu.ovu_magic +#define ov_index ovu.ovu_index + +#define MAGIC 0xef /* magic # on accounting info */ + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+EXP). + * The smallest allocatable block is 8 bytes (ILP32) or 16 bytes (LP64). + * The overhead information precedes the data area returned to the user. + */ +#ifdef _LP64 +#define EXP 4 +#define NBUCKETS 60 +#else +#define EXP 3 +#define NBUCKETS 29 +#endif +static union overhead *nextf[NBUCKETS]; + +static int pagesz; /* page size */ +static long sbrk_adjust; /* in case sbrk() does alignment */ +static int pagebucket; /* page size bucket */ +static void morecore(int); +static int findbucket(union overhead *, int); + +void * +malloc(size_t nbytes) +{ + union overhead *op; + int bucket; + ssize_t n; + size_t amt; + + /* + * First time malloc is called, setup page size and + * align break pointer so all data will be page aligned. + */ + if (pagesz == 0) { + pagesz = getpagesize(); + op = sbrk(0); + n = pagesz - sizeof (*op) - ((uintptr_t)op & (pagesz - 1)); + if (n < 0) + n += pagesz; + if (n) { + if (sbrk(n) == (void *)-1) + return (NULL); + /* + * We were careful to arrange that + * sbrk(0) + sizeof (union overhead) + * should end up on a page boundary. + * If the underlying sbrk() performs alignment + * then this is false. We compute the adjustment. + */ + op = sbrk(0); + sbrk_adjust = (uintptr_t)(op + 1) & (pagesz - 1); + } else { + sbrk_adjust = 0; + } + bucket = 0; + amt = (1UL << EXP); + while (pagesz > amt) { + amt <<= 1; + bucket++; + } + pagebucket = bucket; + } + /* + * Convert amount of memory requested into closest block size + * stored in hash buckets which satisfies request. + * Account for space used per block for accounting. + */ + if (nbytes <= (n = pagesz - sizeof (*op))) { + amt = (1UL << EXP); /* size of first bucket */ + bucket = 0; + n = -(ssize_t)(sizeof (*op)); + } else { + amt = pagesz; + bucket = pagebucket; + } + while (nbytes > amt + n) { + amt <<= 1; + if (amt == 0) + return (NULL); + bucket++; + } + /* + * If nothing in hash bucket right now, + * request more memory from the system. + */ + if ((op = nextf[bucket]) == NULL) { + morecore(bucket); + if ((op = nextf[bucket]) == NULL) + return (NULL); + } + /* remove from linked list */ + nextf[bucket] = op->ov_next; + op->ov_magic = MAGIC; + op->ov_index = (uchar_t)bucket; + return (op + 1); +} + +/* + * Allocate more memory to the indicated bucket. + */ +static void +morecore(int bucket) +{ + union overhead *op; + size_t sz; /* size of desired block */ + ssize_t amt; /* amount to allocate */ + long nblks; /* how many blocks we get */ + + sz = 1UL << (bucket + EXP); + if (sz == 0) + return; + if (sz < pagesz) { + amt = pagesz; + nblks = amt / sz; + } else { + amt = sz + pagesz; + nblks = 1; + } + if (amt <= 0) + return; + if (amt > LONG_MAX) { + intptr_t delta; + /* + * the value required is too big for sbrk() to deal with + * in one go, so use sbrk() at most 2 times instead. + */ + op = sbrk(0); + delta = LONG_MAX; + while (delta > 0) { + if (sbrk(delta) == (void *)-1) { + if (op != sbrk(0)) + (void) sbrk(-LONG_MAX); + return; + } + amt -= LONG_MAX; + delta = amt; + } + } + else + op = sbrk(amt); + /* no more room! */ + if (op == (union overhead *)-1) + return; + /* LINTED improper alignment */ + op = (union overhead *)((caddr_t)op - sbrk_adjust); + /* + * Add new memory allocated to that on + * free list for this hash bucket. + */ + nextf[bucket] = op; + while (--nblks > 0) { + /* LINTED improper alignment */ + op->ov_next = (union overhead *)((caddr_t)op + sz); + /* LINTED improper alignment */ + op = (union overhead *)((caddr_t)op + sz); + } +} + +void +free(void *cp) +{ + int size; + union overhead *op; + + if (cp == NULL) + return; + /* LINTED improper alignment */ + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); + if (op->ov_magic != MAGIC) + return; /* previously freed? */ + size = op->ov_index; + op->ov_next = nextf[size]; /* also clobbers ov_magic */ + nextf[size] = op; +} + +/* + * When a program attempts "storage compaction" as mentioned in the + * old malloc man page, it realloc's an already freed block. Usually + * this is the last block it freed; occasionally it might be farther + * back. We have to search all the free lists for the block in order + * to determine its bucket: 1st we make one pass thru the lists + * checking only the first block in each; if that fails we search + * ``realloc_srchlen'' blocks in each list for a match (the variable + * is extern so the caller can modify it). If that fails we just copy + * however many bytes was given to realloc() and hope it's not huge. + */ +int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ + +void * +realloc(void *cp, size_t nbytes) +{ + size_t onb; + int i; + union overhead *op; + char *res; + int was_alloced = 0; + + if (cp == NULL) + return (malloc(nbytes)); + /* LINTED improper alignment */ + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); + if (op->ov_magic == MAGIC) { + was_alloced++; + i = op->ov_index; + } else { + /* + * Already free, doing "compaction". + * + * Search for the old block of memory on the + * free list. First, check the most common + * case (last element free'd), then (this failing) + * the last ``realloc_srchlen'' items free'd. + * If all lookups fail, then just malloc() the + * space and copy the size of the new space. + */ + if ((i = findbucket(op, 1)) < 0 && + (i = findbucket(op, realloc_srchlen)) < 0) { + if ((res = malloc(nbytes)) != NULL) + (void) memmove(res, cp, nbytes); + return (res); + } + } + onb = 1UL << (i + EXP); + if (onb < pagesz) + onb -= sizeof (*op); + else + onb += pagesz - sizeof (*op); + /* avoid the copy if same size block */ + if (was_alloced) { + size_t sz = 0; + if (i) { + sz = 1UL << (i + EXP - 1); + if (sz < pagesz) + sz -= sizeof (*op); + else + sz += pagesz - sizeof (*op); + } + if (nbytes <= onb && nbytes > sz) { + return (cp); + } else + free(cp); + } + if ((res = malloc(nbytes)) == NULL) + return (NULL); + if (cp != res) /* common optimization if "compacting" */ + (void) memmove(res, cp, (nbytes < onb) ? nbytes : onb); + return (res); +} + +/* + * Search ``srchlen'' elements of each free list for a block whose + * header starts at ``freep''. If srchlen is -1 search the whole list. + * Return bucket number, or -1 if not found. + */ +static int +findbucket(union overhead *freep, int srchlen) +{ + union overhead *p; + int i, j; + + for (i = 0; i < NBUCKETS; i++) { + j = 0; + for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { + if (p == freep) + return (i); + j++; + } + } + return (-1); +} |