/*
 * The contents of this file are subject to the AOLserver Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://aolserver.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is AOLserver Code and related documentation
 * distributed by AOL.
 * 
 * The Initial Developer of the Original Code is America Online,
 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
 * Inc. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU General Public License (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above.  If you wish
 * to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the
 * License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the GPL.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under either the License or the GPL.
 */


/*
 * index.c --
 *
 *	Implement the index data type. 
 */

static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/index.c,v 1.5 2003/03/07 18:08:26 vasiljevic Exp $, compiled: " __DATE__ " " __TIME__;

#include "nsd.h"

/*
 * Local functions defined in this file
 */

static int BinSearch(void **elp, void **list, int n, Ns_IndexCmpProc *cmp);
static int BinSearchKey(void *key, void **list, int n, Ns_IndexCmpProc *cmp);


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexInit --
 *
 *	Initialize a new index. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	Will allocate space for the index elements from the heap. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexInit(Ns_Index *indexPtr, int inc,
	     int (*CmpEls) (const void *, const void *),
	     int (*CmpKeyWithEl) (const void *, const void *))
{
    indexPtr->n = 0;
    indexPtr->max = inc;
    indexPtr->inc = inc;

    indexPtr->CmpEls = CmpEls;
    indexPtr->CmpKeyWithEl = CmpKeyWithEl;

    indexPtr->el = (void **) ns_malloc(inc * sizeof(void *));
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexTrunc --
 *
 *	Remove all elements from an index. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	Frees and mallocs element memory. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexTrunc(Ns_Index* indexPtr)
{
    indexPtr->n = 0;
    ns_free(indexPtr->el);
    indexPtr->max = indexPtr->inc;
    indexPtr->el = (void **) ns_malloc(indexPtr->inc * sizeof(void *));
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexDestroy --
 *
 *	Release all of an index's memory. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	Frees elements. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexDestroy(Ns_Index *indexPtr)
{
    indexPtr->CmpEls = NULL;
    indexPtr->CmpKeyWithEl = NULL;
    ns_free(indexPtr->el);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexDup --
 *
 *	Make a copy of an index. 
 *
 * Results:
 *	A pointer to a copy of the index. 
 *
 * Side effects:
 *	Mallocs memory for index and elements. 
 *
 *----------------------------------------------------------------------
 */

Ns_Index *
Ns_IndexDup(Ns_Index *indexPtr)
{
    Ns_Index *newPtr;

    newPtr = (Ns_Index *) ns_malloc(sizeof(Ns_Index));
    memcpy(newPtr, indexPtr, sizeof(Ns_Index));
    newPtr->el = (void **) ns_malloc(indexPtr->max * sizeof(void *));
    memcpy(newPtr->el, indexPtr->el, indexPtr->n * sizeof(void *));

    return newPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexFind --
 *
 *	Find a key in an index. 
 *
 * Results:
 *	A pointer to the element, or NULL if none found. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

void *
Ns_IndexFind(Ns_Index *indexPtr, void *key)
{
    void **pPtrPtr;

    pPtrPtr = (void **) bsearch(key, indexPtr->el, (size_t)indexPtr->n, 
                                sizeof(void *), indexPtr->CmpKeyWithEl);

    return pPtrPtr ? *pPtrPtr : NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexFindInf --
 *
 *	Find the elment with the key, or if none exists, the element 
 *	before which the key would appear. 
 *
 * Results:
 *	An element, or NULL if the key is not there AND would be the 
 *	last element in the list. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

void *
Ns_IndexFindInf(Ns_Index *indexPtr, void *key)
{
    if (indexPtr->n > 0) {
        int i;

        i = BinSearchKey(key, indexPtr->el, indexPtr->n,
			 indexPtr->CmpKeyWithEl);

        if (i >= indexPtr->n) {
          return NULL;
        }

        if ((i > 0) && \
            ((indexPtr->CmpKeyWithEl)(key, &(indexPtr->el[i])) != 0))  {
            return indexPtr->el[i - 1];
        } else {
            return indexPtr->el[i];
        }
    } else {
        return NULL;
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexFindMultiple --
 *
 *	Find all elements that match key. 
 *
 * Results:
 *	An array of pointers to matching elements, termianted with a 
 *	null poitner. 
 *
 * Side effects:
 *	Will allocate memory for the return result. 
 *
 *----------------------------------------------------------------------
 */

void **
Ns_IndexFindMultiple(Ns_Index *indexPtr, void *key)
{
    void **firstPtrPtr;
    void **retPtrPtr;
    int    i;
    int    n;

    /*
     * Find a place in the array that matches the key
     */
    
    firstPtrPtr = (void **) bsearch(key, indexPtr->el, (size_t)indexPtr->n,
				    sizeof(void *), indexPtr->CmpKeyWithEl);

    if (firstPtrPtr == NULL) {
        return NULL;
    } else {
        /*
	 * Search linearly back to make sure we've got the first one
	 */
	
        while (firstPtrPtr != indexPtr->el
            && indexPtr->CmpKeyWithEl(key, firstPtrPtr - 1) == 0) {
            firstPtrPtr--;
        }
        /*
	 * Search linearly forward to find out how many there are
	 */
	
        n = indexPtr->n - (firstPtrPtr - indexPtr->el);
        for (i = 1; i < n &&
		 indexPtr->CmpKeyWithEl(key, firstPtrPtr + i) == 0; i++)
	    ;

        /*
	 * Build array of values to return
	 */
	
        retPtrPtr = ns_malloc((i + 1) * sizeof(void *));
        memcpy(retPtrPtr, firstPtrPtr, i * sizeof(void *));
        retPtrPtr[i] = NULL;

        return retPtrPtr;
    }
}


/*
 *----------------------------------------------------------------------
 *
 * BinSearch --
 *
 *	Modified from BinSearch in K&R. 
 *
 * Results:
 *	The position where an element should be inserted even if it 
 *	does not already exist. bsearch will just return NULL. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

static int
BinSearch(void **elPtrPtr, void **listPtrPtr, int n, Ns_IndexCmpProc *cmpProc)
{
    int cond = 0, low = 0, high = 0, mid = 0;

    low = 0;
    high = n - 1;
    while (low <= high) {
        mid = (low + high) / 2;
        if ((cond = (*cmpProc) (elPtrPtr, listPtrPtr + mid)) < 0) {
            high = mid - 1;
        } else if (cond > 0) {
            low = mid + 1;
        } else {
            return mid;
        }
    }
    return (high < mid) ? mid : low;
}


/*
 *----------------------------------------------------------------------
 *
 * BinSearchKey --
 *
 *	Like BinSearch, but takes a key instead of an element 
 *	pointer. 
 *
 * Results:
 *	See BinSearch. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

static int
BinSearchKey(void *key, void **listPtrPtr, int n, Ns_IndexCmpProc *cmpProc)
{
    int cond = 0, low = 0, high = 0, mid = 0;

    low = 0;
    high = n - 1;
    while (low <= high) {
        mid = (low + high) / 2;
        if ((cond = (*cmpProc) (key, listPtrPtr + mid)) < 0) {
            high = mid - 1;
        } else if (cond > 0) {
            low = mid + 1;
        } else {
            return mid;
        }
    }
    return (high < mid) ? mid : low;
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexAdd --
 *
 *	Add an element to an index. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	May allocate extra element memory. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexAdd(Ns_Index *indexPtr, void *el)
{
    int i;
    int j;

    if (indexPtr->n == indexPtr->max) {
        indexPtr->max += indexPtr->inc;
        indexPtr->el = (void **) ns_realloc(indexPtr->el,
					    indexPtr->max * sizeof(void *));
    } else if (indexPtr->max == 0) {
        indexPtr->max += indexPtr->inc;
        indexPtr->el = (void **) ns_malloc(indexPtr->max * sizeof(void *));
    }
    if (indexPtr->n > 0) {
        i = BinSearch(&el, indexPtr->el, indexPtr->n, indexPtr->CmpEls);
    } else {
        i = 0;
    }

    if (i < indexPtr->n) {
        for (j = indexPtr->n; j > i; j--) {
            indexPtr->el[j] = indexPtr->el[j - 1];
        }
    }
    indexPtr->el[i] = el;
    indexPtr->n++;
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexDel --
 *
 *	Remove an element from an index. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexDel(Ns_Index *indexPtr, void *el)
{
    int i;
    int done;
    int j;

    done = 0;
    for (i = 0; i < indexPtr->n && !done; i++) {
        if (indexPtr->el[i] == el) {
            indexPtr->n--;
            if (i < indexPtr->n) {
                for (j = i; j < indexPtr->n; j++) {
                    indexPtr->el[j] = indexPtr->el[j + 1];
                }
            }
            done = 1;
        }
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexEl --
 *
 *	Find the i'th element of an index. 
 *
 * Results:
 *	Element. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

void *
Ns_IndexEl(Ns_Index *indexPtr, int i)
{
    return indexPtr->el[i];
}


/*
 *----------------------------------------------------------------------
 *
 * CmpStr --
 *
 *	Default comparison function. 
 *
 * Results:
 *	See strcmp. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

static int
CmpStr(char **leftPtr, char **rightPtr)
{
    return strcmp(*leftPtr, *rightPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * CmpKeyWithStr --
 *
 *	Default comparison function, with key. 
 *
 * Results:
 *	See strcmp. 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

static int
CmpKeyWithStr(char *key, char **elPtr)
{
    return strcmp(key, *elPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexStringInit --
 *
 *	Initialize an index where the elements themselves are 
 *	strings. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	See Ns_IndexInit. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexStringInit(Ns_Index *indexPtr, int inc)
{
    Ns_IndexInit(indexPtr, inc, (int (*) (const void *, const void *)) CmpStr,
        (int (*) (const void *, const void *)) CmpKeyWithStr);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexStringDup --
 *
 *	Make a copy of an index, using ns_strdup to duplicate the 
 *	keys. 
 *
 * Results:
 *	A new index. 
 *
 * Side effects:
 *	Wil make memory copies of the elements. 
 *
 *----------------------------------------------------------------------
 */

Ns_Index *
Ns_IndexStringDup(Ns_Index *indexPtr)
{
    Ns_Index *newPtr;
    int       i;

    newPtr = (Ns_Index *) ns_malloc(sizeof(Ns_Index));
    memcpy(newPtr, indexPtr, sizeof(Ns_Index));
    newPtr->el = (void **) ns_malloc(indexPtr->max * sizeof(void *));

    for (i = 0; i < newPtr->n; i++) {
        newPtr->el[i] = ns_strdup(indexPtr->el[i]);
    }

    return newPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexStringAppend --
 *
 *	Append one index of strings to another. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	Will append to the first index.
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexStringAppend(Ns_Index *addtoPtr, Ns_Index *addfromPtr)
{
    int i;

    for (i = 0; i < addfromPtr->n; i++) {
        Ns_IndexAdd(addtoPtr, ns_strdup(addfromPtr->el[i]));
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexStringDestroy --
 *
 *	Free an index of strings. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	See Ns_IndexDestroy. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexStringDestroy(Ns_Index *indexPtr)
{
    int i;

    for (i = 0; i < indexPtr->n; i++) {
        ns_free(indexPtr->el[i]);
    }

    Ns_IndexDestroy(indexPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexStringTrunc --
 *
 *	Remove all elements from an index of strings. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	See Ns_IndexTrunc. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexStringTrunc(Ns_Index *indexPtr)
{
    int i;

    for (i = 0; i < indexPtr->n; i++) {
        ns_free(indexPtr->el[i]);
    }

    Ns_IndexTrunc(indexPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * CmpInts --
 *
 *	Default comparison function for an index of ints. 
 *
 * Results:
 *	-1: left < right; 1: left > right; 0: left == right 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

static int
CmpInts(int *leftPtr, int *rightPtr)
{
    if (*leftPtr == *rightPtr) {
        return 0;
    } else {
        return *leftPtr < *rightPtr ? -1 : 1;
    }
}


/*
 *----------------------------------------------------------------------
 *
 * CmpKeyWithInt --
 *
 *	Default comparison function for an index of ints, with key. 
 *
 * Results:
 *	-1: key < el; 1: key > el; 0: key == el 
 *
 * Side effects:
 *	None. 
 *
 *----------------------------------------------------------------------
 */

static int
CmpKeyWithInt(int *keyPtr, int *elPtr)
{
    if (*keyPtr == *elPtr) {
        return 0;
    } else {
        return *keyPtr < *elPtr ? -1 : 1;
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_IndexIntInit --
 *
 *	Initialize an index whose elements will be integers. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	See Ns_IndexInit. 
 *
 *----------------------------------------------------------------------
 */

void
Ns_IndexIntInit(Ns_Index *indexPtr, int inc)
{
    Ns_IndexInit(indexPtr, inc, (int (*) (const void *, const void *)) CmpInts,
        (int (*) (const void *, const void *)) CmpKeyWithInt);
}
