summaryrefslogtreecommitdiff
path: root/usr/src/lib/libbsdmalloc/common/malloc.bsd43.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libbsdmalloc/common/malloc.bsd43.c
downloadillumos-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.c344
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);
+}