
/*
 * bltArrayObj.c --
 *
 * Copyright 2000 Silicon Metrics, Inc.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that the copyright notice and warranty
 * disclaimer appear in supporting documentation, and that the names
 * of Lucent Technologies or any of their entities not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 *
 * Lucent Technologies disclaims all warranties with regard to this
 * software, including all implied warranties of merchantability and
 * fitness.  In no event shall Lucent Technologies be liable for any
 * special, indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits, whether in
 * an action of contract, negligence or other tortuous action, arising
 * out of or in connection with the use or performance of this
 * software.
 *
 *	The array Tcl object was created by George A. Howlett.
 */

#include "bltInt.h"

#ifndef NO_ARRAY
#include "bltHash.h"

static Tcl_DupInternalRepProc DupArrayInternalRep;
static Tcl_FreeInternalRepProc FreeArrayInternalRep;
static Tcl_UpdateStringProc UpdateStringOfArray;
static Tcl_SetFromAnyProc SetArrayFromAny;

static Tcl_ObjType arrayObjType = {
    "array",
    FreeArrayInternalRep,	/* Called when an object is freed. */
    DupArrayInternalRep,	/* Copies an internal representation 
				 * from one object to another. */
    UpdateStringOfArray,	/* Creates string representation from
				 * an object's internal representation. */
    SetArrayFromAny,		/* Creates valid internal representation 
				 * from an object's string representation. */
};

#if 1     
static int
SetArrayFromAny(
    Tcl_Interp *interp,
    Tcl_Obj *objPtr)
{
    Blt_HashEntry *hPtr;
    Blt_HashTable *tablePtr;
    Tcl_Obj *elemObjPtr, **vobjv;
    Tcl_ObjType *oldTypePtr;
    char *string;
    int isNew;
    int nElem;
    register int i;

    if (objPtr->typePtr == &arrayObjType) {
	return TCL_OK;
    }
    if (Tcl_ListObjGetElements(interp, objPtr, &nElem, &vobjv) != TCL_OK) {
        return TCL_ERROR;
    }
    if (nElem%2) {
        if (interp != NULL) {
		    string = Tcl_GetString(objPtr);
            Tcl_AppendResult(interp, "odd length: ", string, 0);
        }
        return TCL_ERROR;
    }
    tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
    assert(tablePtr);
    Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
    for (i = 0; i < nElem; i += 2) {
	hPtr = Blt_CreateHashEntry(tablePtr, Tcl_GetString(vobjv[i]), &isNew);
	elemObjPtr = vobjv[i + 1];
	Blt_SetHashValue(hPtr, elemObjPtr);

	/* Make sure we increment the reference count */
	Tcl_IncrRefCount(elemObjPtr);
    }
    
    oldTypePtr = objPtr->typePtr;
    if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
        oldTypePtr->freeIntRepProc(objPtr);
    }
    objPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
    objPtr->typePtr = &arrayObjType;

    return TCL_OK;
}
#else
static int
SetArrayFromAny(
    Tcl_Interp *interp,
    Tcl_Obj *objPtr)
{
    Blt_HashEntry *hPtr;
    Blt_HashTable *tablePtr;
    Tcl_Obj *elemObjPtr;
    Tcl_ObjType *oldTypePtr = objPtr->typePtr;
    char **elemArr;
    char *string;
    int isNew;
    int nElem;
    register int i;

    if (objPtr->typePtr == &arrayObjType) {
	return TCL_OK;
    }
    /*
     * Get the string representation. Make it up-to-date if necessary.
     */
    string = Tcl_GetString(objPtr);
    if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
	return TCL_ERROR;
    }
    if (nElem%2) {
        if (interp != NULL) {
            Tcl_AppendResult(interp, "odd length: ", string, 0);
        }
        Blt_Free(elemArr);
        return TCL_ERROR;
    }
    tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
    assert(tablePtr);
    Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
    for (i = 0; i < nElem; i += 2) {
	hPtr = Blt_CreateHashEntry(tablePtr, elemArr[i], &isNew);
	elemObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1);
	Blt_SetHashValue(hPtr, elemObjPtr);

	/* Make sure we increment the reference count */
	Tcl_IncrRefCount(elemObjPtr);
    }
    
    if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
	oldTypePtr->freeIntRepProc(objPtr);
    }
    objPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
    objPtr->typePtr = &arrayObjType;
    Blt_Free(elemArr);

    return TCL_OK;
}
#endif

static void
DupArrayInternalRep(
    Tcl_Obj *srcPtr,		/* Object with internal rep to copy. */
    Tcl_Obj *destPtr)		/* Object with internal rep to set. */
{
    Blt_HashEntry *hPtr, *h2Ptr;
    Blt_HashSearch cursor;
    Blt_HashTable *srcTablePtr, *destTablePtr;
    Tcl_Obj *valueObjPtr;
    char *key;
    int isNew;

    srcTablePtr = (Blt_HashTable *)srcPtr->internalRep.otherValuePtr;
    destTablePtr = Blt_Malloc(sizeof(Blt_HashTable));
    assert(destTablePtr);
    Blt_InitHashTable(destTablePtr, BLT_STRING_KEYS);
    for (hPtr = Blt_FirstHashEntry(srcTablePtr, &cursor); hPtr != NULL;
	 hPtr = Blt_NextHashEntry(&cursor)) {
	key = Blt_GetHashKey(srcTablePtr, hPtr);
	h2Ptr = Blt_CreateHashEntry(destTablePtr, key, &isNew);
	valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
        assert (valueObjPtr != NULL);
	Blt_SetHashValue(h2Ptr, valueObjPtr );

	/* Make sure we increment the reference count now that both
	 * array objects are using the same elements. */
	Tcl_IncrRefCount(valueObjPtr);
    }
    Tcl_InvalidateStringRep(destPtr);
    destPtr->internalRep.otherValuePtr = (VOID *)destTablePtr;
    destPtr->typePtr = &arrayObjType;
}

static void
UpdateStringOfArray(
    Tcl_Obj *objPtr)		/* Array object whose string rep to update. */
{
    Tcl_DString dString;
    Blt_HashTable *tablePtr;
    Blt_HashEntry *hPtr;
    Blt_HashSearch cursor;
    Tcl_Obj *elemObjPtr;

    tablePtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
    Tcl_DStringInit(&dString);
    for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
	 hPtr = Blt_NextHashEntry(&cursor)) {
	elemObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
	Tcl_DStringAppendElement(&dString, Blt_GetHashKey(tablePtr, hPtr));
	Tcl_DStringAppendElement(&dString, elemObjPtr == NULL?"":Tcl_GetString(elemObjPtr));
    }
    objPtr->bytes = Blt_Strdup(Tcl_DStringValue(&dString));
    objPtr->length = strlen(Tcl_DStringValue(&dString));
    Tcl_DStringFree(&dString);
}

static void
FreeArrayInternalRep(
    Tcl_Obj *objPtr)		/* Array object to release. */
{
    Blt_HashEntry *hPtr;
    Blt_HashSearch cursor;
    Blt_HashTable *tablePtr;
    Tcl_Obj *elemObjPtr;
    
    Tcl_InvalidateStringRep(objPtr);
    tablePtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
    for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
	 hPtr = Blt_NextHashEntry(&cursor)) {
	elemObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
	Tcl_DecrRefCount(elemObjPtr);
    }
    Blt_DeleteHashTable(tablePtr);
    Blt_Free(tablePtr);
}

int
Blt_GetArrayFromObj(
    Tcl_Interp *interp,
    Tcl_Obj *objPtr,
    Blt_HashTable **tablePtrPtr)
{
    if (objPtr->typePtr == &arrayObjType) {
	*tablePtrPtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
	return TCL_OK;
    }
    if (SetArrayFromAny(interp, objPtr) == TCL_OK) {
	*tablePtrPtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
	return TCL_OK;
    }
    return TCL_ERROR;
}
    
Tcl_Obj *
Blt_NewArrayObj(
    int objc,
    Tcl_Obj *objv[])
{
    Blt_HashEntry *hPtr;
    Blt_HashTable *tablePtr;
    Tcl_Obj *arrayObjPtr, *objPtr;
    int isNew;
    register int i;

    if (objc % 2) {
        return NULL;
    }
    tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
    assert(tablePtr);
    Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);

    for (i = 0; i < objc; i += 2) {
	hPtr = Blt_CreateHashEntry(tablePtr, Tcl_GetString(objv[i]), &isNew);
	if ((i + 1) == objc) {
	    objPtr = Tcl_NewStringObj("",-1);
	} else {
	    objPtr = objv[i+1];
	}
	Tcl_IncrRefCount(objPtr);
	if (!isNew) {
	    Tcl_Obj *oldObjPtr;

	    oldObjPtr = Blt_GetHashValue(hPtr);
	    Tcl_DecrRefCount(oldObjPtr);
	}
	Blt_SetHashValue(hPtr, objPtr);
    }
    arrayObjPtr = Tcl_NewObj(); 
    /* 
     * Reference counts for entry objects are initialized to 0. They
     * are incremented as they are inserted into the tree via the 
     * Blt_TreeSetValue call. 
     */
    arrayObjPtr->refCount = 0;	
    arrayObjPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
    arrayObjPtr->bytes = NULL;
    arrayObjPtr->length = 0; 
    arrayObjPtr->typePtr = &arrayObjType;
    return arrayObjPtr;
}

int
Blt_IsArrayObj(
    Tcl_Obj *objPtr)
{
    return (objPtr->typePtr == &arrayObjType);
}

/*ARGSUSED*/
void
Blt_RegisterArrayObj(
    Tcl_Interp *interp)		/* Not used. */
{
    Tcl_RegisterObjType(&arrayObjType);
}
#endif /* NO_ARRAY */
