/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1986 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* file: malloc.c
* description:
* Yet another memory allocator, this one based on a method
* described in C.J. Stephenson, "Fast Fits"
*
* The basic data structure is a "Cartesian" binary tree, in which
* nodes are ordered by ascending addresses (thus minimizing free
* list insertion time) and block sizes decrease with depth in the
* tree (thus minimizing search time for a block of a given size).
*
* In other words: for any node s, let D(s) denote the set of
* descendents of s; for all x in D(left(s)) and all y in
* D(right(s)), we have:
*
* a. addr(x) < addr(s) < addr(y)
* b. len(x) <= len(s) >= len(y)
*/
#include "mallint.h"
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
/* system interface */
extern char *sbrk();
extern int getpagesize();
static int nbpg = 0; /* set by calling getpagesize() */
static bool morecore(uint); /* get more memory into free space */
#ifdef S5EMUL
#define ptr_t void * /* ANSI C says these are voids */
#define free_t void /* ANSI says void free(ptr_t ptr) */
#define free_return(x) return
#else
#define ptr_t char * /* BSD still (4.3) wants char*'s */
#define free_t int /* BSD says int free(ptr_t ptr) */
#define free_return(x) return(x)
#endif
/* SystemV-compatible information structure */
#define INIT_MXFAST 0
#define INIT_NLBLKS 100
#define INIT_GRAIN ALIGNSIZ
struct mallinfo __mallinfo = {
0,0,0,0,0,0,0,0,0,0, /* basic info */
INIT_MXFAST, INIT_NLBLKS, INIT_GRAIN, /* mallopt options */
0,0,0
};
/* heap data structures */
Freehdr _root = NIL; /* root of free space list */
char *_lbound = NULL; /* lower bound of heap */
char *_ubound = NULL; /* upper bound of heap */
/* free header list management */
static Freehdr getfreehdr(void);
static void putfreehdr(Freehdr);
static Freehdr freehdrptr = NIL; /* ptr to block of available headers */
static int nfreehdrs = 0; /* # of headers in current block */
static Freehdr freehdrlist = NIL; /* List of available headers */
/* error checking */
static void error(char *, ...);
/* sets errno; prints msg and aborts if DEBUG is on */
static int reclaim(Dblk, uint, int);
#ifdef DEBUG /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
int malloc_debug(int);
int malloc_verify(void);
static int debug_level = 1;
/*
* A block with a negative size, a size that is not a multiple
* of ALIGNSIZ, a size greater than the current extent of the
* heap, or a size which extends beyond the end of the heap is
* considered bad.
*/
#define badblksize(p,size)\
( (size) < SMALLEST_BLK \
|| (size) & (ALIGNSIZ-1) \
|| (size) > heapsize() \
|| ((char*)(p))+(size) > _ubound )
#else /* !DEBUG ================================================= */
#define malloc_debug(level) 0
#define malloc_verify() 1
#define debug_level 0
#define badblksize(p,size) 0
#endif /* !DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
/*
* insert (newblk, len)
* Inserts a new node in the free space tree, placing it
* in the correct position with respect to the existing nodes.
*
* algorithm:
* Starting from the root, a binary search is made for the new
* node. If this search were allowed to continue, it would
* eventually fail (since there cannot already be a node at the
* given address); but in fact it stops when it reaches a node in
* the tree which has a length less than that of the new node (or
* when it reaches a null tree pointer).
*
* The new node is then inserted at the root of the subtree for
* which the shorter node forms the old root (or in place of the
* null pointer).
*
* Arguments
* newblk: Ptr to the block to insert
* len: Length of new node
*/
static void
insert(Dblk newblk, uint len)
{
Freehdr *fpp; /* Address of ptr to subtree */
Freehdr x;
Freehdr *left_hook; /* Temp for insertion */
Freehdr *right_hook; /* Temp for insertion */
Freehdr newhdr;
/*
* check for bad block size.
*/
if ( badblksize(newblk,len) ) {
error("insert: bad block size (%d) at %#x\n", len, newblk);
return;
}
/*
* Search for the first node which has a weight less
* than that of the new node; this will be the
* point at which we insert the new node.
*/
fpp = &_root;
x = *fpp;
while (weight(x) >= len) {
if (newblk < x->block)
fpp = &x->left;
else
fpp = &x->right;
x = *fpp;
}
/*
* Perform root insertion. The variable x traces a path through
* the fpp, and with the help of left_hook and right_hook,
* rewrites all links that cross the territory occupied
* by newblk.
*/
if ((newhdr = getfreehdr()) == NIL) {
/* Error message returned by getfreehdr() */
return;
}
*fpp = newhdr;
newhdr->left = NIL;
newhdr->right = NIL;
newhdr->block = newblk;
newhdr->size = len;
/*
* set length word in the block for consistency with the header.
*/
newblk->size = len;
left_hook = &newhdr->left;
right_hook = &newhdr->right;
while (x != NIL) {
/*
* Remark:
* The name 'left_hook' is somewhat confusing, since
* it is always set to the address of a .right link
* field. However, its value is always an address
* below (i.e., to the left of) newblk. Similarly
* for right_hook. The values of left_hook and
* right_hook converge toward the value of newblk,
* as in a classical binary search.
*/
if (x->block < newblk) {
/*
* rewrite link crossing from the left
*/
*left_hook = x;
left_hook = &x->right;
x = x->right;
} else {
/*
* rewrite link crossing from the right
*/
*right_hook = x;
right_hook = &x->left;
x = x->left;
} /*else*/
} /*while*/
*left_hook = *right_hook = NIL; /* clear remaining hooks */
} /*insert*/
/*
* delete(p)
* deletes a node from a cartesian tree. p is the address of
* a pointer to the node which is to be deleted.
*
* algorithm:
* The left and right branches of the node to be deleted define two
* subtrees which are to be merged and attached in place of the
* deleted node. Each node on the inside edges of these two
* subtrees is examined and longer nodes are placed above the
* shorter ones.
*
* On entry:
* *p is assumed to be non-null.
*/
static void
delete(Freehdr *p)
{
Freehdr x;
Freehdr left_branch; /* left subtree of deleted node */
Freehdr right_branch; /* right subtree of deleted node */
uint left_weight;
uint right_weight;
x = *p;
left_branch = x->left;
left_weight = weight(left_branch);
right_branch = x->right;
right_weight = weight(right_branch);
while (left_branch != right_branch) {
/*
* iterate until left branch and right branch are
* both NIL.
*/
if ( left_weight >= right_weight ) {
/*
* promote the left branch
*/
if (left_branch != NIL) {
if (left_weight == 0) {
/* zero-length block */
error("blocksize=0 at %#x\n",
(int)left_branch->block->data);
break;
}
*p = left_branch;
p = &left_branch->right;
left_branch = *p;
left_weight = weight(left_branch);
}
} else {
/*
* promote the right branch
*/
if (right_branch != NIL) {
if (right_weight == 0) {
/* zero-length block */
error("blocksize=0 at %#x\n",
(int)right_branch->block->data);
break;
}
*p = right_branch;
p = &right_branch->left;
right_branch = *p;
right_weight = weight(right_branch);
}
}/*else*/
}/*while*/
*p = NIL;
putfreehdr(x);
} /*delete*/
/*
* demote(p)
* Demotes a node in a cartesian tree, if necessary, to establish
* the required vertical ordering.
*
* algorithm:
* The left and right subtrees of the node to be demoted are to
* be partially merged and attached in place of the demoted node.
* The nodes on the inside edges of these two subtrees are
* examined and the longer nodes are placed above the shorter
* ones, until a node is reached which has a length no greater
* than that of the node being demoted (or until a null pointer
* is reached). The node is then attached at this point, and
* the remaining subtrees (if any) become its descendants.
*
* on entry:
* a. All the nodes in the tree, including the one to be demoted,
* must be correctly ordered horizontally;
* b. All the nodes except the one to be demoted must also be
* correctly positioned vertically. The node to be demoted
* may be already correctly positioned vertically, or it may
* have a length which is less than that of one or both of
* its progeny.
* c. *p is non-null
*/
static void
demote(Freehdr *p)
{
Freehdr x; /* addr of node to be demoted */
Freehdr left_branch;
Freehdr right_branch;
uint left_weight;
uint right_weight;
uint x_weight;
x = *p;
x_weight = weight(x);
left_branch = x->left;
right_branch = x->right;
left_weight = weight(left_branch);
right_weight = weight(right_branch);
while (left_weight > x_weight || right_weight > x_weight) {
/*
* select a descendant branch for promotion
*/
if (left_weight >= right_weight) {
/*
* promote the left branch
*/
*p = left_branch;
p = &left_branch->right;
left_branch = *p;
left_weight = weight(left_branch);
} else {
/*
* promote the right branch
*/
*p = right_branch;
p = &right_branch->left;
right_branch = *p;
right_weight = weight(right_branch);
} /*else*/
} /*while*/
*p = x; /* attach demoted node here */
x->left = left_branch;
x->right = right_branch;
} /*demote*/
/*
* char*
* malloc(nbytes)
* Allocates a block of length specified in bytes. If nbytes is
* zero, a valid pointer (that should not be dereferenced) is returned.
*
* algorithm:
* The freelist is searched by descending the tree from the root
* so that at each decision point the "better fitting" branch node
* is chosen (i.e., the shorter one, if it is long enough, or
* the longer one, otherwise). The descent stops when both
* branch nodes are too short.
*
* function result:
* Malloc returns a pointer to the allocated block. A null
* pointer indicates an error.
*
* diagnostics:
*
* ENOMEM: storage could not be allocated.
*
* EINVAL: either the argument was invalid, or the heap was found
* to be in an inconsistent state. More detailed information may
* be obtained by enabling range checks (cf., malloc_debug()).
*
* Note: In this implementation, each allocated block includes a
* length word, which occurs before the address seen by the caller.
* Allocation requests are rounded up to a multiple of wordsize.
*/
ptr_t
malloc(uint nbytes)
{
Freehdr allocp; /* ptr to node to be allocated */
Freehdr *fpp; /* for tree modifications */
Freehdr left_branch;
Freehdr right_branch;
uint left_weight;
uint right_weight;
Dblk retblk; /* block returned to the user */
/*
* if rigorous checking was requested, do it.
*/
if (debug_level >= 2) {
malloc_verify();
}
/*
* add the size of a length word to the request, and
* guarantee at least one word of usable data.
*/
nbytes += ALIGNSIZ;
if (nbytes < SMALLEST_BLK) {
nbytes = SMALLEST_BLK;
} else {
nbytes = roundup(nbytes, ALIGNSIZ);
}
/*
* ensure that at least one block is big enough to satisfy
* the request.
*/
if (weight(_root) < nbytes) {
/*
* the largest block is not enough.
*/
if(!morecore(nbytes))
return 0;
}
/*
* search down through the tree until a suitable block is
* found. At each decision point, select the better
* fitting node.
*/
fpp = &_root;
allocp = *fpp;
left_branch = allocp->left;
right_branch = allocp->right;
left_weight = weight(left_branch);
right_weight = weight(right_branch);
while (left_weight >= nbytes || right_weight >= nbytes) {
if (left_weight <= right_weight) {
if (left_weight >= nbytes) {
fpp = &allocp->left;
allocp = left_branch;
} else {
fpp = &allocp->right;
allocp = right_branch;
}
} else {
if (right_weight >= nbytes) {
fpp = &allocp->right;
allocp = right_branch;
} else {
fpp = &allocp->left;
allocp = left_branch;
}
}
left_branch = allocp->left;
right_branch = allocp->right;
left_weight = weight(left_branch);
right_weight = weight(right_branch);
} /*while*/
/*
* allocate storage from the selected node.
*/
if (allocp->size - nbytes <= SMALLEST_BLK) {
/*
* not big enough to split; must leave at least
* a dblk's worth of space.
*/
retblk = allocp->block;
delete(fpp);
} else {
/*
* Split the selected block n bytes from the top. The
* n bytes at the top are returned to the caller; the
* remainder of the block goes back to free space.
*/
Dblk nblk;
retblk = allocp->block;
nblk = nextblk(retblk, nbytes); /* ^next block */
nblk->size = allocp->size = retblk->size - nbytes;
__mallinfo.ordblks++; /* count fragments */
/*
* Change the selected node to point at the newly split
* block, and move the node to its proper place in
* the free space list.
*/
allocp->block = nblk;
demote(fpp);
/*
* set the length field of the allocated block; we need
* this because free() does not specify a length.
*/
retblk->size = nbytes;
}
/* maintain statistics */
__mallinfo.uordbytes += retblk->size; /* bytes allocated */
__mallinfo.allocated++; /* frags allocated */
if (nbytes < __mallinfo.mxfast)
__mallinfo.smblks++; /* kludge to pass the SVVS */
return((ptr_t)retblk->data);
} /*malloc*/
/*
* free(p)
* return a block to the free space tree.
*
* algorithm:
* Starting at the root, search for and coalesce free blocks
* adjacent to one given. When the appropriate place in the
* tree is found, insert the given block.
*
* Some sanity checks to avoid total confusion in the tree.
* If the block has already been freed, return.
* If the ptr is not from the sbrk'ed space, return.
* If the block size is invalid, return.
*/
free_t
free(ptr_t ptr)
{
uint nbytes; /* Size of node to be released */
Freehdr *fpp; /* For deletion from free list */
Freehdr neighbor; /* Node to be coalesced */
Dblk neighbor_blk; /* Ptr to potential neighbor */
uint neighbor_size; /* Size of potential neighbor */
Dblk oldblk; /* Ptr to block to be freed */
/*
* if rigorous checking was requested, do it.
*/
if (debug_level >= 2) {
malloc_verify();
}
/*
* Check the address of the old block.
*/
if ( misaligned(ptr) ) {
error("free: illegal address (%#x)\n", ptr);
free_return(0);
}
/*
* Freeing something that wasn't allocated isn't
* exactly kosher, but fclose() does it routinely.
*/
if( ptr < (ptr_t)_lbound || ptr > (ptr_t)_ubound ) {
errno = EINVAL;
free_return(0);
}
/*
* Get node length by backing up by the size of a header.
* Check for a valid length. It must be a positive
* multiple of ALIGNSIZ, at least as large as SMALLEST_BLK,
* no larger than the extent of the heap, and must not
* extend beyond the end of the heap.
*/
oldblk = (Dblk)((char*)ptr - ALIGNSIZ);
nbytes = oldblk->size;
if (badblksize(oldblk,nbytes)) {
error("free: bad block size (%d) at %#x\n",
(int)nbytes, (int)oldblk );
free_return(0);
}
/* maintain statistics */
__mallinfo.uordbytes -= nbytes; /* bytes allocated */
__mallinfo.allocated--; /* frags allocated */
/*
* Search the tree for the correct insertion point for this
* node, coalescing adjacent free blocks along the way.
*/
fpp = &_root;
neighbor = *fpp;
while (neighbor != NIL) {
neighbor_blk = neighbor->block;
neighbor_size = neighbor->size;
if (oldblk < neighbor_blk) {
Dblk nblk = nextblk(oldblk,nbytes);
if (nblk == neighbor_blk) {
/*
* Absorb and delete right neighbor
*/
nbytes += neighbor_size;
__mallinfo.ordblks--;
delete(fpp);
} else if (nblk > neighbor_blk) {
/*
* The block being freed overlaps
* another block in the tree. This
* is bad news. Return to avoid
* further fouling up the the tree.
*/
error("free: blocks %#x, %#x overlap\n",
(int)oldblk, (int)neighbor_blk);
free_return(0);
} else {
/*
* Search to the left
*/
fpp = &neighbor->left;
}
} else if (oldblk > neighbor_blk) {
Dblk nblk = nextblk(neighbor_blk, neighbor_size);
if (nblk == oldblk) {
/*
* Absorb and delete left neighbor
*/
oldblk = neighbor_blk;
nbytes += neighbor_size;
__mallinfo.ordblks--;
delete(fpp);
} else if (nblk > oldblk) {
/*
* This block has already been freed
*/
error("free: block %#x was already free\n",
(int)ptr);
free_return(0);
} else {
/*
* search to the right
*/
fpp = &neighbor->right;
}
} else {
/*
* This block has already been freed
* as "oldblk == neighbor_blk"
*/
error("free: block %#x was already free\n", (int)ptr);
free_return(0);
} /*else*/
/*
* Note that this depends on a side effect of
* delete(fpp) in order to terminate the loop!
*/
neighbor = *fpp;
} /*while*/
/*
* Insert the new node into the free space tree
*/
insert( oldblk, nbytes );
free_return(1);
} /*free*/
/*
* char*
* shrink(oldblk, oldsize, newsize)
* Decreases the size of an old block to a new size.
* Returns the remainder to free space. Returns the
* truncated block to the caller.
*/
static char *
shrink(Dblk oldblk, uint oldsize, uint newsize)
{
Dblk remainder;
if (oldsize - newsize >= SMALLEST_BLK) {
/*
* Block is to be contracted. Split the old block
* and return the remainder to free space.
*/
remainder = nextblk(oldblk, newsize);
remainder->size = oldsize - newsize;
oldblk->size = newsize;
/* maintain statistics */
__mallinfo.ordblks++; /* count fragments */
__mallinfo.allocated++; /* negate effect of free() */
free(remainder->data);
}
return(oldblk->data);
}
/*
* char*
* realloc(ptr, nbytes)
*
* Reallocate an old block with a new size, returning the old block
* if possible. The block returned is guaranteed to preserve the
* contents of the old block up to min(size(old block), newsize).
*
* For backwards compatibility, ptr is allowed to reference
* a block freed since the LAST call of malloc(). Thus the old
* block may be busy, free, or may even be nested within a free
* block.
*
* Some old programs have been known to do things like the following,
* which is guaranteed not to work:
*
* free(ptr);
* free(dummy);
* dummy = malloc(1);
* ptr = realloc(ptr,nbytes);
*
* This atrocity was found in the source for diff(1).
*/
ptr_t
realloc(ptr_t ptr, uint nbytes)
{
Freehdr *fpp;
Freehdr fp;
Dblk oldblk;
Dblk freeblk;
Dblk oldneighbor;
uint oldsize;
uint newsize;
uint oldneighborsize;
/*
* Add SVR4 semantics for OS 5.x so /usr/lib librarys
* work correctly when running in BCP mode
*/
if (ptr == NULL) {
return (malloc(nbytes));
}
/*
* if rigorous checking was requested, do it.
*/
if (debug_level >= 2) {
malloc_verify();
}
/*
* Check the address of the old block.
*/
if ( misaligned(ptr) ||
ptr < (ptr_t)_lbound ||
ptr > (ptr_t)_ubound ) {
error("realloc: illegal address (%#x)\n", ptr);
return(NULL);
}
/*
* check location and size of the old block and its
* neighboring block to the right. If the old block is
* at end of memory, the neighboring block is undefined.
*/
oldblk = (Dblk)((char*)ptr - ALIGNSIZ);
oldsize = oldblk->size;
if (badblksize(oldblk,oldsize)) {
error("realloc: bad block size (%d) at %#x\n",
oldsize, oldblk);
return(NULL);
}
oldneighbor = nextblk(oldblk,oldsize);
/* *** tree search code pulled into separate subroutine *** */
if (reclaim(oldblk, oldsize, 1) == -1) {
return(NULL); /* internal error */
}
/*
* At this point, we can guarantee that oldblk is out of free
* space. What we do next depends on a comparison of the size
* of the old block and the requested new block size. To do
* this, first round up the new size request.
*/
newsize = nbytes + ALIGNSIZ; /* add size of a length word */
if (newsize < SMALLEST_BLK) {
newsize = SMALLEST_BLK;
} else {
newsize = roundup(newsize, ALIGNSIZ);
}
/*
* Next, examine the size of the old block, and compare it
* with the requested new size.
*/
if (oldsize >= newsize) {
/*
* Block is to be made smaller.
*/
return(shrink(oldblk, oldsize, newsize));
}
/*
* Block is to be expanded. Look for adjacent free memory.
*/
if ( oldneighbor < (Dblk)_ubound ) {
/*
* Search for the adjacent block in the free
* space tree. Note that the tree may have been
* modified in the earlier loop.
*/
fpp = &_root;
fp = *fpp;
oldneighborsize = oldneighbor->size;
if ( badblksize(oldneighbor, oldneighborsize) ) {
error("realloc: bad blocksize(%d) at %#x\n",
oldneighborsize, oldneighbor);
return(NULL);
}
while ( weight(fp) >= oldneighborsize ) {
freeblk = fp->block;
if (oldneighbor < freeblk) {
/*
* search to the left
*/
fpp = &(fp->left);
fp = *fpp;
}
else if (oldneighbor > freeblk) {
/*
* search to the right
*/
fpp = &(fp->right);
fp = *fpp;
}
else { /* oldneighbor == freeblk */
/*
* neighboring block is free; is it big enough?
*/
if (oldsize + oldneighborsize >= newsize) {
/*
* Big enough. Delete freeblk, join
* oldblk to neighbor, return newsize
* bytes to the caller, and return the
* remainder to free storage.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.ordblks--;
__mallinfo.uordbytes += oldneighborsize;
oldsize += oldneighborsize;
oldblk->size = oldsize;
return(shrink(oldblk, oldsize, newsize));
} else {
/*
* Not big enough. Stop looking for a
* free lunch.
*/
break;
} /*else*/
} /*else*/
}/*while*/
} /*if*/
/*
* At this point, we know there is no free space in which to
* expand. Malloc a new block, copy the old block to the new,
* and free the old block, IN THAT ORDER.
*/
ptr = malloc(nbytes);
if (ptr != NULL) {
bcopy(oldblk->data, ptr, oldsize-ALIGNSIZ);
free(oldblk->data);
}
return(ptr);
} /* realloc */
/*
* *** The following code was pulled out of realloc() ***
*
* int
* reclaim(oldblk, oldsize, flag)
* If a block containing 'oldsize' bytes from 'oldblk'
* is in the free list, remove it from the free list.
* 'oldblk' and 'oldsize' are assumed to include the free block header.
*
* Returns 1 if block was successfully removed.
* Returns 0 if block was not in free list.
* Returns -1 if block spans a free/allocated boundary (error() called
* if 'flag' == 1).
*/
static int
reclaim(Dblk oldblk, uint oldsize, int flag)
{
Dblk oldneighbor;
Freehdr *fpp;
Freehdr fp;
Dblk freeblk;
uint size;
/*
* Search the free space list for a node describing oldblk,
* or a node describing a block containing oldblk. Assuming
* the size of blocks decreases monotonically with depth in
* the tree, the loop may terminate as soon as a block smaller
* than oldblk is encountered.
*/
oldneighbor = nextblk(oldblk, oldsize);
fpp = &_root;
fp = *fpp;
while ( (size = weight(fp)) >= oldsize ) {
freeblk = fp->block;
if (badblksize(freeblk,size)) {
error("realloc: bad block size (%d) at %#x\n",
size, freeblk);
return(-1);
}
if ( oldblk == freeblk ) {
/*
* |<-- freeblk ...
* _________________________________
* |<-- oldblk ...
* ---------------------------------
* Found oldblk in the free space tree; delete it.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.uordbytes += oldsize;
__mallinfo.allocated++;
return(1);
}
else if (oldblk < freeblk) {
/*
* |<-- freeblk ...
* _________________________________
* |<--oldblk ...
* ---------------------------------
* Search to the left for oldblk
*/
fpp = &fp->left;
fp = *fpp;
}
else {
/*
* |<-- freeblk ...
* _________________________________
* | |<--oldblk--->|<--oldneighbor
* ---------------------------------
* oldblk is somewhere to the right of freeblk.
* Check to see if it lies within freeblk.
*/
Dblk freeneighbor;
freeneighbor = nextblk(freeblk, freeblk->size);
if (oldblk >= freeneighbor) {
/*
* |<-- freeblk--->|<--- freeneighbor ...
* _________________________________
* | |<--oldblk--->|
* ---------------------------------
* no such luck; search to the right.
*/
fpp = &fp->right;
fp = *fpp;
}
else {
/*
* freeblk < oldblk < freeneighbor;
* i.e., oldblk begins within freeblk.
*/
if (oldneighbor > freeneighbor) {
/*
* |<-- freeblk--->|<--- freeneighbor
* _________________________________
* | |<--oldblk--->|<--oldneighbor
* ---------------------------------
* oldblk straddles a block boundary!
*/
if (flag) {
error("realloc: block %#x straddles free block boundary\n", oldblk);
}
return(-1);
}
else if ( oldneighbor == freeneighbor ) {
/*
* |<-------- freeblk------------->|
* _________________________________
* | |<--oldblk--->|
* ---------------------------------
* Oldblk is on the right end of
* freeblk. Delete freeblk, split
* into two fragments, and return
* the one on the left to free space.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.ordblks++;
__mallinfo.uordbytes += oldsize;
__mallinfo.allocated += 2;
freeblk->size -= oldsize;
free(freeblk->data);
return(1);
}
else {
/*
* |<-------- freeblk------------->|
* _________________________________
* | |oldblk | oldneighbor |
* ---------------------------------
* Oldblk is in the middle of freeblk.
* Delete freeblk, split into three
* fragments, and return the ones on
* the ends to free space.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.ordblks += 2;
__mallinfo.uordbytes += freeblk->size;
__mallinfo.allocated += 3;
/*
* split the left fragment by
* subtracting the size of oldblk
* and oldblk's neighbor
*/
freeblk->size -=
( (char*)freeneighbor
- (char*)oldblk );
/*
* split the right fragment by
* setting oldblk's neighbor's size
*/
oldneighbor->size =
(char*)freeneighbor
- (char*)oldneighbor;
/*
* return the fragments to free space
*/
free(freeblk->data);
free(oldneighbor->data);
return(1);
} /*else*/
} /*else*/
} /* else */
} /*while*/
return(0); /* free block not found */
}
/*
* bool
* morecore(nbytes)
* Add a block of at least nbytes from end-of-memory to the
* free space tree.
*
* return value:
* true if at least n bytes can be allocated
* false otherwise
*
* remarks:
*
* -- free space (delimited by the extern variable _ubound) is
* extended by an amount determined by rounding nbytes up to
* a multiple of the system page size.
*
* -- The lower bound of the heap is determined the first time
* this routine is entered. It does NOT necessarily begin at
* the end of static data space, since startup code (e.g., for
* profiling) may have invoked sbrk() before we got here.
*/
static bool
morecore(uint nbytes)
{
Dblk p;
Freehdr newhdr;
if (nbpg == 0) {
nbpg = getpagesize();
/* hack to avoid fragmenting the heap with the first
freehdr page */
if ((newhdr = getfreehdr()) == NIL) {
/* Error message returned by getfreehdr() */
return(false);
}
(void)putfreehdr(newhdr);
}
nbytes = roundup(nbytes, nbpg);
p = (Dblk) sbrk((int)nbytes);
if (p == (Dblk) -1) {
if (errno == EAGAIN) errno = ENOMEM;
return(false); /* errno = ENOMEM */
}
if (_lbound == NULL) /* set _lbound the first time through */
_lbound = (char*) p;
_ubound = (char *) p + nbytes;
p->size = nbytes;
/* maintain statistics */
__mallinfo.arena = _ubound - _lbound;
__mallinfo.uordbytes += nbytes;
__mallinfo.ordblks++;
__mallinfo.allocated++;
free(p->data);
return(true);
} /*morecore*/
/*
* Get a free block header from the free header list.
* When the list is empty, allocate an array of headers.
* When the array is empty, allocate another one.
* When we can't allocate another array, we're in deep weeds.
*/
static Freehdr
getfreehdr(void)
{
Freehdr r;
Dblk blk;
uint size;
if (freehdrlist != NIL) {
r = freehdrlist;
freehdrlist = freehdrlist->left;
return(r);
}
if (nfreehdrs <= 0) {
size = NFREE_HDRS*sizeof(struct freehdr) + ALIGNSIZ;
blk = (Dblk) sbrk(size);
if ((int)blk == -1) {
malloc_debug(1);
error("getfreehdr: out of memory");
if (errno == EAGAIN) errno = ENOMEM;
return(NIL);
}
if (_lbound == NULL) /* set _lbound on first allocation */
_lbound = (char*)blk;
blk->size = size;
freehdrptr = (Freehdr)blk->data;
nfreehdrs = NFREE_HDRS;
_ubound = (char*) nextblk(blk,size);
/* maintain statistics */
__mallinfo.arena = _ubound - _lbound;
__mallinfo.treeoverhead += size;
}
nfreehdrs--;
return(freehdrptr++);
}
/*
* Free a free block header
* Add it to the list of available headers.
*/
static void
putfreehdr(Freehdr p)
{
p->left = freehdrlist;
freehdrlist = p;
}
#ifndef DEBUG /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
/*
* stubs for error handling and diagnosis routines. These are what
* you get in the standard C library; for non-placebo diagnostics
* load /usr/lib/malloc.debug.o with your program.
*/
/*ARGSUSED*/
static void
error(char *fmt, ...)
{
errno = EINVAL;
}
#endif /* !DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
#ifdef DEBUG /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
/*
* malloc_debug(level)
*
* description:
*
* Controls the level of error diagnosis and consistency checking
* done by malloc() and free(). level is interpreted as follows:
*
* 0: malloc() and free() return 0 if error detected in arguments
* (errno is set to EINVAL)
* 1: malloc() and free() abort if errors detected in arguments
* 2: same as 1, but scan entire heap for errors on every call
* to malloc() or free()
*
* function result:
* returns the previous level of error reporting.
*/
int
malloc_debug(int level)
{
int old_level;
old_level = debug_level;
debug_level = level;
return (old_level);
}
/*
* check a free space tree pointer. Should be in
* the static free pool or somewhere in the heap.
*/
#define chkblk(p)\
if ( misaligned(p)\
|| ((Dblk)(p) < (Dblk)_lbound || (Dblk)(p) > (Dblk)_ubound)){\
blkerror(p);\
return 0;\
}
#define chkhdr(p) chkblk(p)
static
blkerror(Freehdr p)
{
error("Illegal block address (%#x)\n", (p));
}
/*
* cartesian(p)
* returns 1 if free space tree p satisfies internal consistency
* checks.
*/
static int
cartesian(Freehdr p)
{
Freehdr probe;
Dblk db,pdb;
if (p == NIL) /* no tree to test */
return 1;
/*
* check that root has a data block
*/
chkhdr(p);
pdb = p->block;
chkblk(pdb);
/*
* check that the child blocks are no larger than the parent block.
*/
probe = p->left;
if (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if (probe->size > p->size) /* child larger than parent */
return 0;
}
probe = p->right;
if (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if (probe->size > p->size) /* child larger than parent */
return 0;
}
/*
* test data addresses in the left subtree,
* starting at the left subroot and probing to
* the right. All data addresses must be < p->block.
*/
probe = p->left;
while (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if ( nextblk(db, probe->size) >= pdb ) /* overlap */
return 0;
probe = probe->right;
}
/*
* test data addresses in the right subtree,
* starting at the right subroot and probing to
* the left. All addresses must be > nextblk(p->block).
*/
pdb = nextblk(pdb, p->size);
probe = p->right;
while (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if (db == NULL || db <= pdb) /* overlap */
return 0;
probe = probe->left;
}
return (cartesian(p->left) && cartesian(p->right));
}
/*
* malloc_verify()
*
* This is a verification routine. It walks through all blocks
* in the heap (both free and busy) and checks for bad blocks.
* malloc_verify returns 1 if the heap contains no detectably bad
* blocks; otherwise it returns 0.
*/
int
malloc_verify(void)
{
int maxsize;
int hdrsize;
int size;
Dblk p;
uint lb,ub;
extern char end[];
if (_lbound == NULL) /* no allocation yet */
return 1;
/*
* first check heap bounds pointers
*/
lb = (uint)end;
ub = (uint)sbrk(0);
if ((uint)_lbound < lb || (uint)_lbound > ub) {
error("malloc_verify: illegal heap lower bound (%#x)\n",
_lbound);
return 0;
}
if ((uint)_ubound < lb || (uint)_ubound > ub) {
error("malloc_verify: illegal heap upper bound (%#x)\n",
_ubound);
return 0;
}
maxsize = heapsize();
p = (Dblk)_lbound;
while (p < (Dblk) _ubound) {
size = p->size;
if ( (size) < SMALLEST_BLK
|| (size) & (ALIGNSIZ-1)
|| (size) > heapsize()
|| ((char*)(p))+(size) > _ubound ) {
error("malloc_verify: bad block size (%d) at %#x\n",
size, p);
return(0); /* Badness */
}
p = nextblk(p, size);
}
if (p > (Dblk) _ubound) {
error("malloc_verify: heap corrupted\n");
return(0);
}
if (!cartesian(_root)){
error("malloc_verify: free space tree corrupted\n");
return(0);
}
return(1);
}
/*
* The following is a kludge to avoid dependency on stdio, which
* uses malloc() and free(), one of which probably got us here in
* the first place.
*/
#define putchar(c) (*buf++ = (c))
extern int fileno(); /*bletch*/
#define stderr 2 /*bletch*/
#define LBUFSIZ 256
static char stderrbuf[LBUFSIZ];
/*
* Error routine.
* If debug_level == 0, does nothing except set errno = EINVAL.
* Otherwise, prints an error message to stderr and generates a
* core image.
*/
static void
error(char *fmt, ...)
{
static int n = 0; /* prevents infinite recursion when using stdio */
int nbytes;
va_list ap;
errno = EINVAL;
if (debug_level == 0)
return;
if (!n++) {
va_start(ap, fmt);
nbytes = vsprintf(stderrbuf, fmt, ap);
va_end(ap);
stderrbuf[nbytes++] = '\n';
stderrbuf[nbytes] = '\0';
write(fileno(stderr), stderrbuf, nbytes);
}
abort();
}
#endif /* DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */