
/********************************************
zmalloc.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/

/*$Log: zmalloc.c,v $
 * Revision 5.1.1.1  1993/02/06  11:12:19  mike
 * fix bug in reuse of parser table memory
 * for most users ifdef the mess out
 *
 * Revision 5.1  1991/12/05  07:56:35  brennan
 * 1.1 pre-release
 *
*/

/*  zmalloc.c  */
#include  "mawk.h"
#include  "zmalloc.h"

void PROTO( mawk_exit, (int) ) ;

extern  struct yacc_mem  *yacc_memp ;

/*
  zmalloc() gets mem from malloc() in CHUNKS of 2048 bytes
  and cuts these blocks into smaller pieces that are multiples
  of eight bytes.  When a piece is returned via zfree(), it goes
  on a linked linear list indexed by its size.  The lists are
  an array, pool[].

  E.g., if you ask for 22 bytes with p = zmalloc(22), you actually get
  a piece of size 24.  When you free it with zfree(p,22) , it is added
  to the list at pool[2].
*/

#define POOLSZ      16

#define  CHUNK          256    
        /* number of blocks to get from malloc */

static PTR  PROTO( emalloc, (unsigned) ) ;
void PROTO( errmsg, (int , char *, ...) ) ;

static PTR emalloc(size)
  unsigned size ;
{ PTR p ;
  static char out[] = "out of memory" ;

  if( !(p = (PTR) malloc(SIZE_T(size))) )
	if ( mawk_state == EXECUTION ) rt_error(out) ;
	else /* I don't think this will ever happen */
	{ compile_error(out) ; mawk_exit(1) ; }
  return p ;
}


typedef  union  zblock {
char dummy[ZBLOCKSZ] ;
union zblock *link ;
}  ZBLOCK  ;

/* ZBLOCKS of sizes 1, 2, ... 16
   which is bytes of sizes 8, 16, ... , 128
   are stored on the linked linear lists in
   pool[0], pool[1], ... , pool[15]
*/

static  ZBLOCK  *pool[POOLSZ] ;

PTR   bmalloc( blocks )
  register unsigned blocks ;
{ 
  register ZBLOCK *p ;
  static  unsigned amt_avail ;
  static  ZBLOCK  *avail ;

  if ( blocks > POOLSZ )  return emalloc(blocks<<ZSHIFT) ;

  if ( p = pool[blocks-1] )
  { pool[blocks-1] = p->link ; return (PTR) p ; }

  if ( blocks > amt_avail )
  { 
    if ( amt_avail != 0 ) /* free avail */
    { 
      avail->link = pool[--amt_avail] ;
      pool[amt_avail] = avail ;
    }

#if MSDOS || HAVE_SMALL_MEMORY
/* this hack is dangerous (I've blown it twice), not portable,
   and counts on byacc not changing, but it is a big win on
   DOS.  On paged vmem systems it is a nop so ifdef it out.
*/
    /* use parser tables first */
    if ( yacc_memp->zblocks > blocks )
    { 
      avail = (ZBLOCK *) yacc_memp->mem ;
      amt_avail = yacc_memp++ -> zblocks ;
      /* make sure its -- aligned */
      {
	int k = (int) avail & 7 ;
        if ( k )
        { 
	  avail = (ZBLOCK*)((char *)avail + (8-k)) ;
	  amt_avail-- ;
	}
      }
    }
    else
#endif

    if ( !(avail = (ZBLOCK *) malloc(SIZE_T(CHUNK*ZBLOCKSZ))) )
    { /* if we get here, almost out of memory */
        amt_avail = 0 ;   
	return  emalloc(blocks << ZSHIFT) ;
    }
    else  amt_avail = CHUNK ;
  }
  
  /* get p from the avail pile */
  p = avail ; avail += blocks ; amt_avail -= blocks ; 
  return (PTR) p ;
}

void  bfree( p, blocks)
  register PTR p ;  
  register unsigned blocks ;
{ 

  if ( blocks > POOLSZ )  free(p) ;
  else
  {
    ((ZBLOCK *)p)->link = pool[--blocks] ;
    pool[blocks] = (ZBLOCK *) p ;
  }
}

PTR  zrealloc( p, old_size, new_size )
  register PTR  p ;
  unsigned old_size, new_size ;
{ register PTR q ;

  (void) memcpy(q = zmalloc(new_size), p, 
                SIZE_T(old_size < new_size ? old_size : new_size)) ;
 
  zfree(p, old_size) ;
  return q ;
}


