
/********************************************
memory.c

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

copyright 1991, 1992  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 "memory.h"

static mawk_string_t *xnew_STRING(mawk_state_t *, unsigned);


static mawk_string_t *xnew_STRING(mawk_state_t *MAWK, unsigned len)
{
	mawk_string_t *sval = (mawk_string_t *) mawk_zmalloc(MAWK, len + STRING_OH);

	sval->len = len;
	sval->ref_cnt = 1;
	return sval;
}

/* allocate space for a mawk_string_t */

mawk_string_t *mawk_new_STRING0(mawk_state_t *MAWK, unsigned len)
{
	if (len == 0) {
		MAWK->null_str.ref_cnt++;
		return &MAWK->null_str;
	}
	else {
		mawk_string_t *sval = xnew_STRING(MAWK, len);
		sval->str[len] = 0;
		return sval;
	}
}

/* convert char* to mawk_string_t* */

mawk_string_t *mawk_new_STRING(mawk_state_t *MAWK, const char *s)
{
	if (s[0] == 0) {
		MAWK->null_str.ref_cnt++;
		return &MAWK->null_str;
	}
	else {
		mawk_string_t *sval = xnew_STRING(MAWK, strlen(s));
		strcpy(sval->str, s);
		return sval;
	}
}


#ifdef	 DEBUG

void DB_free_STRING(mawk_state_t *MAWK, register mawk_string_t *sval)
{
	if (--sval->ref_cnt == 0)
		mawk_zfree(MAWK, sval, sval->len + STRING_OH);
}

#endif


/**************************************************************************
large block allocation: collect all allocation in a double linked list in
MAWK so they can be easily free'd without any leak at the end of script
execution. Also do memory accounting and don't allocate over preset limit.
***************************************************************************/

#define OVERHEAD ((char *)&(r->data) - (char *)(r))
static void *mawk_mm_link(mawk_state_t *MAWK, mawk_mm_t *r)
{
	r->next = MAWK->mawk_mm_head;
	r->prev = NULL;
	if (r->next != NULL)
		r->next->prev = r;
	MAWK->mawk_mm_head = r;
	return &r->data;
}

static void mawk_mm_unlink(mawk_state_t *MAWK, mawk_mm_t *r)
{
	if (r->prev != NULL)
		r->prev->next = r->next;
	else
		MAWK->mawk_mm_head = r->next;
	if (r->next != NULL)
		r->next->prev = r->prev;
}

/* store a pointer in the mawk_mm_head list */
void *mawk_malloc(mawk_state_t *MAWK, int size)
{
	mawk_mm_t *r;

	if ((MAWK->mm_max > 0) && (MAWK->mm_used + size > MAWK->mm_max))
		return NULL;

	r = malloc(OVERHEAD + size);
	r->size = OVERHEAD + size;
	MAWK->mm_used += r->size;
	return mawk_mm_link(MAWK, r);
}

void *mawk_realloc(mawk_state_t *MAWK, void *ptr, int size)
{
	mawk_mm_t *r;

	/* emulate mawk_malloc() for simpler implementation of growing buff */
	if (ptr == NULL)
		return mawk_malloc(MAWK, size);

	r = (mawk_mm_t *)((char *)ptr - OVERHEAD);

	mawk_mm_unlink(MAWK, r);

	if ((MAWK->mm_max > 0) && (MAWK->mm_used - r->size + size > MAWK->mm_max))
		return NULL;

	MAWK->mm_used -= r->size;
	r = realloc(r, OVERHEAD + size);
	if (r == NULL)
		return NULL;
	r->size = OVERHEAD + size;
	MAWK->mm_used += r->size;
	return mawk_mm_link(MAWK, r);
}

void mawk_free(mawk_state_t *MAWK, void *ptr)
{
	mawk_mm_t *r = (mawk_mm_t *)((char *)ptr - OVERHEAD);
	mawk_mm_unlink(MAWK, r);
	MAWK->mm_used -= r->size;
	free(r);
}

void mawk_free_all(mawk_state_t *MAWK)
{
	mawk_mm_t *r, *n;
	for(r = MAWK->mawk_mm_head; r != NULL; r = n) {
		n = r->next;
		free(r);
	}
	MAWK->mawk_mm_head = NULL;
	MAWK->mm_used = 0;
}

char *mawk_strdup(mawk_state_t *MAWK, const char *s)
{
	int l;
	char *r;

	if (s == NULL)
		return NULL;

	l = strlen(s);
	r = mawk_malloc(MAWK, l+1);
	memcpy(r, s, l+1);
	return r;
}

char *mawk_strdup_(const char *s)
{
	int l;
	char *r;

	if (s == NULL)
		return NULL;

	l = strlen(s);
	r = malloc(l+1);
	memcpy(r, s, l+1);
	return r;
}

