
/********************************************
zmalloc.c

libmawk changes (C) 2009-2012, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

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.
********************************************/

#include  "mawk.h"
#include  "zmalloc.h"
#include  "memory.h"

/*
  mawk_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 mawk_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 = mawk_zmalloc(22), you actually get
  a piece of size 24.  When you free it with mawk_zfree(p,22) , it is added
  to the list at pool[2].


  In more details:
  When a block-allocated area is free'd, put it in pool[blocksize], which
  is a linked list. When a new reuqest comes in, try to serve it from the
  pool.

  If that fails: there is a single chunk allocated, called MAWK->avail,
  with an index (MAWK->amt_avail) pointing to the first unclaimed block; cut
  down the requested size from there, from the end of the ->avail chunk. If
  it is impossible (not enough unclaimed blocks at the end), put the unclaimed
  part of ->avail in the pool and open a new ->avail chunk.

  For corner cases see comments there.
*/

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

static void out_of_mem(mawk_state_t * MAWK);


static void out_of_mem(mawk_state_t * MAWK)
{
	static const char out[] = "out of memory";

	if (MAWK->mawk_state == EXECUTION)
		mawk_rt_error(MAWK, out);
	else {
		/* I don't think this will ever happen */
		mawk_compile_error(MAWK, out);
		mawk_exit(MAWK, 2);
	}
}

/* mawk_zmalloc() is a macro in front of mawk_bmalloc "BLOCK malloc" */
PTR mawk_bmalloc(mawk_state_t *MAWK, register unsigned blocks)
{
	register ZBLOCK *p;

	if (blocks > POOLSZ) {
		/* larger than pooled allocations */
		p = (ZBLOCK *) mawk_malloc(MAWK, blocks << ZSHIFT);
		if (!p)
			out_of_mem(MAWK);
		return (PTR) p;
	}

	if ((p = MAWK->pool[blocks - 1])) {
		/* remove the first item of the linked list and return it */
		MAWK->pool[blocks - 1] = p->link;
		return (PTR) p;
	}

	if (blocks > MAWK->amt_avail) {
		if (MAWK->amt_avail != 0) {
			/* block is bigger than what's available in the last chunk
			   mark the reamining of the last chunk free and put it in the
			   corresponding pool. We can do this because blocks is small
			   enough to be pooled and unclaimed area is even smaller
			   so there is a pool for it for sure. */

			MAWK->avail->link = MAWK->pool[--MAWK->amt_avail];
			MAWK->pool[MAWK->amt_avail] = MAWK->avail;
		}

		/* no unclaimed memory is available in the last chunk, alloc a new chunk */

		if ((MAWK->avail = (ZBLOCK *) mawk_malloc(MAWK, CHUNK * ZBLOCKSZ)) == NULL) {
			/* if we get here, almost out of memory - couldn't allocate a whole
			   new chunk; try to allocate the current request out-of-pool, as the
			   request may be smaller than a chunk
			
			 WARNING: this means pool[] contains not only allocated-in-chunk blocks
			 but plain mallocs as well.
			
			*/
			MAWK->amt_avail = 0;
			p = mawk_malloc(MAWK, blocks << ZSHIFT);
			if (!p)
				out_of_mem(MAWK);
			return (PTR) p;
		}
		else {
			/* we have a new chunk to play with */
			MAWK->amt_avail = CHUNK;
		}
	}

	/* get p from the end of the avail chunk - by now we made sure
	   we have enough unclaimed blocks at the end */
	p = MAWK->avail;
	MAWK->avail += blocks;
	MAWK->amt_avail -= blocks;
	return (PTR) p;
}

void mawk_bfree(mawk_state_t *MAWK, register PTR p, register unsigned blocks)
{
	if (blocks > POOLSZ)
		mawk_free(MAWK, p);
	else {
		((ZBLOCK *) p)->link = MAWK->pool[--blocks];
		MAWK->pool[blocks] = (ZBLOCK *) p;
	}
}

PTR mawk_zrealloc(mawk_state_t *MAWK, register PTR p, unsigned old_size, unsigned new_size)
{
	register PTR q;

	if (new_size > (POOLSZ << ZSHIFT) && old_size > (POOLSZ << ZSHIFT)) {
		/* was not a pool allocation, just realloc */
		if (!(q = mawk_realloc(MAWK, p, new_size)))
			out_of_mem(MAWK);
	}
	else {
		/* pool allocation: zalloc new, zfree old */
		q = mawk_zmalloc(MAWK, new_size);
		if (p != NULL) {
			memcpy(q, p, old_size < new_size ? old_size : new_size);
			mawk_zfree(MAWK, p, old_size);
		}
	}
	return q;
}

char *mawk_zstrclone(mawk_state_t *MAWK, const char *s)
{
	int l;
	char *ret;

	if (s == NULL)
		return NULL;

	l = strlen(s);
	ret = mawk_zmalloc(MAWK, l+1);
	memcpy(ret, s, l+1);
	return ret;
}


#ifndef	 __GNUC__
/* pacifier for Bison , this is really dead code */
PTR alloca(unsigned sz)
{
	/* hell just froze over */
	exit(100);
	return (PTR) 0;
}
#endif
