/* Copyright (C) 2005-2006 Datapark corp. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
*/

#include "dps_config.h"
#include "dps_charsetutils.h"
#include "dps_unidata.h"

#include <ctype.h>
#include <string.h>


__C_LINK int dps_tolower(int c) {
  if (c < 0x80) return (int)DpsUniToLower((dpsunicode_t)c);
  return tolower(c);
}


/***** dps_memmove ****/

/*
 * sizeof(dps_word) MUST BE A POWER OF TWO SO THAT dps_wmask BELOW IS ALL ONES
 */
#define dps_wsize sizeof(dps_word)
#define dps_wmask (dps_wsize - 1)

void * dps_memmove(void *dst0, const void *src0, size_t length) {
	char *dst = dst0;
	const char *src = src0;
	size_t t;

	if (length == 0 || dst == src)		/* nothing to do */
		goto done;

	/*
	 * Macros: loop-t-times; and loop-t-times, t>0
	 */
#define	DPS_TLOOP(s) if (t) DPS_TLOOPo(s)
#define	DPS_TLOOP1(s) do { s; } while (--t)
#define DPS_TLOOPo(s)			  \
	{ \
		register size_t n = (t + 7) / 8; \
		switch(t % 8) {           \
		case 0:	do {	s;        \
		case 7:		s;        \
		case 6:		s;        \
		case 5:		s;        \
		case 4:		s;        \
		case 3:		s;        \
		case 2:		s;        \
		case 1:		s;        \
			} while(--n > 0); \
		}                         \
	}

	if ((unsigned long)dst < (unsigned long)src) {
		/*
		 * Copy forward.
		 */
		t = (dps_uintptr_t)src;	/* only need low bits */
		if ((t | (dps_uintptr_t)dst) & dps_wmask) {
			/*
			 * Try to align operands.  This cannot be done
			 * unless the low bits match.
			 */
			if ((t ^ (dps_uintptr_t)dst) & dps_wmask || length < dps_wsize)
				t = length;
			else
				t = dps_wsize - (t & dps_wmask);
			length -= t;
			DPS_TLOOPo(*dst++ = *src++);
		}
		/*
		 * Copy whole words, then mop up any trailing bytes.
		 */
		t = length / dps_wsize;
		DPS_TLOOP(*(dps_word *)dst = *(dps_word *)src; src += dps_wsize; dst += dps_wsize);
		t = length & dps_wmask;
		DPS_TLOOP(*dst++ = *src++);
	} else {
		/*
		 * Copy backwards.  Otherwise essentially the same.
		 * Alignment works as before, except that it takes
		 * (t & dps_wmask) bytes to align, not dps_wsize - (t & dps_wmask).
		 */
		src += length;
		dst += length;
		t = (dps_uintptr_t)src;
		if ((t | (dps_uintptr_t)dst) & dps_wmask) {
			if ((t ^ (dps_uintptr_t)dst) & dps_wmask || length <= dps_wsize)
				t = length;
			else
				t &= dps_wmask;
			length -= t;
			DPS_TLOOPo(*--dst = *--src);
		}
		t = length / dps_wsize;
		DPS_TLOOP(src -= dps_wsize; dst -= dps_wsize; *(dps_word *)dst = *(dps_word *)src);
		t = length & dps_wmask;
		DPS_TLOOP(*--dst = *--src);
	}
done:
	return (dst0);
}

/**** /dps_memmove ****/


void * dps_memcpy(void *dst0, const void *src0, size_t length) {
  register size_t n = (length + 7) / 8;
  register char *dst = dst0, *src = src0;
  switch(length % 8) {
  case 0:	do {	*dst++ = *src++;
  case 7:		*dst++ = *src++;
  case 6:		*dst++ = *src++;
  case 5:		*dst++ = *src++;
  case 4:		*dst++ = *src++;
  case 3:		*dst++ = *src++;
  case 2:		*dst++ = *src++;
  case 1:		*dst++ = *src++;
                 } while(--n > 0);
  }
  return dst0;
}

void * dps_strcpy(void *dst0, const void *src0) {
  register char *dst = dst0, *src = src0;
  while (*dst++ = *src++);
  return dst0;
#if 0
  register size_t n = dps_strlen(src0);
  return dps_memmove(dst0, src0, n + 1);
#endif
}

#if 0
void * dps_strncpy(void *dst0, const void *src0, size_t length) {
  register size_t n = dps_strlen(src0) + 1;
  return dps_memmove(dst0, src0, (n < length) ? n : length);
}

#else

void * dps_strncpy(void *dst0, const void *src0, size_t length) {
  if (length) {
    register size_t n = (length + 7) / 8;
    register char *dst = dst0, *src = src0;
    switch(length % 8) {
    case 0:	do {	if (!(*dst++ = *src++)) break;
    case 7:		if (!(*dst++ = *src++)) break;
    case 6:		if (!(*dst++ = *src++)) break;
    case 5:		if (!(*dst++ = *src++)) break;
    case 4:		if (!(*dst++ = *src++)) break;
    case 3:		if (!(*dst++ = *src++)) break;
    case 2:		if (!(*dst++ = *src++)) break;
    case 1:		if (!(*dst++ = *src++)) break;
                 } while(--n > 0);
    }
  }
  return dst0;
}
#endif

void * dps_strcat(void *dst0, const void *src0) {
  register size_t n = dps_strlen(dst0);
  dps_strcpy((char*)dst0 + n, src0);
  return dst0;
}

void * dps_strncat(void *dst0, const void *src0, size_t length) {
  register size_t n = dps_strlen(dst0);
  dps_strncpy((char*)dst0 + n, src0, length);
  return dst0;
}

size_t dps_strlen(const char *src) {
#if 1
  register const char *s;

  for (s = src; *s; s++);
  return(s - src);

#else
    size_t len = 0;
    switch(
#if SIZEOF_CHARP == 4
	   (dps_uint4)src & 3
#else
	   (dps_uint8)src & 3
#endif
	   ) {
    case 1: if (*src++ == 0) return len; len++;
    case 2: if (*src++ == 0) return len; len++;
    case 3: if (*src++ == 0) return len; len++;
    default:
      for(;;) {
        dps_uint4 x = *(dps_uint4*)src;
#ifdef WORDS_BIGENDIAN
        if((x & 0xFF000000) == 0) return len;
        if((x & 0xFF0000) == 0) return len + 1;
        if((x & 0xFF00) == 0) return len + 2;
        if((x & 0xFF) == 0) return len + 3;
#else
        if((x & 0xFF) == 0) return len;
        if((x & 0xFF00) == 0) return len + 1;
        if((x & 0xFF0000) == 0) return len + 2;
        if((x & 0xFF000000) == 0) return len + 3;
#endif
        src += 4, len += 4;
      }
    }
    /* this point is reachless. possible compilation warning is OK */
#endif
}

