wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
#ifdef MEMWRAP
/* This software is written and copyright Sam Lantinga: mem.c and mem.h */
/*  I have been having real problems with malloc() on an AT&T 3b2, so
    here is a customized memory handler specialized for many small
    requests for memory.  It allocates a large chunk with malloc, and
    then lops hunks off for the user routines.  Hopefully it is more
    efficient.  
      -Sam Lantinga  6/8/93
*/

#include  <stdio.h>
#include  <errno.h>
#include  "mem.h"    /* The memory include file */

static struct grain {
  int   size;    /* The request size in grains */
  int   used;    /* Have we been allocated?    */
  char *bit;    /* The grain of memory we are */
  } yougetawarningifyouhavenotypehere;

static struct memblock {
  int   numfree;    /* The number of free grains  */
  char  *head;
  struct grain grains[GRAINS];
  struct memblock *next;
  } memstart;

static struct bigblock {
  char *data;
  struct bigblock *next;
  } Memstart;

int debugmem=0;        /* Memory debugging level */
static int havealloct=0;    /* Have we initialized memory yet? */
static int tracemem=0;      /* Do we trace allocated memory? */


/* Used during debugging to keep track of the memory allocated and freed.  */

static struct memtrace {
  char *data;
  struct memtrace *next;
  } tracestart = { NULL, NULL };

void set_mtrace(on)
int on;      /* Turn it on or off? */
{
  if ( on )
    tracemem=1;
  else
    tracemem=0;
}

void print_mtrace()
{
  struct memtrace *memptr;

  fprintf(stderr, "Printing memory trace:\n");

  for ( memptr=(&tracestart); memptr->next != NULL; memptr=memptr->next )
    fprintf(stderr,"\ttracing address %u\n",(memptr->next)->data);
}

static int addtotrace(ptr)
char *ptr;
{
  struct memtrace *memptr;

  for (memptr=(&tracestart); memptr->next != NULL; memptr=memptr->next);

  if ( (memptr->next=(struct memtrace *)malloc(sizeof(struct memtrace)))
                == NULL )
    return(-1);
  else
    memptr=memptr->next;
  memptr->data=ptr;
  memptr->next=NULL;

  return(0);
}

static int delfromtrace(ptr)
char *ptr;
{
  struct memtrace *prev, *memptr;

  for ( memptr=(&tracestart); memptr->next != NULL; memptr=memptr->next )
  {
    if ( (memptr->next)->data == ptr )
    {
      prev=(memptr->next)->next;
      (void) free(memptr->next);
      memptr->next=prev;
      return(0);
    }
  }
  fprintf(stderr, "Address %d not found in memory trace.\n");
  return(-1);
}
  
      

static int meminit(blkptr)
struct memblock *blkptr;
{
  int   i;
  char *newarea;

  if ( (blkptr->head=(char *)malloc(MEMSIZ)) == NULL )
    return(-1);
  blkptr->numfree=GRAINS;

  for ( i=0, newarea=blkptr->head; i<GRAINS; ++i )
  {
    blkptr->grains[i].bit=newarea;
    blkptr->grains[i].used=0;
    newarea+=GRAIN;
  }
  blkptr->next=NULL;

#ifdef CLEANMEM
  zerout(blkptr->head, MEMSIZ);
#endif 
  return(0);
}


char *myalloc(size)
int size;
{
  int i=0;
  int needed=0;    /* The number of grains needed to fulfill */
  int need=0;    /* The size of request unfulfilled */
  int freestart=(-1);  /* The first free grain */

  struct memblock *blk, *temp;
  struct bigblock *Blk, *Temp;

  if ( ! havealloct )
  {  /* We need to initialize some memory */
    if ( meminit(&memstart) < 0 )
      return(NULL);

    Memstart.data=NULL;
    Memstart.next=NULL;

    havealloct=1;
  }

  /* If no need, no problem! */
  if ( size == 0 )
    return(NULL);

  /* Call malloc() directly if the request is larger than MEMSIZ */
  /* Create a big block, and attatch it to the linked list */
  if ( size > MEMSIZ )
  {
    for ( Blk=Memstart.next, Temp=(&Memstart);
           Blk; Temp=Blk, Blk=Blk->next );
    
    if ( (Blk=(struct bigblock *)malloc(sizeof(struct bigblock)))
                == NULL )
      return(NULL);
    if ( (Blk->data=(char *)malloc(size)) == NULL )
    {
      free(Blk);
      return(NULL);
    }
    Temp=Blk;
    Blk->next=NULL;
    return(Blk->data);
  }

  needed=((size+(GRAIN-1))/GRAIN);

  if ( debugmem > 1 )
    fprintf(stderr, "myalloc():\n\tsize: %d\n\tneeded: %d\n",
                size, needed);

  for (blk=(&memstart),temp=blk; (blk != NULL); temp=blk,blk=blk->next )
  {
    if ( needed <= blk->numfree )
    {
      need=needed;
      for ( i=0; i<GRAINS; ++i )
      {
        if ( blk->grains[i].used )
        {
          freestart=(-1);
          need=needed;
          continue;
        }

        if ( freestart < 0 )
          freestart=i;
        --need;
        
        if ( need == 0 )
        {
          for ( i=freestart;
                      i<(needed+freestart); ++i )
            blk->grains[i].used=1;
          blk->grains[freestart].size=needed;
          blk->numfree-=needed;
#ifdef CLEANMEM
          zerout(blk->grains[freestart].bit,
                (needed*GRAIN));
#endif /* CLEANMEM */
          if ( tracemem )
          {
            (void) addtotrace(
            blk->grains[freestart].bit);
          }
          return(blk->grains[freestart].bit);
        }
      }
    }
  }

  /* We didn't get the memory from a pool.. allocate a new one */

  if ((blk=(struct memblock *)malloc(sizeof(struct memblock))) == NULL)
    return(NULL);

  if ( meminit(blk) < 0 )
    return(NULL);
  else
    temp->next=blk;

  for ( i=0; i<needed; ++i )
    blk->grains[i].used=1;

  blk->grains[0].size=needed;
  blk->numfree-=needed;
  return(blk->grains[0].bit);
}

int myfree(ptr)
char *ptr;
{
  int i, cur;
  struct memblock *blk;
  struct bigblock *Blk, *Temp;

  /* A little sanity please. :) */
  if ( ptr == NULL )
    return(0);

  if ( ! havealloct )
    return(free(ptr));

  if ( debugmem > 0 )
    fprintf(stderr, "myfree(): freeing address %u\n", ptr);

  for ( blk=(&memstart); (blk != NULL); blk=blk->next )
  {
    if ( (ptr >= blk->head) && (ptr < (blk->head+MEMSIZ)) )
    {   /* The area to be freed is in this block */
      for ( i=0; i<GRAINS; ++i )
      {
        if ( ptr == blk->grains[i].bit )
        {
          if ( ! blk->grains[i].used )
            return(-1);

          for( cur=i;
            (i<(blk->grains[cur].size+cur));
                        ++i )
            blk->grains[i].used=0;
          blk->numfree+=blk->grains[cur].size;
          blk->grains[cur].size=0;
          if ( tracemem )
            (void) delfromtrace(ptr);
          return(0);
        }
      }
      return(-1);
    }
  }

  /* Either the user passed us a bogus address, or it's from malloc() */

  for (Blk=Memstart.next,Temp=(&Memstart); Blk; Temp=Blk,Blk=Blk->next)
  {
    if ( ptr == Blk->data )
    {
      Temp->next=Blk->next;
      free(Blk->data);
      return(0);
    }
  }
  errno=EINVAL;
  return(-1);
}

/* A debug routine for the memory functions.  It produces VOLUMOUS
   output.  */

void mem_stat()
{
  int i=0, k;
  struct memblock *blk;

  if ( ! havealloct )
    return;

  for ( blk=(&memstart); (blk != NULL); blk=blk->next )
  {
    fprintf(stderr, "Memory block #%d:\n", i++);
    fprintf(stderr, "\tfree grains: %d\n", blk->numfree);
    fprintf(stderr, "\tstarting address: %d\n", blk->head);
    fprintf(stderr, "\tending address: %d\n", blk->head+MEMSIZ);

    for ( k=0; k<GRAINS; ++k )
    {
      if ( debugmem > 1 )
      {
        if ( blk->grains[k].used )
        fprintf(stderr, "\tgrain #%d is used.\n", k);
      }
      if ( blk->grains[k].size > 0 )
      {
        fprintf(stderr,
     "\tgrain #%d is a Grain %d grains long at address %u\n", k, 
        blk->grains[k].size, blk->grains[k].bit);
      }
    }
  }
}

/* Replacement for bzero(), which isn't always available.  */

void zerout(data, size)
char *data;
int   size;
{
  int i;

  for ( i=0; (i < size); ++i, ++data )
    *data='\0';
}

/* Replacement for bcopy(), which isn't always available.  */

void zcopy(frombuf, tobuf, size)
char *frombuf;
char *tobuf;
int   size;
{
  int i;

  for ( i=0; (i < size); ++i, ++frombuf, ++tobuf )
    *tobuf=(*frombuf);
}
#endif