/*
 *    Copyright (C) 1998 Nikos Mavroyanopoulos
 *    Copyright (C) 1999,2000 Sascha Schumman, Nikos Mavroyanopoulos
 *
 *    This library is free software; you can redistribute it and/or modify it 
 *    under the terms of the GNU Library General Public License as published 
 *    by the Free Software Foundation; either version 2 of the License, or 
 *    (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Library General Public License for more details.
 *
 *    You should have received a copy of the GNU Library General Public
 *    License along with this library; if not, write to the
 *    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *    Boston, MA 02111-1307, USA.
 */


/*
   $Id: stdfns.c,v 1.2 2006/01/10 03:47:18 imipak Exp $ 
 */

#include "libdefs.h"
#include <limits.h>

/**
 * Some of these are wrappers. The idea is to eventually produce an extremely
 * lightweight set of robust, portable functions that are guaranteed to produce
 * a "safe" result, even with bogus inputs. We can't trust native C libraries
 * to validate inputs.
 */

WIN32DLL_DEFINE
int mutils_mlock(__const void *addr, __const mutils_word32 len)
{
	int ret;

	if ((addr == NULL) || (len == 0))
	{
		ret = -1;
		errno = EINVAL;
	}

#if defined(HAVE_MLOCK)
	ret = mlock(addr, len);
#endif

	return(ret);
}

WIN32DLL_DEFINE
int mutils_munlock(__const void *addr, __const mutils_word32 len)
{
	int ret;

	if ((addr == NULL) || (len == 0))
	{
		ret = -1;
		errno = EINVAL;
	}

#if defined(HAVE_MLOCK)
	ret = munlock(addr, len);
#endif

	return(ret);
}

/*
 * NOTE: In order for memory locking to be useful, we need our own malloc
 * implementation, as we cannot guarantee that the system-supplied one will
 * let us know how much memory has been malloc()'ed. For much the same reason,
 * neither free() nor realloc() can zero safely. This allows the potential
 * leakage of information.
 */

WIN32DLL_DEFINE
void *
mutils_malloc(__const mutils_word32 n)
{
	void *ptr;

	if (n == 0)
	{
		return(NULL);
	}

	ptr = malloc(n);

	if (ptr != NULL)
	{
		mutils_bzero(ptr, n);
	}

	return(ptr);
}

WIN32DLL_DEFINE
void *
mutils_realloc(__const void *ptr, __const mutils_word32 n)
{
	void *ptrOut;

	if ((ptr == NULL) && (n > 0))
	{
		ptrOut = mutils_malloc(n);
	}
	else
	{
		if ((ptr != NULL) && (n == 0))
		{
			mutils_free(ptr);
			ptrOut = NULL;
		}
		else
		{
		  ptrOut = realloc((void *) ptr, n);
		}
	}
	return(ptrOut);
}

WIN32DLL_DEFINE
void
mutils_free(__const void *ptr)
{
	if (ptr == NULL)
	{
		return;
	}
	free((void *) ptr);
	return;
}

WIN32DLL_DEFINE
void
mutils_bzero(void *s, __const mutils_word32 n)
{
	mutils_word8 *stmp = (mutils_word8 *) s;
	mutils_word32 i;

	if ((s == NULL) || (n == 0))
	{
		return;
	}

	for (i = 0; i < n; i++, stmp++)
	{
		*stmp = '\0';
	}
}

static void
mutils_memset8(void *s, __const mutils_word8 c, __const mutils_word32 n)
{
	mutils_word8 *stmp = s;
	mutils_word32 i;

	for (i = 0; i < n; i++, stmp++)
	{
               *stmp = c;
	}
}

WIN32DLL_DEFINE
void
mutils_memset(void *s, __const mutils_word8 c, __const mutils_word32 n)
{
#if defined(sparc)
	/* Sparc needs 8-bit alignment - just use standard memset */
	memset(s, (int) c, (size_t) n);
#else
	mutils_word32 *ltmp;
	mutils_word32 lump;
	mutils_word32 i;
	mutils_word32 words;
	mutils_word32 remainder;

	if ((s == NULL) || (n == 0))
	{
		return;
	}

	if (n < 16)
	{
		return mutils_memset8(s, c, n);
	}

	/* unaligned portion at beginning */
	remainder = (-(mutils_word32)s) & 0x3;
	mutils_memset8(s, c, remainder);

	/* aligned words in the middle */
	ltmp = (mutils_word32 *) (s + remainder);

	lump = (c << 24) + (c << 16) + (c << 8) + c;

	words = (n - remainder) >> 2;
	remainder = n - remainder - (words << 2);

	for (i = 0; i < words; i++, ltmp++)
	{
		*ltmp = lump;
	}

	/* unaligned portion at end */
	return mutils_memset8(ltmp, c, remainder);
#endif
}

static void
mutils_memcpy8(mutils_word8 *p_dest, __const mutils_word8 *p_src,
	__const mutils_word32 n)
{
	mutils_word32 i;

	for (i = 0; i < n; i++)
	{
		*p_dest++ = *p_src++;
	}
}

WIN32DLL_DEFINE
void
mutils_memcpy(void *dest, __const void *src, __const mutils_word32 n)
{
	mutils_word8 *ptr1;
	mutils_word8 *ptr2;
	mutils_word32 *bigptr1;
	mutils_word32 *bigptr2;
	mutils_word32 i;
	mutils_word32 words;
	mutils_word32 remainder;

	if ((dest == NULL) || (src == NULL) || (n == 0))
	{
		return;
	}

	ptr1 = (mutils_word8 *)src;
	ptr2 = dest;

	/* copy byte-by-byte for small and/or unaligned copies */
	if ((n < 16) || ((mutils_word32)ptr1 & 0x3) || ((mutils_word32)ptr2 
& 0x3))
	{
		mutils_memcpy8(ptr2, ptr1, n);
		return;
	}

	words = n >> 2;
	remainder = n - (words << 2);

	bigptr1 = (mutils_word32 *) dest;
	bigptr2 = (mutils_word32 *) src;

	for (i = 0; i < words; i ++, bigptr1++, bigptr2++)
	{
		*bigptr1 = *bigptr2;
	}

	ptr1 = (mutils_word8 *) bigptr1;
	ptr2 = (mutils_word8 *) bigptr2;

	for (i = 0; i < remainder; i++, ptr1++, ptr2++)
	{
		*ptr1 = *ptr2;
	}
}

/*
 * If the system ordering is big-endian, convert to little-endian format.
 * In both cases, the data is held in a temporary workspace that will be
 * overwritten the next time this function is called. This function is
 * deprecated and should NOT be called in new code. The ordering macros
 * should be used instead, for all single-word conversions.
 */
WIN32DLL_DEFINE
mutils_word32
mutils_word32swap(mutils_word32 x)
{
	static mutils_word32 value;

	value = mutils_lend32(x);
	return(value);
}

/*
 * Byte swap a series of 32-bit integers. If destructive is set to false, the
 * output will be placed in a freshly malloc()'ed buffer and the original
 * data will remain intact.
 */
WIN32DLL_DEFINE
mutils_word32 *
mutils_word32nswap(mutils_word32 *x, mutils_word32 n, mutils_boolean destructive)
{
	mutils_word32 loop;
	mutils_word32 *buffer;
	mutils_word32 *ptrIn;
	mutils_word32 *ptrOut;
	mutils_word8 *ptr8In;
	mutils_word8 *ptr8Out;
	mutils_word8 tmp8;
	mutils_word32 count = n * 4;

	if (destructive == MUTILS_FALSE)
	{
		buffer = mutils_malloc(count);
		if (buffer == NULL)
		{
			return(NULL);
		}
	}
	else
	{
		buffer = x;
	}

/*
 * This loop is totally useless for destructive processing of little-endian
 * data on a little-endian machine.
 */

	if ((mutils_word32)x & 0x3)
	{
         ptr8In = (mutils_word8 *) x;
         ptr8Out = (mutils_word8 *) buffer;
         for (loop = 0; loop < n; loop++)
           {
#ifdef WORDS_BIGENDIAN
             tmp8 = ptr8In[0];
             ptr8Out[0] = ptr8In[3];
             ptr8Out[3] = tmp8;
             tmp8 = ptr8In[1];
             ptr8Out[1] = ptr8In[2];
             ptr8Out[2] = tmp8;
#else
             ptr8Out[0] = ptr8In[0];
             ptr8Out[1] = ptr8In[1];
             ptr8Out[2] = ptr8In[2];
             ptr8Out[3] = ptr8In[3];
#endif
             ptr8Out += 4;
             ptr8In += 4;
           }
	}
	else
	{
               for (loop = 0, ptrIn = x, ptrOut = buffer; loop < n; loop++, ptrOut++, ptrIn++)
               {
                       *ptrOut = mutils_lend32(*ptrIn);
               }
	}

	return(buffer);
}

WIN32DLL_DEFINE
void
mutils_memmove(void *dest, __const void *src, const mutils_word32 n)
{
	mutils_word8 *ptr1;
	mutils_word8 *ptr2;
	mutils_word32 i;
	mutils_word32 *bigptr1;
	mutils_word32 *bigptr2;
	mutils_word32 words;
	mutils_word32 remainder;

	if ((dest == NULL) || (src == NULL) || (n == 0))
	{
		return;
	}

	bigptr1 = (mutils_word32 *) dest;
	bigptr2 = (mutils_word32 *) src;

	/* copy byte-by-byte for small and/or unaligned copies */
	if ((n < 16) || ((mutils_word32)dest & 0x3) || ((mutils_word32)src & 0x3))
	{
		return mutils_memcpy8(dest, src, n);
	}

	words = n >> 2;
	remainder = n - (words << 2);

	for (i = 0; i < words; i++, bigptr1++, bigptr2++)
	{
		*bigptr1 = *bigptr2;
	}

	ptr1 = (mutils_word8 *) bigptr1;
	ptr2 = (mutils_word8 *) bigptr2;

	for (i = 0; i < remainder; i++, ptr1++, ptr2++)
	{
		*ptr1 = *ptr2;
	}
}

WIN32DLL_DEFINE
int
mutils_memcmp(__const void *s1, const void *s2, const mutils_word32 n)
{
	if (n == 0)
	{
		return(0);
	}
	if (s1 == NULL)
	{
		if (s2 == NULL)
		{
			return(0);
		}
		return(-INT_MAX);
	}
	if (s2 == NULL)
	{
		return(INT_MAX);
	}

	return(memcmp(s1, s2, n));
}

/* Ugh. This could do nasty things, if a string isn't correctly terminated,
 * particularly as memory can be larger than can be stored in a 32-bit word.
 * If it is possible to produce an incorrect result, or segfault, then it
 * is not a robust solution.
 */

WIN32DLL_DEFINE
mutils_word32
mutils_strlen(__const mutils_word8 *str)
{
	mutils_word32 ret;
	mutils_word8 *endStr = (mutils_word8 *) str;

	if (str == NULL)
	{
		return(0);
	}

	while (*endStr != 0)
	{
		endStr++;
	}

	ret = endStr - str;

	return(ret);
}

WIN32DLL_DEFINE
mutils_word8 *
mutils_strdup(__const mutils_word8 *str)
{
	mutils_word8 *ret = NULL;
	mutils_word8 *ptr1;
	mutils_word8 *ptr2;
	mutils_word32 len;

	if (str == NULL)
	{
		return(NULL);
	}

	len = mutils_strlen(str) + 1;

	ret = (mutils_word8 *) mutils_malloc(len);

	if (ret != NULL)
	{
		ptr1 = (mutils_word8 *) str;
		ptr2 = ret;

		for (len = mutils_strlen(str); len > 0; len--, ptr1++, ptr2++)
		{
			*ptr2 = *ptr1;
		}
	}

	return(ret);
}

WIN32DLL_DEFINE
mutils_word8 *
mutils_strcat(mutils_word8 *dest, __const mutils_word8 *src)
{
	mutils_word8 *ptrIn = (mutils_word8 *) src;
	mutils_word8 *ptrOut = dest + mutils_strlen(dest);

	if (dest == NULL)
	{
		return(NULL);
	}
	if (src == NULL)
	{
		return(dest);
	}	
	while (*src != 0)
	{
		*ptrOut = *ptrIn;
		ptrOut++;
		ptrIn++;
	}
	*ptrOut = '\0';
	return((mutils_word8 *) dest);
}

WIN32DLL_DEFINE
mutils_word8 *
mutils_strcpy(mutils_word8 *dest, __const mutils_word8 *src)
{
	if (dest == NULL)
	{
		return(NULL);
	}
	return((mutils_word8 *) strcpy((char *) dest, (char *) src));
}

WIN32DLL_DEFINE
mutils_word8 *
mutils_strncpy(mutils_word8 *dest, __const mutils_word8 *src, const mutils_word32 n)
{
	if (dest == NULL)
	{
		return(NULL);
	}
	if (n == 0)
	{
		return(NULL);
	}
	return((mutils_word8 *) strncpy((char *) dest, (char *) src, n));
}

WIN32DLL_DEFINE
int
mutils_strcmp(__const mutils_word8 *src1, const mutils_word8 *src2)
{
	if (src1 == NULL)
	{
		if (src2 == NULL)
		{
			return(0);
		}
		return(-INT_MAX);
	}
	if (src2 == NULL)
	{
		return(INT_MAX);
	}
	return(strcmp((char *) src1, (char *) src2));
}

WIN32DLL_DEFINE
int
mutils_strncmp(__const mutils_word8 *src1, const mutils_word8 *src2, const mutils_word32 n)
{
	if (n == 0)
	{
		return(0);
	}
	if (src1 == NULL)
	{
		if (src2 == NULL)
		{
			return(0);
		}
		return(-INT_MAX);
	}
	if (src2 == NULL)
	{
		return(INT_MAX);
	}
	return(strncmp((char *) src1, (char *) src2, n));
}

WIN32DLL_DEFINE
long
mutils_strtol(__const mutils_word8 *nptr, mutils_word8 **endptr, const mutils_word8 base)
{
	return(strtol((char *) nptr, (char **) endptr, (int) base));
}

WIN32DLL_DEFINE
mutils_word8
mutils_val2char(mutils_word8 x)
{
	mutils_word8 out;

	static mutils_word8 *table = (mutils_word8 *) "0123456789abcdef";

	out = *(table + x);

	return(out);
}

WIN32DLL_DEFINE
mutils_word8 *
mutils_asciify(mutils_word8 *in, __const mutils_word32 len)
{
	mutils_word8 *ptrIn = in;
	mutils_word8 *buffer = mutils_malloc((2 * len) + 1);
	mutils_word8 *ptrOut = buffer;
	mutils_word32 loop;

	for (loop = 0; loop < len; loop++, ptrIn++)
	{
		*ptrOut++ = mutils_val2char((*ptrIn & 0xf0) >> 4);
		*ptrOut++ = mutils_val2char((*ptrIn & 0x0f));
	}
	return(buffer);
}

WIN32DLL_DEFINE
mutils_boolean
mutils_thequals(mutils_word8 *text, mutils_word8 *hash, __const mutils_word32 len)
{
	mutils_word8  *ptrText = text;
	mutils_word8  *ptrHash = hash;
	mutils_word32  loop;

	for (loop = 0; loop < len; loop++, ptrHash++)
	{
		if (mutils_val2char((*ptrHash & 0xf0) >> 4) != *ptrText++)
		{
			return(MUTILS_FALSE);
		}
		if (mutils_val2char((*ptrHash & 0x0f)) != *ptrText++)
		{
			return(MUTILS_FALSE);
		}
	}
	return(MUTILS_TRUE);
}

