/*-------------------------------------------------------------------------
 *
 * char.c--
 *    Functions for the built-in type char() and varchar().
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.4 1996/11/04 04:53:37 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>		/* for sprintf() */
#include <string.h>
#include "postgres.h"
#include "utils/palloc.h"
#include "utils/elog.h"

/*
 * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR()
 * is for blank-padded string whose length is specified in CREATE TABLE.
 * VARCHAR is for storing string whose length is at most the length specified
 * at CREATE TABLE time.
 *
 * It's hard to implement these types because we cannot figure out what
 * the length of the type from the type itself. I change (hopefully all) the
 * fmgr calls that invoke input functions of a data type to supply the
 * length also. (eg. in INSERTs, we have the tupleDescriptor which contains
 * the length of the attributes and hence the exact length of the char() or
 * varchar(). We pass this to bpcharin() or varcharin().) In the case where
 * we cannot determine the length, we pass in -1 instead and the input string
 * must be null-terminated.
 *
 * We actually implement this as a varlena so that we don't have to pass in
 * the length for the comparison functions. (The difference between "text"
 * is that we truncate and possibly blank-pad the string at insertion time.)
 *
 *                                                            - ay 6/95
 */


/***************************************************************************** 
 *   bpchar - char()                                                         *
 *****************************************************************************/

/*
 * bpcharin -
 *    converts a string of char() type to the internal representation.
 *    len is the length specified in () plus 4 bytes. (XXX dummy is here
 *    because we pass typelem as the second argument for array_in.)
 */
char *
bpcharin(char *s, int dummy, int typlen)
{
    char *result, *r;
    int	len = typlen - 4;
    int i;
    
    if (s == NULL)
	return((char *) NULL);

    if (typlen == -1) {
	/*
	 * this is here because some functions can't supply the typlen
	 */
	len = strlen(s);
	typlen = len + 4;
    }
    
    if (len > 4096)
	elog(WARN, "bpcharin: length of char() must be less than 4096");
    
    result = (char *) palloc(typlen);
    *(int32*)result = typlen;
    r = result + 4;
    for(i=0; i < len; i++, r++, s++) {
	*r = *s;
	if (*r == '\0')
	    break;
    }
    /* blank pad the string if necessary */
    for(; i < len; i++) {
	*r++ = ' ';
    }
    return(result);
}

char *
bpcharout(char *s)
{
    char *result;
    int len;

    if (s == NULL) {
	result = (char *) palloc(2);
	result[0] = '-';
	result[1] = '\0';
    } else {
	len = *(int32*)s - 4;
	result = (char *) palloc(len+1);
	strncpy(result, s+4, len);	/* these are blank-padded */
	result[len] = '\0';
    }
    return(result);
}

/***************************************************************************** 
 *   varchar - varchar()                                                     *
 *****************************************************************************/

/*
 * vcharin -
 *    converts a string of varchar() type to the internal representation.
 *    len is the length specified in () plus 4 bytes. (XXX dummy is here
 *    because we pass typelem as the second argument for array_in.)
 */
char *
varcharin(char *s, int dummy, int typlen)
{
    char *result;
    int	len = typlen - 4;
    
    if (s == NULL)
	return((char *) NULL);

    if (typlen == -1) {
	/*
	 * this is here because some functions can't supply the typlen
	 */
	len = strlen(s);
	typlen = len + 4;
    }
    
    if (len > 4096)
	elog(WARN, "varcharin: length of char() must be less than 4096");
    
    result = (char *) palloc(typlen);
    *(int32*)result = typlen;
    memset(result+4, 0, len);
    (void) strncpy(result+4, s, len);

    return(result);
}

char *
varcharout(char *s)
{
    char *result;
    int len;

    if (s == NULL) {
	result = (char *) palloc(2);
	result[0] = '-';
	result[1] = '\0';
    } else {
	len = *(int32*)s - 4;
	result = (char *) palloc(len+1);
	memset(result, 0, len+1);
	strncpy(result, s+4, len);
    }
    return(result);
}

/*****************************************************************************
 *  Comparison Functions used for bpchar 
 *****************************************************************************/

static int
bcTruelen(char *arg)
{
    char *s = arg + 4;
    int i;
    int len;

    len = *(int32*)arg - 4;
    for(i=len-1; i >= 0; i--) {
	if (s[i] != ' ')
	    break;
    }
    return (i+1);
}

int32
bpchareq(char *arg1, char *arg2)
{
    int len1, len2;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    if (len1!=len2)
	return 0;
    
    return(strncmp(arg1+4, arg2+4, len1) == 0);
}

int32
bpcharne(char *arg1, char *arg2)
{
    int len1, len2;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    if (len1!=len2)
	return 1;

    return(strncmp(arg1+4, arg2+4, len1) != 0);
}

int32
bpcharlt(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;
    
    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (cmp == 0)
	return (len1<len2);
    else 
	return (cmp < 0);
}

int32
bpcharle(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (0 == cmp)
      return (int32)(len1 <= len2 ? 1 : 0);
    else
      return (int32)(cmp <= 0);
}

int32
bpchargt(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (cmp == 0)
	return (len1 > len2);
    else
	return (cmp > 0);
}

int32
bpcharge(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (0 == cmp)
      return (int32)(len1 >= len2 ? 1 : 0);
    else
      return (int32)(cmp >= 0);
}

int32
bpcharcmp(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if ((0 == cmp) && (len1 != len2))
      return (int32)(len1 < len2 ? -1 : 1);
    else
      return cmp;
}

/*****************************************************************************
 *  Comparison Functions used for varchar 
 *****************************************************************************/

static int
vcTruelen(char *arg)
{
    char *s = arg + 4;
    int i;
    int len;

    len = *(int32*)arg - 4;
    for(i=0; i < len; i++) {
	if (*s++ == '\0')
	    break;
    }
    return i;
}

int32
varchareq(char *arg1, char *arg2)
{
    int len1, len2;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);

    if (len1!=len2)
	return 0;
    
    return(strncmp(arg1+4, arg2+4, len1) == 0);
}

int32
varcharne(char *arg1, char *arg2)
{
    int len1, len2;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);

    if (len1!=len2)
	return 1;

    return(strncmp(arg1+4, arg2+4, len1) != 0);
}

int32
varcharlt(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;
    
    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (cmp == 0)
	return (len1<len2);
    else 
	return (cmp < 0);
}

int32
varcharle(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (0 == cmp)
      return (int32)( len1 <= len2 ? 1 : 0);
    else
      return (int32)(cmp <= 0);
}

int32
varchargt(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (cmp == 0)
	return (len1 > len2);
    else
	return (cmp > 0);
}

int32
varcharge(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    if (arg1 == NULL || arg2 == NULL)
	return((int32) 0);
    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);

    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
    if (0 == cmp)
      return (int32)(len1 >= len2 ? 1 : 0);
    else 
      return (int32)(cmp >= 0);

}

int32
varcharcmp(char *arg1, char *arg2)
{
    int len1, len2;
    int cmp;

    len1 = vcTruelen(arg1);
    len2 = vcTruelen(arg2);
    cmp = (strncmp(arg1+4, arg2+4, Min(len1,len2)));
    if ((0 == cmp) && (len1 != len2))
      return (int32)(len1 < len2 ? -1 : 1);
    else
      return (int32)(cmp);
}

/*****************************************************************************
 * Hash functions (modified from hashtext in access/hash/hashfunc.c)
 *****************************************************************************/

uint32 hashbpchar(struct varlena *key)
{
    int keylen;
    char *keydata;
    uint32 n;
    int loop;

    keydata = VARDATA(key);
    keylen = bcTruelen((char*)key);

#define HASHC   n = *keydata++ + 65599 * n

    n = 0;
    if (keylen > 0) {
	loop = (keylen + 8 - 1) >> 3;
	
	switch (keylen & (8 - 1)) {
	case 0:
	    do {	/* All fall throughs */
		HASHC;
	    case 7:
		HASHC;
	    case 6:
		HASHC;
	    case 5:
		HASHC;
	    case 4:
		HASHC;
	    case 3:
		HASHC;
	    case 2:
		HASHC;
	    case 1:
		HASHC;
	    } while (--loop);
	}
    }
    return (n);
}	

uint32 hashvarchar(struct varlena *key)
{
    int keylen;
    char *keydata;
    uint32 n;
    int loop;

    keydata = VARDATA(key);
    keylen = vcTruelen((char*)key);

#define HASHC   n = *keydata++ + 65599 * n

    n = 0;
    if (keylen > 0) {
	loop = (keylen + 8 - 1) >> 3;
	
	switch (keylen & (8 - 1)) {
	case 0:
	    do {	/* All fall throughs */
		HASHC;
	    case 7:
		HASHC;
	    case 6:
		HASHC;
	    case 5:
		HASHC;
	    case 4:
		HASHC;
	    case 3:
		HASHC;
	    case 2:
		HASHC;
	    case 1:
		HASHC;
	    } while (--loop);
	}
    }
    return (n);
}	

