/*
*
* Copyright 1994 FirmWorks, Mountain View CA USA. All rights reserved.
* Copyright (c) 1995 FirePower Systems, Inc.
*
* $RCSfile: vrmalloc.c $
* $Revision: 1.6 $
* $Date: 1996/02/17 00:41:29 $
* $Locker: $
*
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* A "smarter" malloc William L. Sebok
* Sept. 24, 1984
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* If n = the size of an area rounded DOWN to the nearest power of two,
* all free areas of memory whose length is the same index n is organized
* into a chain with other free areas of index n. A request for memory
* takes the first item in the chain whose index is the size of the
* request rounded UP to the nearest power of two. If this chain is
* empty the next higher chain is examined. If no larger chain has memory
* then new memory is allocated. Only the amount of new memory needed is
* allocated. Any old free memory left after an allocation is returned
* to the free list. Extra new memory returned because of rounding
* to page boundaries is returned to free list.
*
* All memory areas (free or busy) handled by malloc are also chained
* sequentially by increasing address. When memory is freed it is
* merged with adjacent free areas, if any. If a free area of memory
* ends at the end of memory (i.e. at the break), the break is
* contracted, freeing the memory back to the system.
*
* Notes:
* ov_length field includes sizeof(struct overhead)
* adjacency chain includes all memory, allocated plus free.
*/
#include "veneer.h"
#define MAGIC_FREE 0x548a934c
#define MAGIC_BUSY 0xc139569a
#define NBUCKETS 24
#define NALIGN 4
struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
};
struct overhead {
struct qelem ov_adj; /* adjacency chain pointers */
struct qelem ov_buk; /* bucket chain pointers */
long ov_magic;
unsigned long ov_length;
};
char endfree = 0;
struct qelem adjhead = { &adjhead, &adjhead };
struct qelem buckets[NBUCKETS] = {
&buckets[0], &buckets[0],
&buckets[1], &buckets[1],
&buckets[2], &buckets[2],
&buckets[3], &buckets[3],
&buckets[4], &buckets[4],
&buckets[5], &buckets[5],
&buckets[6], &buckets[6],
&buckets[7], &buckets[7],
&buckets[8], &buckets[8],
&buckets[9], &buckets[9],
&buckets[10], &buckets[10],
&buckets[11], &buckets[11],
&buckets[12], &buckets[12],
&buckets[13], &buckets[13],
&buckets[14], &buckets[14],
&buckets[15], &buckets[15],
&buckets[16], &buckets[16],
&buckets[17], &buckets[17],
&buckets[18], &buckets[18],
&buckets[19], &buckets[19],
&buckets[20], &buckets[20],
&buckets[21], &buckets[21],
&buckets[22], &buckets[22],
&buckets[23], &buckets[23],
};
/*
* The following macros depend on the order of the elements in struct overhead
*/
#define TOADJ(p) ((struct qelem *)(p))
#define FROMADJ(p) ((struct overhead *)(p))
#define FROMBUK(p) ((struct overhead *)( (char *)p - sizeof(struct qelem)))
#define TOBUK(p) ((struct qelem *)( (char *)p + sizeof(struct qelem)))
#ifndef CURBRK
#define CURBRK sbrk(0)
#endif CURBRK
STATIC void insque(), remque();
#ifdef NULL
#undef NULL
#endif
#define NULL 0
#ifdef debug
# define ASSERT(p,q) if (!(p)) fatal(q)
#else
# define ASSERT(p,q)
#endif
#define ALIGN(n, granule) ((n + ((granule)-1)) & ~((granule)-1))
char *
malloc(nbytes)
unsigned nbytes;
{
register struct overhead *p, *q;
register int surplus = 0;
register struct qelem *bucket;
nbytes = ALIGN(nbytes, NALIGN) + sizeof(struct overhead);
bucket = &buckets[log2(nbytes-1) + 1]; /* log2 rounded up */
for (p = NULL; bucket < &buckets[NBUCKETS]; bucket++) {
if (bucket->q_forw != bucket) {
/* remove from bucket chain */
p = FROMBUK(bucket->q_forw);
ASSERT(p->ov_magic == MAGIC_FREE, "\nmalloc: Entry \
not marked FREE found on Free List!\n");
remque(TOBUK(p));
surplus = p->ov_length - nbytes;
break;
}
}
if (p == NULL) {
#ifdef USE_SBRK
register int i;
p = (struct overhead *)CURBRK;
if ((int)p == -1) {
return(NULL);
}
if (i = (int)p&(NALIGN-1)) {
sbrk(NALIGN-i);
}
p = (struct overhead *)sbrk(nbytes);
if ((int)p == -1) {
return(NULL);
}
q = (struct overhead *)CURBRK;
if ((int)q == -1) {
return(NULL);
}
p->ov_length = (char *)q - (char *)p;
surplus = p->ov_length - nbytes;
/* add to end of adjacency chain */
ASSERT((FROMADJ(adjhead.q_back)) < p, "\nmalloc: Entry in \
adjacency chain found with address lower than Chain head!\n" );
insque(TOADJ(p),adjhead.q_back);
#else
struct qelem *pp;
int alloc_size = ALIGN(nbytes, PAGE_SIZE);
p = (struct overhead *)alloc(alloc_size, NALIGN);
if ((int)p == -1) {
return(NULL);
}
p->ov_length = alloc_size;
surplus = p->ov_length - nbytes;
/* add to adjacency chain in the correct place */
for (pp = adjhead.q_forw; pp != &adjhead; pp = pp->q_forw) {
if (p < FROMADJ(pp)) {
break;
}
}
ASSERT(pp == &adjhead || (p < FROMADJ(pp)),
"\nmalloc: Bogus insertion in adjacency list\n");
insque(TOADJ(p),pp->q_back);
#endif
}
if (surplus > sizeof(struct overhead)) {
/* if big enough, split it up */
q = (struct overhead *)( (char *)p + nbytes);
q->ov_length = surplus;
p->ov_length = nbytes;
q->ov_magic = MAGIC_FREE;
/* add surplus into adjacency chain */
insque(TOADJ(q),TOADJ(p));
/* add surplus into bucket chain */
insque(TOBUK(q),&buckets[log2(surplus)]);
}
p->ov_magic = MAGIC_BUSY;
return((char*)p + sizeof(struct overhead));
}
void
free(mem)
register char *mem;
{
register struct overhead *p, *q;
if (mem == NULL) {
return;
}
p = (struct overhead *)(mem - sizeof(struct overhead));
if (p->ov_magic == MAGIC_FREE) {
return;
}
if (p->ov_magic != MAGIC_BUSY) {
fatal("attempt to free memory not allocated with malloc!\n");
}
q = FROMADJ((TOADJ(p))->q_back);
if (q != FROMADJ(&adjhead)) { /* q is not the first list item */
ASSERT(q < p, "\nfree: While trying to merge a free area with \
a lower adjacent free area,\n addresses were found out of order!\n");
/* If lower segment can be merged */
if ((q->ov_magic == MAGIC_FREE) &&
((char *)q + q->ov_length == (char *)p) ) {
/* remove lower address area from bucket chain */
remque(TOBUK(q));
/* remove upper address area from adjacency chain */
remque(TOADJ(p));
q->ov_length += p->ov_length;
p->ov_magic = NULL;
p = q;
}
}
q = FROMADJ((TOADJ(p))->q_forw);
if (q != FROMADJ(&adjhead)) { /* q is not the last list item */
/* upper segment can be merged */
ASSERT(q > p, "\nfree: While trying to merge a free area with \
a higher adjacent free area,\n addresses were found out of order!\n");
if ( (q->ov_magic == MAGIC_FREE) &&
((char *)p + p->ov_length == (char *)q) ) {
/* remove upper from bucket chain */
remque(TOBUK(q));
/* remove upper from adjacency chain */
remque(TOADJ(q));
p->ov_length += q->ov_length;
q->ov_magic = NULL;
}
}
#ifdef USE_SBRK
/* freed area is at end of memory */
if ((endfree && adjhead.q_back == TOADJ(p)) &&
((char*)p + p->ov_length == (char *)CURBRK) ) {
/* remove from end of adjacency chain */
remque(adjhead.q_back);
/* release memory to system */
sbrk( -((int)(p->ov_length)));
return;
}
#endif
p->ov_magic = MAGIC_FREE;
/* place in bucket chain */
insque(TOBUK(p),&buckets[log2(p->ov_length)]);
return;
}
char *
realloc(mem,nbytes)
register char *mem; unsigned nbytes;
{
register char *newmem;
register struct overhead *p, *q;
register int surplus;
if (mem == NULL) {
return(malloc(nbytes));
}
if(mem > (char*)FROMADJ(adjhead.q_back) + sizeof(struct overhead)) {
return(NULL);
}
p = (struct overhead *)(mem - sizeof(struct overhead));
nbytes = (nbytes + (NALIGN-1)) & (~(NALIGN-1));
if ( (p->ov_magic == MAGIC_BUSY) && (q = FROMADJ(adjhead.q_back)) != p
&& (q->ov_magic != MAGIC_FREE || (FROMADJ(q->ov_adj.q_back) != p)) ) {
free(mem);
}
if( (p->ov_magic == MAGIC_BUSY || p->ov_magic == MAGIC_FREE)
&& (surplus = p->ov_length - nbytes - sizeof(struct overhead)) >= 0 ) {
if (surplus > sizeof(struct overhead)) {
/* return surplus to free list */
nbytes += sizeof(struct overhead);
#ifdef USE_SBRK
/* freed area is at end of memory */
if ( endfree && adjhead.q_back == TOADJ(p)
&& (char*)p + p->ov_length == (char *)CURBRK ) {
/* release memory to system */
sbrk(-surplus);
} else
#endif
{
q = (struct overhead *)( (char *)p + nbytes);
q->ov_length = surplus;
q->ov_magic = MAGIC_FREE;
insque(TOADJ(q),TOADJ(p));
insque(TOBUK(q),&buckets[log2(surplus)]);
}
p->ov_length = nbytes;
}
if (p->ov_magic == MAGIC_FREE) {
remque(TOBUK(p));
p->ov_magic = MAGIC_BUSY;
}
return(mem);
}
newmem = malloc(nbytes);
if (newmem != mem && newmem != NULL) {
register unsigned n;
if (p->ov_magic == MAGIC_BUSY || p->ov_magic == MAGIC_FREE) {
n = p->ov_length - sizeof(struct overhead);
nbytes = (nbytes < n) ? nbytes : n ;
}
bcopy(mem,newmem,nbytes);
}
if (p->ov_magic == MAGIC_BUSY) {
free(mem);
}
return(newmem);
}
log2(n)
register int n;
{
register int i = 0;
while ((n >>= 1) > 0) {
i++;
}
return(i);
}
void
insque(item,queu)
register struct qelem *item, *queu;
{
register struct qelem *pueu;
pueu = queu->q_forw;
item->q_forw = pueu;
item->q_back = queu;
queu->q_forw = item;
pueu->q_back = item;
}
void
remque(item)
register struct qelem *item;
{
register struct qelem *queu, *pueu;
pueu = item->q_forw;
queu = item->q_back;
queu->q_forw = pueu;
pueu->q_back = queu;
}