/* sswf_memory.c -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2009 */

/*

Copyright (c) 2002-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/


#include	"sswf.h"

#if DEBUG
struct mem_info_t {
	unsigned long		magic;		/* the 'SSWF' identifier */
	struct mem_info_t *	next;		/* link all the memory blocks */
	struct mem_info_t *	previous;
	const char *		name;		/* some info about this memory buffer */
	unsigned long		serial;		/* a counter */
	size_t			size;		/* user allocation size */
};
#define	SSWF_MEM_INFO_SIZEOF	SSWF_MEM_ALIGN(sizeof(struct mem_info_t))
#define	SSWF_MEM_INFO_MAGIC	((unsigned long) 0x53535746)

#define	SSWF_MEM_SAFE		(sizeof(double) * 32)
#define	SSWF_MEM_TEST		0xBADC0FFE
#define	SSWF_MEM_CLEAR		0xCB

static	struct mem_info_t *	mem_head;
static	unsigned long		mem_serial;


#else
#define	SSWF_MEM_INFO_SIZEOF	0
#define	SSWF_MEM_SAFE		0
#endif




#if DEBUG
void *sswf_alloc(size_t size, const char *name)
#else
void *sswf_alloc(size_t size)
#endif
{
#if DEBUG
	long	*l, *m;
	int	idx;
#endif
	void	*p;

	p = malloc(((size + sizeof(long) - 1) & - (long) sizeof(long)) + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE * 2);
	if(p == NULL) {
		fprintf(stderr, "FATAL ERROR: out of memory allocating %d bytes.\n", (int) size);
		exit(-1);
		/*NOTREACHED*/
	}

#if DEBUG
	((struct mem_info_t *) p)->magic = SSWF_MEM_INFO_MAGIC;
	((struct mem_info_t *) p)->next = mem_head;
	if(mem_head != NULL) {
		mem_head->previous = (struct mem_info_t *) p;
	}
	((struct mem_info_t *) p)->previous = NULL;
	mem_head = (struct mem_info_t *) p;
	((struct mem_info_t *) p)->name = name;
	mem_serial++;
	((struct mem_info_t *) p)->serial = mem_serial;
	((struct mem_info_t *) p)->size = size;
	l = (long *) ((char *) p + SSWF_MEM_INFO_SIZEOF);
	p = (char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE;
	m = (long *) ((char *) p + ((size + sizeof(long) - 1) & - (long) sizeof(long)));
	idx = SSWF_MEM_SAFE / sizeof(long);
	do {
		*l++ = *m++ = SSWF_MEM_TEST;
		idx--;
	} while(idx > 0);
#endif

	return p;
}


#if DEBUG
static void sswf_test_safe(void *p, const char *what)
{
	long		*l, *m;
	int		idx, head, tail;

	assert(((long) p & (SSWF_MEM_ALIGN_SIZE - 1)) == 0, "%s an unaligned memory buffer at %p", what, (char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE);
	assert(((struct mem_info_t *) p)->magic == SSWF_MEM_INFO_MAGIC, "%s a memory buffer without a magic marker at %p", what, (char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE);
	l = (long *) ((char *) p + SSWF_MEM_INFO_SIZEOF);
	m = (long *) ((char *) l + SSWF_MEM_SAFE + ((((struct mem_info_t *) p)->size + sizeof(long) - 1) & - (long) sizeof(long)));
	head = 0;
	tail = 0;
	idx = SSWF_MEM_SAFE / sizeof(long);
	do {
		if(*l++ != SSWF_MEM_TEST) {
			head = 1;
		}
		if(*m++ != SSWF_MEM_TEST) {
			tail = 1;
		}
		idx--;
	} while(idx > 0);
	if(head) {
		/* an error occured, print out the corresponding data */
		l = (long *) ((char *) p + SSWF_MEM_INFO_SIZEOF);
		idx = SSWF_MEM_SAFE / sizeof(long);
		do {
			idx--;
			if((idx & 7) == 7) {
				printf("%08X- ", (unsigned int) (SSWF_MEM_SAFE - idx * sizeof(long)));
			}
			printf(" %08lX", *l++);
			if((idx & 7) == 0) {
				printf("\n");
			}
		} while(idx > 0);
	}
	if(tail) {
		m = (long *) ((char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE + ((((struct mem_info_t *) p)->size + sizeof(long) - 1) & - (long) sizeof(long)));
		idx = SSWF_MEM_SAFE / sizeof(long);
		do {
			idx--;
			if((idx & 7) == 7) {
				printf("%08X- ", (unsigned int) (SSWF_MEM_SAFE - idx * sizeof(long) + 256));
			}
			printf(" %08lX", *m++);
			if((idx & 7) == 0) {
				printf("\n");
			}
		} while(idx > 0);
	}
	assert(!head && !tail, "head and/or tail data was overwritten");
}
#endif



#if DEBUG
void *sswf_realloc(void *p, size_t size, const char *name)
#else
void *sswf_realloc(void *p, size_t size)
#endif
{
#if DEBUG
	long		*m;
	int		idx;

	if(p == NULL) {
		return sswf_alloc(size, name);
	}

	p = (char *) p - SSWF_MEM_INFO_SIZEOF - SSWF_MEM_SAFE;
	sswf_test_safe(p, "reallocating");
#else
	if(p == NULL) {
		return sswf_alloc(size);
	}
#endif
	p = realloc(p, size + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE * 2);
	if(p == NULL) {
		fprintf(stderr, "FATAL ERROR: out of memory re-allocating %d bytes.\n", (int) size);
		exit(-1);
		/*NOTREACHED*/
	}

#if DEBUG
	/*((struct mem_info_t *) p)->magic = SSWF_MEM_INFO_MAGIC; -- shouldn't have changed */
	/* p has changed so the next/previous of the previous/next nodes need fixing */
	if(((struct mem_info_t *) p)->next != NULL) {
		((struct mem_info_t *) p)->next->previous = (struct mem_info_t *) p;
	}
	if(((struct mem_info_t *) p)->previous != NULL) {
		((struct mem_info_t *) p)->previous->next = (struct mem_info_t *) p;
	}
	else {
		mem_head = (struct mem_info_t *) p;
	}
	((struct mem_info_t *) p)->name = name;
	mem_serial++;
	((struct mem_info_t *) p)->serial = mem_serial;
	((struct mem_info_t *) p)->size = size;
	p = (char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE;
	m = (long *) ((char *) p + ((size + sizeof(long) - 1) & - (long) sizeof(long)));
	idx = SSWF_MEM_SAFE / sizeof(long);
	do {
		*m++ = SSWF_MEM_TEST;
		idx--;
	} while(idx > 0);
#endif

	return p;
}



void sswf_free(void *p)
{
	/* accept NULL pointers */
	if(p == NULL) {
		return;
	}
#if DEBUG
	p = (char *) p - SSWF_MEM_INFO_SIZEOF - SSWF_MEM_SAFE;
	sswf_test_safe(p, "freeing");

	if(((struct mem_info_t *) p)->next != NULL) {
		((struct mem_info_t *) p)->next->previous = ((struct mem_info_t *) p)->previous;
	}
	if(((struct mem_info_t *) p)->previous != NULL) {
		((struct mem_info_t *) p)->previous->next = ((struct mem_info_t *) p)->next;
	}
	else {
		mem_head = ((struct mem_info_t *) p)->next;
	}
	/* clear this buffer so we can't free it again and again */
	memset(p, SSWF_MEM_CLEAR, ((struct mem_info_t *) p)->size + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE * 2);
#endif
	free(p);
}


void sswf_clean(void *p)
{
	/* p is really a pointer to a pointer in this case */
	if(p == NULL) {
		return;
	}
	if(* (void **) p == NULL) {
		return;
	}
	sswf_free(* (void **) p);
	* (void **) p = NULL;
}



char *sswf_strdup(const char *string)
{
	int		l;
	char		*r;

	if(string == NULL) {
		l = 1;
	}
	else {
		l = strlen(string) + 1;
	}

	r = sswf_malloc(l, "dynamic string (strdup)");

	if(l > 1) {
		/* l includes the nul terminator */
		memcpy(r, string, l);
	}
	else {
		*r = '\0';
	}

	return r;
}


char *sswf_strcat(const char *a, const char *b)
{
	int		la, lb;
	char		*r;

	if(a == NULL) {
		return sswf_strdup(b);
	}
	if(b == NULL) {
		return sswf_strdup(a);
	}

	la = strlen(a);
	lb = strlen(b);

	r = sswf_malloc(la + lb + 1, "dynamic string (sswf_strcat)");

	if(la > 0) {
		memcpy(r, a, la);
	}
	if(lb > 0) {
		memcpy(r + la, b, lb);
	}
	r[la + lb] = '\0';

	return r;
}



char *sswf_strcat3(const char *a, const char *b, const char *c)
{
	int		la, lb, lc;
	char		*r;

	if(a == NULL) {
		a = "";
	}
	if(b == NULL) {
		b = "";
	}
	if(c == NULL) {
		c = "";
	}

	la = strlen(a);
	lb = strlen(b);
	lc = strlen(c);

	r = sswf_malloc(la + lb + lc + 1, "dynamic string (sswf_strcat3)");

	if(la > 0) {
		memcpy(r, a, la);
	}
	if(lb > 0) {
		memcpy(r + la, b, lb);
	}
	if(lc > 0) {
		memcpy(r + la + lb, c, lc);
	}
	r[la + lb + lc] = '\0';

	return r;
}



char *sswf_strchild(const char *a, const char *b)
{
	int		la, lb;
	const char	*e;
	char		*r;

	if(a == NULL) {
		return sswf_strdup(b);
	}
	if(b == NULL) {
		return sswf_strdup(a);
	}

	e = a;
	while(*e != '\0') {
		e++;
	}
	if(e > a && (e[-1] == '/' || e[-1] == '\\' || e[-1] == ':')) {
		return sswf_strcat(a, b);
	}

	la = strlen(a);
	lb = strlen(b);

	r = sswf_malloc(la + lb + 2, "dynamic string (strcat)");

	if(la > 0) {
		memcpy(r, a, la);
	}
	r[la] = '/';		/* TODO: we need to fix this with the system separator instead */
	if(lb > 0) {
		memcpy(r + la + 1, b, lb);
	}
	r[la + lb + 1] = '\0';

	return r;
}



#if DEBUG
void sswf_info(void)
{
	struct mem_info_t	*p;
	size_t			count, total;

	printf("\n*** Memory Info:\n");
	total = 0;
	count = 0;
	p = mem_head;
	if(p == NULL) {
		printf("!!! No memory leaks. Very good. !!!\n");
		return;
	}
	if(p->previous != NULL) {
		printf("+++ INVALID +++ First block not a head list block!\n");
	}
	while(p != NULL) {
		if(p->magic != SSWF_MEM_INFO_MAGIC) {
			printf("+++ INVALID +++ ");
		}
		count++;
		printf("0x%08lX: Block #%06ld (Serial #%06ld) - %7ld bytes - %s", (long) ((char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE), (long) count, (long) p->serial, (long) p->size, p->name);
		if(p->name != NULL && strncmp(p->name, "dynamic string", 14) == 0) {
			printf(" [%s]", (char *) p + SSWF_MEM_INFO_SIZEOF + SSWF_MEM_SAFE);
		}
		printf("\n");
		total += p->size;
		p = p->next;
	}

	printf("Total of %ld blocks for %ld bytes.\n", (long) count, (long) total);
}

void sswf_info_serial(void)
{
	printf("At this point, the memory serial counter is #%ld\n", mem_serial);
}
#endif


