diff options
| author | hershey <hershey> | 1999-05-06 02:34:42 (GMT) | 
|---|---|---|
| committer | hershey <hershey> | 1999-05-06 02:34:42 (GMT) | 
| commit | e87fc48c3d08b0973fd342dedfaa528261775909 (patch) | |
| tree | 8a8696c4290124c0cda743f831a3dec5c797b2b5 | |
| parent | 83892355ab18e4509d6b2a3dc6a1e9a8c2943033 (diff) | |
| download | tcl-e87fc48c3d08b0973fd342dedfaa528261775909.zip tcl-e87fc48c3d08b0973fd342dedfaa528261775909.tar.gz tcl-e87fc48c3d08b0973fd342dedfaa528261775909.tar.bz2  | |
ran dos2unix on this file--Irix couldn't build Tcl because it detected strange
chars at the end of each line.
| -rw-r--r-- | generic/tclLiteral.c | 2124 | 
1 files changed, 1062 insertions, 1062 deletions
diff --git a/generic/tclLiteral.c b/generic/tclLiteral.c index 99e46ae..37c9be9 100644 --- a/generic/tclLiteral.c +++ b/generic/tclLiteral.c @@ -1,1062 +1,1062 @@ -/* 
 - * tclLiteral.c --
 - *
 - *	Implementation of the global and ByteCode-local literal tables
 - *	used to manage the Tcl objects created for literal values during
 - *	compilation of Tcl scripts. This implementation borrows heavily
 - *	from the more general hashtable implementation of Tcl hash tables
 - *	that appears in tclHash.c.
 - *
 - * Copyright (c) 1997-1998 Sun Microsystems, Inc.
 - *
 - * See the file "license.terms" for information on usage and redistribution
 - * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 - *
 - * RCS: @(#) $Id: tclLiteral.c,v 1.5 1999/05/05 00:35:41 surles Exp $
 - */
 -
 -#include "tclInt.h"
 -#include "tclCompile.h"
 -#include "tclPort.h"
 -/*
 - * When there are this many entries per bucket, on average, rebuild
 - * a literal's hash table to make it larger.
 - */
 -
 -#define REBUILD_MULTIPLIER	3
 -
 -/*
 - * Procedure prototypes for static procedures in this file:
 - */
 -
 -static int		AddLocalLiteralEntry _ANSI_ARGS_((
 -			    CompileEnv *envPtr, LiteralEntry *globalPtr,
 -			    int localHash));
 -static void		ExpandLocalLiteralArray _ANSI_ARGS_((
 -			    CompileEnv *envPtr));
 -static unsigned int	HashString _ANSI_ARGS_((CONST char *bytes,
 -			    int length));
 -static void		RebuildLiteralTable _ANSI_ARGS_((
 -			    LiteralTable *tablePtr));
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclInitLiteralTable --
 - *
 - *	This procedure is called to initialize the fields of a literal table
 - *	structure for either an interpreter or a compilation's CompileEnv
 - *	structure.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects: 
 - *	The literal table is made ready for use.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -void
 -TclInitLiteralTable(tablePtr)
 -    register LiteralTable *tablePtr; /* Pointer to table structure, which
 -				      * is supplied by the caller. */
 -{
 -#if (TCL_SMALL_HASH_TABLE != 4) 
 -    panic("TclInitLiteralTable: TCL_SMALL_HASH_TABLE is %d, not 4\n",
 -	    TCL_SMALL_HASH_TABLE);
 -#endif
 -    
 -    tablePtr->buckets = tablePtr->staticBuckets;
 -    tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
 -    tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
 -    tablePtr->numBuckets = TCL_SMALL_HASH_TABLE;
 -    tablePtr->numEntries = 0;
 -    tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER;
 -    tablePtr->mask = 3;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclDeleteLiteralTable --
 - *
 - *	This procedure frees up everything associated with a literal table
 - *	except for the table's structure itself.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	Each literal in the table is released: i.e., its reference count
 - *	in the global literal table is decremented and, if it becomes zero,
 - *	the literal is freed. In addition, the table's bucket array is
 - *	freed.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -void
 -TclDeleteLiteralTable(interp, tablePtr)
 -    Tcl_Interp *interp;		/* Interpreter containing shared literals
 -				 * referenced by the table to delete. */
 -    LiteralTable *tablePtr;	/* Points to the literal table to delete. */
 -{
 -    LiteralEntry *entryPtr;
 -    int i, start;
 -
 -    /*
 -     * Release remaining literals in the table. Note that releasing a
 -     * literal might release other literals, modifying the table, so we
 -     * restart the search from the bucket chain we last found an entry.
 -     */
 -
 -#ifdef TCL_COMPILE_DEBUG
 -    TclVerifyGlobalLiteralTable((Interp *) interp);
 -#endif /*TCL_COMPILE_DEBUG*/
 -
 -    start = 0;
 -    while (tablePtr->numEntries > 0) {
 -	for (i = start;  i < tablePtr->numBuckets;  i++) {
 -	    entryPtr = tablePtr->buckets[i];
 -	    if (entryPtr != NULL) {
 -		TclReleaseLiteral(interp, entryPtr->objPtr);
 -		start = i;
 -		break;
 -	    }
 -	}
 -    }
 -
 -    /*
 -     * Free up the table's bucket array if it was dynamically allocated.
 -     */
 -
 -    if (tablePtr->buckets != tablePtr->staticBuckets) {
 -	ckfree((char *) tablePtr->buckets);
 -    }
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclRegisterLiteral --
 - *
 - *	Find, or if necessary create, an object in a CompileEnv literal
 - *	array that has a string representation matching the argument string.
 - *
 - * Results:
 - *	The index in the CompileEnv's literal array that references a
 - *	shared literal matching the string. The object is created if
 - *	necessary.
 - *
 - * Side effects:
 - *	To maximize sharing, we look up the string in the interpreter's
 - *	global literal table. If not found, we create a new shared literal
 - *	in the global table. We then add a reference to the shared
 - *	literal in the CompileEnv's literal array. 
 - *
 - *	If onHeap is 1, this procedure is given ownership of the string: if
 - *	an object is created then its string representation is set directly
 - *	from string, otherwise the string is freed. Typically, a caller sets
 - *	onHeap 1 if "string" is an already heap-allocated buffer holding the
 - *	result of backslash substitutions.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -int
 -TclRegisterLiteral(envPtr, bytes, length, onHeap)
 -    CompileEnv *envPtr;		/* Points to the CompileEnv in whose object
 -				 * array an object is found or created. */
 -    register char *bytes;	/* Points to string for which to find or
 -				 * create an object in CompileEnv's object
 -				 * array. */
 -    int length;			/* Number of bytes in the string. If < 0,
 -				 * the string consists of all bytes up to
 -				 * the first null character. */
 -    int onHeap;			/* If 1 then the caller already malloc'd
 -				 * bytes and ownership is passed to this
 -				 * procedure. */
 -{
 -    Interp *iPtr = envPtr->iPtr;
 -    LiteralTable *globalTablePtr = &(iPtr->literalTable);
 -    LiteralTable *localTablePtr = &(envPtr->localLitTable);
 -    register LiteralEntry *globalPtr, *localPtr;
 -    register Tcl_Obj *objPtr;
 -    unsigned int hash;
 -    int localHash, globalHash, objIndex;
 -    long n;
 -    char buf[TCL_INTEGER_SPACE];
 -
 - 
 -    if (length < 0) {
 -	length = (bytes? strlen(bytes) : 0);
 -    }
 -    hash = HashString(bytes, length);
 -
 -    /*
 -     * Is the literal already in the CompileEnv's local literal array?
 -     * If so, just return its index.
 -     */
 -
 -    localHash = (hash & localTablePtr->mask);
 -    for (localPtr = localTablePtr->buckets[localHash];
 -	  localPtr != NULL;  localPtr = localPtr->nextPtr) {
 -	objPtr = localPtr->objPtr;
 -	if ((objPtr->length == length) && ((length == 0)
 -		|| ((objPtr->bytes[0] == bytes[0])
 -			&& (memcmp(objPtr->bytes, bytes, (unsigned) length)
 -				== 0)))) {
 -	    if (onHeap) {
 -		ckfree(bytes);
 -	    }
 -	    objIndex = (localPtr - envPtr->literalArrayPtr);
 -#ifdef TCL_COMPILE_DEBUG
 -	    TclVerifyLocalLiteralTable(envPtr);
 -#endif /*TCL_COMPILE_DEBUG*/
 -
 -	    return objIndex;
 -	}
 -    }
 -
 -    /*
 -     * The literal is new to this CompileEnv. Is it in the interpreter's
 -     * global literal table?
 -     */
 -
 -    globalHash = (hash & globalTablePtr->mask);
 -    for (globalPtr = globalTablePtr->buckets[globalHash];
 -	 globalPtr != NULL;  globalPtr = globalPtr->nextPtr) {
 -	objPtr = globalPtr->objPtr;
 -	if ((objPtr->length == length) && ((length == 0)
 -		|| ((objPtr->bytes[0] == bytes[0])
 -			&& (memcmp(objPtr->bytes, bytes, (unsigned) length)
 -				== 0)))) {
 -	    /*
 -	     * A global literal was found. Add an entry to the CompileEnv's
 -	     * local literal array.
 -	     */
 -	    
 -	    if (onHeap) {
 -		ckfree(bytes);
 -	    }
 -	    objIndex = AddLocalLiteralEntry(envPtr, globalPtr, localHash);
 -#ifdef TCL_COMPILE_DEBUG
 -	    if (globalPtr->refCount < 1) {
 -		panic("TclRegisterLiteral: global literal \"%.*s\" had bad refCount %d",
 -			(length>60? 60 : length), bytes,
 -			globalPtr->refCount);
 -	    }
 -	    TclVerifyLocalLiteralTable(envPtr);
 -#endif /*TCL_COMPILE_DEBUG*/ 
 -	    return objIndex;
 -	}
 -    }
 -
 -    /*
 -     * The literal is new to the interpreter. Add it to the global literal
 -     * table then add an entry to the CompileEnv's local literal array.
 -     * Convert the object to an integer object if possible.
 -     */
 -
 -    TclNewObj(objPtr);
 -    Tcl_IncrRefCount(objPtr);
 -    if (onHeap) {
 -	objPtr->bytes = bytes;
 -	objPtr->length = length;
 -    } else {
 -	TclInitStringRep(objPtr, bytes, length);
 -    }
 -
 -    if (TclLooksLikeInt(bytes, length)) {
 -	if (TclGetLong((Tcl_Interp *) NULL, bytes, &n) == TCL_OK) {
 -	    TclFormatInt(buf, n);
 -	    if (strcmp(bytes, buf) == 0) {
 -		objPtr->internalRep.longValue = n;
 -		objPtr->typePtr = &tclIntType;
 -	    }
 -	}
 -    }
 -    
 -#ifdef TCL_COMPILE_DEBUG
 -    if (TclLookupLiteralEntry((Tcl_Interp *) iPtr, objPtr) != NULL) {
 -	panic("TclRegisterLiteral: literal \"%.*s\" found globally but shouldn't be",
 -	        (length>60? 60 : length), bytes);
 -    }
 -#endif
 -
 -    globalPtr = (LiteralEntry *) ckalloc((unsigned) sizeof(LiteralEntry));
 -    globalPtr->objPtr = objPtr;
 -    globalPtr->refCount = 0;
 -    globalPtr->nextPtr = globalTablePtr->buckets[globalHash];
 -    globalTablePtr->buckets[globalHash] = globalPtr;
 -    globalTablePtr->numEntries++;
 -
 -    /*
 -     * If the global literal table has exceeded a decent size, rebuild it
 -     * with more buckets.
 -     */
 -
 -    if (globalTablePtr->numEntries >= globalTablePtr->rebuildSize) {
 -	RebuildLiteralTable(globalTablePtr);
 -    }
 -    
 -    objIndex = AddLocalLiteralEntry(envPtr, globalPtr, localHash);
 -
 -#ifdef TCL_COMPILE_DEBUG
 -    TclVerifyGlobalLiteralTable(iPtr);
 -    TclVerifyLocalLiteralTable(envPtr);
 -    {
 -	LiteralEntry *entryPtr;
 -	int found, i;
 -	found = 0;
 -	for (i = 0;  i < globalTablePtr->numBuckets;  i++) {
 -	    for (entryPtr = globalTablePtr->buckets[i];
 -		    entryPtr != NULL;  entryPtr = entryPtr->nextPtr) {
 -		if ((entryPtr == globalPtr)
 -		        && (entryPtr->objPtr == objPtr)) {
 -		    found = 1;
 -		}
 -	    }
 -	}
 -	if (!found) {
 -	    panic("TclRegisterLiteral: literal \"%.*s\" wasn't global",
 -	            (length>60? 60 : length), bytes);
 -	}
 -    }
 -#endif /*TCL_COMPILE_DEBUG*/
 -#ifdef TCL_COMPILE_STATS   
 -    iPtr->stats.numLiteralsCreated++;
 -    iPtr->stats.totalLitStringBytes   += (double) (length + 1);
 -    iPtr->stats.currentLitStringBytes += (double) (length + 1);
 -    iPtr->stats.literalCount[TclLog2(length)]++;
 -#endif /*TCL_COMPILE_STATS*/
 -    return objIndex;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclLookupLiteralEntry --
 - *
 - *	Finds the LiteralEntry that corresponds to a literal Tcl object
 - *      holding a literal.
 - *
 - * Results:
 - *      Returns the matching LiteralEntry if found, otherwise NULL.
 - *
 - * Side effects:
 - *      None.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -LiteralEntry *
 -TclLookupLiteralEntry(interp, objPtr)
 -    Tcl_Interp *interp;		/* Interpreter for which objPtr was created
 -                                 * to hold a literal. */
 -    register Tcl_Obj *objPtr;	/* Points to a Tcl object holding a
 -                                 * literal that was previously created by a
 -                                 * call to TclRegisterLiteral. */
 -{
 -    Interp *iPtr = (Interp *) interp;
 -    LiteralTable *globalTablePtr = &(iPtr->literalTable);
 -    register LiteralEntry *entryPtr;
 -    char *bytes;
 -    int length, globalHash;
 -
 -    bytes = Tcl_GetStringFromObj(objPtr, &length);
 -    globalHash = (HashString(bytes, length) & globalTablePtr->mask);
 -    for (entryPtr = globalTablePtr->buckets[globalHash];
 -            entryPtr != NULL;  entryPtr = entryPtr->nextPtr) {
 -        if (entryPtr->objPtr == objPtr) {
 -            return entryPtr;
 -        }
 -    }
 -    return NULL;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclHideLiteral --
 - *
 - *	Remove a literal entry from the literal hash tables, leaving it in
 - *	the literal array so existing references continue to function.
 - *	This makes it possible to turn a shared literal into a private
 - *	literal that cannot be shared.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	Removes the literal from the local hash table and decrements the
 - *	global hash entry's reference count.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -void
 -TclHideLiteral(interp, envPtr, index)
 -    Tcl_Interp *interp;		 /* Interpreter for which objPtr was created
 -                                  * to hold a literal. */
 -    register CompileEnv *envPtr; /* Points to CompileEnv whose literal array
 -				  * contains the entry being hidden. */
 -    int index;			 /* The index of the entry in the literal
 -				  * array. */
 -{
 -    LiteralEntry **nextPtrPtr, *entryPtr, *lPtr;
 -    LiteralTable *localTablePtr = &(envPtr->localLitTable);
 -    int localHash, length;
 -    char *bytes;
 -    Tcl_Obj *newObjPtr;
 -
 -    lPtr = &(envPtr->literalArrayPtr[index]);
 -
 -    /*
 -     * To avoid unwanted sharing we need to copy the object and remove it from
 -     * the local and global literal tables.  It still has a slot in the literal
 -     * array so it can be referred to by byte codes, but it will not be matched
 -     * by literal searches.
 -     */
 -
 -    newObjPtr = Tcl_DuplicateObj(lPtr->objPtr);
 -    Tcl_IncrRefCount(newObjPtr);
 -    TclReleaseLiteral(interp, lPtr->objPtr);
 -    lPtr->objPtr = newObjPtr;
 -
 -    bytes = Tcl_GetStringFromObj(newObjPtr, &length);
 -    localHash = (HashString(bytes, length) & localTablePtr->mask);
 -    nextPtrPtr = &localTablePtr->buckets[localHash];
 -
 -    for (entryPtr = *nextPtrPtr; entryPtr != NULL; entryPtr = *nextPtrPtr) {
 -	if (entryPtr == lPtr) {
 -	    *nextPtrPtr = lPtr->nextPtr;
 -	    lPtr->nextPtr = NULL;
 -	    localTablePtr->numEntries--;
 -	    break;
 -	}
 -	nextPtrPtr = &entryPtr->nextPtr;
 -    }
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclAddLiteralObj --
 - *
 - *	Add a single literal object to the literal array.  This
 - *	function does not add the literal to the local or global
 - *	literal tables.  The caller is expected to add the entry
 - *	to whatever tables are appropriate.
 - *
 - * Results:
 - *	The index in the CompileEnv's literal array that references the
 - *	literal.  Stores the pointer to the new literal entry in the
 - *	location referenced by the localPtrPtr argument.
 - *
 - * Side effects:
 - *	Expands the literal array if necessary.  Increments the refcount
 - *	on the literal object.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -int
 -TclAddLiteralObj(envPtr, objPtr, litPtrPtr)
 -    register CompileEnv *envPtr; /* Points to CompileEnv in whose literal
 -				  * array the object is to be inserted. */
 -    Tcl_Obj *objPtr;		 /* The object to insert into the array. */
 -    LiteralEntry **litPtrPtr;	 /* The location where the pointer to the
 -				  * new literal entry should be stored.
 -				  * May be NULL. */
 -{
 -    register LiteralEntry *lPtr;
 -    int objIndex;
 -
 -    if (envPtr->literalArrayNext >= envPtr->literalArrayEnd) {
 -	ExpandLocalLiteralArray(envPtr);
 -    }
 -    objIndex = envPtr->literalArrayNext;
 -    envPtr->literalArrayNext++;
 -
 -    lPtr = &(envPtr->literalArrayPtr[objIndex]);
 -    lPtr->objPtr = objPtr;
 -    Tcl_IncrRefCount(objPtr);
 -    lPtr->refCount = -1;	/* i.e., unused */
 -    lPtr->nextPtr = NULL;
 -
 -    if (litPtrPtr) {
 -	*litPtrPtr = lPtr;
 -    }
 -
 -    return objIndex;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * AddLocalLiteralEntry --
 - *
 - *	Insert a new literal into a CompileEnv's local literal array.
 - *
 - * Results:
 - *	The index in the CompileEnv's literal array that references the
 - *	literal.
 - *
 - * Side effects:
 - *	Increments the ref count of the global LiteralEntry since the
 - *	CompileEnv now refers to the literal. Expands the literal array
 - *	if necessary. May rebuild the hash bucket array of the CompileEnv's
 - *	literal array if it becomes too large.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -static int
 -AddLocalLiteralEntry(envPtr, globalPtr, localHash)
 -    register CompileEnv *envPtr; /* Points to CompileEnv in whose literal
 -				  * array the object is to be inserted. */
 -    LiteralEntry *globalPtr;	 /* Points to the global LiteralEntry for
 -				  * the literal to add to the CompileEnv. */
 -    int localHash;		 /* Hash value for the literal's string. */
 -{
 -    register LiteralTable *localTablePtr = &(envPtr->localLitTable);
 -    LiteralEntry *localPtr;
 -    int objIndex;
 -    
 -    objIndex = TclAddLiteralObj(envPtr, globalPtr->objPtr, &localPtr);
 -
 -    /*
 -     * Add the literal to the local table.
 -     */
 -
 -    localPtr->nextPtr = localTablePtr->buckets[localHash];
 -    localTablePtr->buckets[localHash] = localPtr;
 -    localTablePtr->numEntries++;
 -
 -    globalPtr->refCount++;
 -
 -    /*
 -     * If the CompileEnv's local literal table has exceeded a decent size,
 -     * rebuild it with more buckets.
 -     */
 -
 -    if (localTablePtr->numEntries >= localTablePtr->rebuildSize) {
 -	RebuildLiteralTable(localTablePtr);
 -    }
 -
 -#ifdef TCL_COMPILE_DEBUG
 -    TclVerifyLocalLiteralTable(envPtr);
 -    {
 -	char *bytes;
 -	int length, found, i;
 -	found = 0;
 -	for (i = 0;  i < localTablePtr->numBuckets;  i++) {
 -	    for (localPtr = localTablePtr->buckets[i];
 -		    localPtr != NULL;  localPtr = localPtr->nextPtr) {
 -		if (localPtr->objPtr == globalPtr->objPtr) {
 -		    found = 1;
 -		}
 -	    }
 -	}
 -	if (!found) {
 -	    bytes = Tcl_GetStringFromObj(globalPtr->objPtr, &length);
 -	    panic("AddLocalLiteralEntry: literal \"%.*s\" wasn't found locally",
 -	            (length>60? 60 : length), bytes);
 -	}
 -    }
 -#endif /*TCL_COMPILE_DEBUG*/
 -    return objIndex;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * ExpandLocalLiteralArray --
 - *
 - *	Procedure that uses malloc to allocate more storage for a
 - *	CompileEnv's local literal array.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	The literal array in *envPtr is reallocated to a new array of
 - *	double the size, and if envPtr->mallocedLiteralArray is non-zero
 - *	the old array is freed. Entries are copied from the old array
 - *	to the new one. The local literal table is updated to refer to
 - *	the new entries.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -static void
 -ExpandLocalLiteralArray(envPtr)
 -    register CompileEnv *envPtr; /* Points to the CompileEnv whose object
 -				  * array must be enlarged. */
 -{
 -    /*
 -     * The current allocated local literal entries are stored between
 -     * elements 0 and (envPtr->literalArrayNext - 1) [inclusive].
 -     */
 -
 -    LiteralTable *localTablePtr = &(envPtr->localLitTable);
 -    int currElems = envPtr->literalArrayNext;
 -    size_t currBytes = (currElems * sizeof(LiteralEntry));
 -    register LiteralEntry *currArrayPtr = envPtr->literalArrayPtr;
 -    register LiteralEntry *newArrayPtr =
 -	    (LiteralEntry *) ckalloc((unsigned) (2 * currBytes));
 -    int i;
 -    
 -    /*
 -     * Copy from the old literal array to the new, then update the local
 -     * literal table's bucket array.
 -     */
 -
 -    memcpy((VOID *) newArrayPtr, (VOID *) currArrayPtr, currBytes);
 -    for (i = 0;  i < currElems;  i++) {
 -	if (currArrayPtr[i].nextPtr == NULL) {
 -	    newArrayPtr[i].nextPtr = NULL;
 -	} else {
 -	    newArrayPtr[i].nextPtr = newArrayPtr
 -		    + (currArrayPtr[i].nextPtr - currArrayPtr);
 -	}
 -    }
 -    for (i = 0;  i < localTablePtr->numBuckets;  i++) {
 -	if (localTablePtr->buckets[i] != NULL) {
 -	    localTablePtr->buckets[i] = newArrayPtr
 -	            + (localTablePtr->buckets[i] - currArrayPtr);
 -	}
 -    }
 -
 -    /*
 -     * Free the old literal array if needed, and mark the new literal
 -     * array as malloced.
 -     */
 -    
 -    if (envPtr->mallocedLiteralArray) {
 -	ckfree((char *) currArrayPtr);
 -    }
 -    envPtr->literalArrayPtr = newArrayPtr;
 -    envPtr->literalArrayEnd = (2 * currElems);
 -    envPtr->mallocedLiteralArray = 1;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclReleaseLiteral --
 - *
 - *	This procedure releases a reference to one of the shared Tcl objects
 - *	that hold literals. It is called to release the literals referenced
 - *	by a ByteCode that is being destroyed, and it is also called by
 - *	TclDeleteLiteralTable.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	The reference count for the global LiteralTable entry that 
 - *	corresponds to the literal is decremented. If no other reference
 - *	to a global literal object remains, it is freed.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -void
 -TclReleaseLiteral(interp, objPtr)
 -    Tcl_Interp *interp;		/* Interpreter for which objPtr was created
 -				 * to hold a literal. */
 -    register Tcl_Obj *objPtr;	/* Points to a literal object that was
 -				 * previously created by a call to
 -				 * TclRegisterLiteral. */
 -{
 -    Interp *iPtr = (Interp *) interp;
 -    LiteralTable *globalTablePtr = &(iPtr->literalTable);
 -    register LiteralEntry *entryPtr, *prevPtr;
 -    ByteCode* codePtr;
 -    char *bytes;
 -    int length, index;
 -
 -    bytes = Tcl_GetStringFromObj(objPtr, &length);
 -    index = (HashString(bytes, length) & globalTablePtr->mask);
 -
 -    /*
 -     * Check to see if the object is in the global literal table and 
 -     * remove this reference.  The object may not be in the table if
 -     * it is a hidden local literal.
 -     */
 -
 -    for (prevPtr = NULL, entryPtr = globalTablePtr->buckets[index];
 -	    entryPtr != NULL;
 -	    prevPtr = entryPtr, entryPtr = entryPtr->nextPtr) {
 -	if (entryPtr->objPtr == objPtr) {
 -	    entryPtr->refCount--;
 -
 -	    /*
 -	     * We found the matching LiteralEntry. Check if it's only being
 -	     * kept alive only by a circular reference from a ByteCode
 -	     * stored as its internal rep.
 -	     */
 -	    
 -	    if ((entryPtr->refCount == 1)
 -		    && (objPtr->typePtr == &tclByteCodeType)) {
 -		codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
 -		if ((codePtr->numLitObjects == 1)
 -		        && (codePtr->objArrayPtr[0] == objPtr)) {
 -		    entryPtr->refCount = 0;
 -
 -		    /*
 -		     * Set the ByteCode object array entry NULL to signal
 -		     * to TclCleanupByteCode to not try to release this
 -		     * about to be freed literal again.
 -		     */
 -
 -		    codePtr->objArrayPtr[0] = NULL;
 -		}
 -	    }
 -
 -	    /*
 -	     * If the literal is no longer being used by any ByteCode,
 -	     * delete the entry then decrement the ref count of its object.
 -	     */
 -		
 -	    if (entryPtr->refCount == 0) {
 -		if (prevPtr == NULL) {
 -		    globalTablePtr->buckets[index] = entryPtr->nextPtr;
 -		} else {
 -		    prevPtr->nextPtr = entryPtr->nextPtr;
 -		}
 -#ifdef TCL_COMPILE_STATS
 -		iPtr->stats.currentLitStringBytes -= (double) (length + 1);
 -#endif /*TCL_COMPILE_STATS*/
 -		ckfree((char *) entryPtr);
 -		globalTablePtr->numEntries--;
 -
 -		/*
 -		 * Remove the reference corresponding to the global 
 -		 * literal table entry.
 -		 */
 -
 -		TclDecrRefCount(objPtr);
 -	    }
 -	    break;
 -	}
 -    }
 -
 -    /*
 -     * Remove the reference corresponding to the local literal table
 -     * entry.
 -     */
 -
 -    Tcl_DecrRefCount(objPtr);
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * HashString --
 - *
 - *	Compute a one-word summary of a text string, which can be
 - *	used to generate a hash index.
 - *
 - * Results:
 - *	The return value is a one-word summary of the information in
 - *	string.
 - *
 - * Side effects:
 - *	None.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -static unsigned int
 -HashString(bytes, length)
 -    register CONST char *bytes; /* String for which to compute hash
 -				 * value. */
 -    int length;			/* Number of bytes in the string. */
 -{
 -    register unsigned int result;
 -    register int i;
 -
 -    /*
 -     * I tried a zillion different hash functions and asked many other
 -     * people for advice.  Many people had their own favorite functions,
 -     * all different, but no-one had much idea why they were good ones.
 -     * I chose the one below (multiply by 9 and add new character)
 -     * because of the following reasons:
 -     *
 -     * 1. Multiplying by 10 is perfect for keys that are decimal strings,
 -     *    and multiplying by 9 is just about as good.
 -     * 2. Times-9 is (shift-left-3) plus (old).  This means that each
 -     *    character's bits hang around in the low-order bits of the
 -     *    hash value for ever, plus they spread fairly rapidly up to
 -     *    the high-order bits to fill out the hash value.  This seems
 -     *    works well both for decimal and non-decimal strings.
 -     */
 -
 -    result = 0;
 -    for (i = 0;  i < length;  i++) {
 -	result += (result<<3) + *bytes++;
 -    }
 -    return result;
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * RebuildLiteralTable --
 - *
 - *	This procedure is invoked when the ratio of entries to hash buckets
 - *	becomes too large in a local or global literal table. It allocates
 - *	a larger bucket array and moves the entries into the new buckets.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	Memory gets reallocated and entries get rehashed into new buckets.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -static void
 -RebuildLiteralTable(tablePtr)
 -    register LiteralTable *tablePtr; /* Local or global table to enlarge. */
 -{
 -    LiteralEntry **oldBuckets;
 -    register LiteralEntry **oldChainPtr, **newChainPtr;
 -    register LiteralEntry *entryPtr;
 -    LiteralEntry **bucketPtr;
 -    char *bytes;
 -    int oldSize, count, index, length;
 -
 -    oldSize = tablePtr->numBuckets;
 -    oldBuckets = tablePtr->buckets;
 -
 -    /*
 -     * Allocate and initialize the new bucket array, and set up
 -     * hashing constants for new array size.
 -     */
 -
 -    tablePtr->numBuckets *= 4;
 -    tablePtr->buckets = (LiteralEntry **) ckalloc((unsigned)
 -	    (tablePtr->numBuckets * sizeof(LiteralEntry *)));
 -    for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets;
 -	    count > 0;
 -	    count--, newChainPtr++) {
 -	*newChainPtr = NULL;
 -    }
 -    tablePtr->rebuildSize *= 4;
 -    tablePtr->mask = (tablePtr->mask << 2) + 3;
 -
 -    /*
 -     * Rehash all of the existing entries into the new bucket array.
 -     */
 -
 -    for (oldChainPtr = oldBuckets;
 -	    oldSize > 0;
 -	    oldSize--, oldChainPtr++) {
 -	for (entryPtr = *oldChainPtr;  entryPtr != NULL;
 -	        entryPtr = *oldChainPtr) {
 -	    bytes = Tcl_GetStringFromObj(entryPtr->objPtr, &length);
 -	    index = (HashString(bytes, length) & tablePtr->mask);
 -	    
 -	    *oldChainPtr = entryPtr->nextPtr;
 -	    bucketPtr = &(tablePtr->buckets[index]);
 -	    entryPtr->nextPtr = *bucketPtr;
 -	    *bucketPtr = entryPtr;
 -	}
 -    }
 -
 -    /*
 -     * Free up the old bucket array, if it was dynamically allocated.
 -     */
 -
 -    if (oldBuckets != tablePtr->staticBuckets) {
 -	ckfree((char *) oldBuckets);
 -    }
 -}
 -
 -#ifdef TCL_COMPILE_STATS
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclLiteralStats --
 - *
 - *	Return statistics describing the layout of the hash table
 - *	in its hash buckets.
 - *
 - * Results:
 - *	The return value is a malloc-ed string containing information
 - *	about tablePtr.  It is the caller's responsibility to free
 - *	this string.
 - *
 - * Side effects:
 - *	None.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -char *
 -TclLiteralStats(tablePtr)
 -    LiteralTable *tablePtr;	/* Table for which to produce stats. */
 -{
 -#define NUM_COUNTERS 10
 -    int count[NUM_COUNTERS], overflow, i, j;
 -    double average, tmp;
 -    register LiteralEntry *entryPtr;
 -    char *result, *p;
 -
 -    /*
 -     * Compute a histogram of bucket usage. For each bucket chain i,
 -     * j is the number of entries in the chain.
 -     */
 -
 -    for (i = 0;  i < NUM_COUNTERS;  i++) {
 -	count[i] = 0;
 -    }
 -    overflow = 0;
 -    average = 0.0;
 -    for (i = 0;  i < tablePtr->numBuckets;  i++) {
 -	j = 0;
 -	for (entryPtr = tablePtr->buckets[i];  entryPtr != NULL;
 -	        entryPtr = entryPtr->nextPtr) {
 -	    j++;
 -	}
 -	if (j < NUM_COUNTERS) {
 -	    count[j]++;
 -	} else {
 -	    overflow++;
 -	}
 -	tmp = j;
 -	average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
 -    }
 -
 -    /*
 -     * Print out the histogram and a few other pieces of information.
 -     */
 -
 -    result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300));
 -    sprintf(result, "%d entries in table, %d buckets\n",
 -	    tablePtr->numEntries, tablePtr->numBuckets);
 -    p = result + strlen(result);
 -    for (i = 0; i < NUM_COUNTERS; i++) {
 -	sprintf(p, "number of buckets with %d entries: %d\n",
 -		i, count[i]);
 -	p += strlen(p);
 -    }
 -    sprintf(p, "number of buckets with %d or more entries: %d\n",
 -	    NUM_COUNTERS, overflow);
 -    p += strlen(p);
 -    sprintf(p, "average search distance for entry: %.1f", average);
 -    return result;
 -}
 -#endif /*TCL_COMPILE_STATS*/
 -
 -#ifdef TCL_COMPILE_DEBUG
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclVerifyLocalLiteralTable --
 - *
 - *	Check a CompileEnv's local literal table for consistency.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	Panics if problems are found.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -void
 -TclVerifyLocalLiteralTable(envPtr)
 -    CompileEnv *envPtr;		/* Points to CompileEnv whose literal
 -				 * table is to be validated. */
 -{
 -    register LiteralTable *localTablePtr = &(envPtr->localLitTable);
 -    register LiteralEntry *localPtr;
 -    char *bytes;
 -    register int i;
 -    int length, count;
 -
 -    count = 0;
 -    for (i = 0;  i < localTablePtr->numBuckets;  i++) {
 -	for (localPtr = localTablePtr->buckets[i];
 -	        localPtr != NULL;  localPtr = localPtr->nextPtr) {
 -	    count++;
 -	    if (localPtr->refCount != -1) {
 -		bytes = Tcl_GetStringFromObj(localPtr->objPtr, &length);
 -		panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" had bad refCount %d",
 -		        (length>60? 60 : length), bytes,
 -		        localPtr->refCount);
 -	    }
 -	    if (TclLookupLiteralEntry((Tcl_Interp *) envPtr->iPtr,
 -		    localPtr->objPtr) == NULL) {
 -		bytes = Tcl_GetStringFromObj(localPtr->objPtr, &length);
 -		panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" is not global",
 -		         (length>60? 60 : length), bytes);
 -	    }
 -	    if (localPtr->objPtr->bytes == NULL) {
 -		panic("TclVerifyLocalLiteralTable: literal has NULL string rep");
 -	    }
 -	}
 -    }
 -    if (count != localTablePtr->numEntries) {
 -	panic("TclVerifyLocalLiteralTable: local literal table had %d entries, should be %d",
 -	      count, localTablePtr->numEntries);
 -    }
 -}
 -
 -/*
 - *----------------------------------------------------------------------
 - *
 - * TclVerifyGlobalLiteralTable --
 - *
 - *	Check an interpreter's global literal table literal for consistency.
 - *
 - * Results:
 - *	None.
 - *
 - * Side effects:
 - *	Panics if problems are found.
 - *
 - *----------------------------------------------------------------------
 - */
 -
 -void
 -TclVerifyGlobalLiteralTable(iPtr)
 -    Interp *iPtr;		/* Points to interpreter whose global
 -				 * literal table is to be validated. */
 -{
 -    register LiteralTable *globalTablePtr = &(iPtr->literalTable);
 -    register LiteralEntry *globalPtr;
 -    char *bytes;
 -    register int i;
 -    int length, count;
 -
 -    count = 0;
 -    for (i = 0;  i < globalTablePtr->numBuckets;  i++) {
 -	for (globalPtr = globalTablePtr->buckets[i];
 -	        globalPtr != NULL;  globalPtr = globalPtr->nextPtr) {
 -	    count++;
 -	    if (globalPtr->refCount < 1) {
 -		bytes = Tcl_GetStringFromObj(globalPtr->objPtr, &length);
 -		panic("TclVerifyGlobalLiteralTable: global literal \"%.*s\" had bad refCount %d",
 -		        (length>60? 60 : length), bytes,
 -		        globalPtr->refCount);
 -	    }
 -	    if (globalPtr->objPtr->bytes == NULL) {
 -		panic("TclVerifyGlobalLiteralTable: literal has NULL string rep");
 -	    }
 -	}
 -    }
 -    if (count != globalTablePtr->numEntries) {
 -	panic("TclVerifyGlobalLiteralTable: global literal table had %d entries, should be %d",
 -	      count, globalTablePtr->numEntries);
 -    }
 -}
 -#endif /*TCL_COMPILE_DEBUG*/
 +/*  + * tclLiteral.c -- + * + *	Implementation of the global and ByteCode-local literal tables + *	used to manage the Tcl objects created for literal values during + *	compilation of Tcl scripts. This implementation borrows heavily + *	from the more general hashtable implementation of Tcl hash tables + *	that appears in tclHash.c. + * + * Copyright (c) 1997-1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclLiteral.c,v 1.6 1999/05/06 02:34:42 hershey Exp $ + */ + +#include "tclInt.h" +#include "tclCompile.h" +#include "tclPort.h" +/* + * When there are this many entries per bucket, on average, rebuild + * a literal's hash table to make it larger. + */ + +#define REBUILD_MULTIPLIER	3 + +/* + * Procedure prototypes for static procedures in this file: + */ + +static int		AddLocalLiteralEntry _ANSI_ARGS_(( +			    CompileEnv *envPtr, LiteralEntry *globalPtr, +			    int localHash)); +static void		ExpandLocalLiteralArray _ANSI_ARGS_(( +			    CompileEnv *envPtr)); +static unsigned int	HashString _ANSI_ARGS_((CONST char *bytes, +			    int length)); +static void		RebuildLiteralTable _ANSI_ARGS_(( +			    LiteralTable *tablePtr)); + +/* + *---------------------------------------------------------------------- + * + * TclInitLiteralTable -- + * + *	This procedure is called to initialize the fields of a literal table + *	structure for either an interpreter or a compilation's CompileEnv + *	structure. + * + * Results: + *	None. + * + * Side effects:  + *	The literal table is made ready for use. + * + *---------------------------------------------------------------------- + */ + +void +TclInitLiteralTable(tablePtr) +    register LiteralTable *tablePtr; /* Pointer to table structure, which +				      * is supplied by the caller. */ +{ +#if (TCL_SMALL_HASH_TABLE != 4)  +    panic("TclInitLiteralTable: TCL_SMALL_HASH_TABLE is %d, not 4\n", +	    TCL_SMALL_HASH_TABLE); +#endif +     +    tablePtr->buckets = tablePtr->staticBuckets; +    tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0; +    tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0; +    tablePtr->numBuckets = TCL_SMALL_HASH_TABLE; +    tablePtr->numEntries = 0; +    tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER; +    tablePtr->mask = 3; +} + +/* + *---------------------------------------------------------------------- + * + * TclDeleteLiteralTable -- + * + *	This procedure frees up everything associated with a literal table + *	except for the table's structure itself. + * + * Results: + *	None. + * + * Side effects: + *	Each literal in the table is released: i.e., its reference count + *	in the global literal table is decremented and, if it becomes zero, + *	the literal is freed. In addition, the table's bucket array is + *	freed. + * + *---------------------------------------------------------------------- + */ + +void +TclDeleteLiteralTable(interp, tablePtr) +    Tcl_Interp *interp;		/* Interpreter containing shared literals +				 * referenced by the table to delete. */ +    LiteralTable *tablePtr;	/* Points to the literal table to delete. */ +{ +    LiteralEntry *entryPtr; +    int i, start; + +    /* +     * Release remaining literals in the table. Note that releasing a +     * literal might release other literals, modifying the table, so we +     * restart the search from the bucket chain we last found an entry. +     */ + +#ifdef TCL_COMPILE_DEBUG +    TclVerifyGlobalLiteralTable((Interp *) interp); +#endif /*TCL_COMPILE_DEBUG*/ + +    start = 0; +    while (tablePtr->numEntries > 0) { +	for (i = start;  i < tablePtr->numBuckets;  i++) { +	    entryPtr = tablePtr->buckets[i]; +	    if (entryPtr != NULL) { +		TclReleaseLiteral(interp, entryPtr->objPtr); +		start = i; +		break; +	    } +	} +    } + +    /* +     * Free up the table's bucket array if it was dynamically allocated. +     */ + +    if (tablePtr->buckets != tablePtr->staticBuckets) { +	ckfree((char *) tablePtr->buckets); +    } +} + +/* + *---------------------------------------------------------------------- + * + * TclRegisterLiteral -- + * + *	Find, or if necessary create, an object in a CompileEnv literal + *	array that has a string representation matching the argument string. + * + * Results: + *	The index in the CompileEnv's literal array that references a + *	shared literal matching the string. The object is created if + *	necessary. + * + * Side effects: + *	To maximize sharing, we look up the string in the interpreter's + *	global literal table. If not found, we create a new shared literal + *	in the global table. We then add a reference to the shared + *	literal in the CompileEnv's literal array.  + * + *	If onHeap is 1, this procedure is given ownership of the string: if + *	an object is created then its string representation is set directly + *	from string, otherwise the string is freed. Typically, a caller sets + *	onHeap 1 if "string" is an already heap-allocated buffer holding the + *	result of backslash substitutions. + * + *---------------------------------------------------------------------- + */ + +int +TclRegisterLiteral(envPtr, bytes, length, onHeap) +    CompileEnv *envPtr;		/* Points to the CompileEnv in whose object +				 * array an object is found or created. */ +    register char *bytes;	/* Points to string for which to find or +				 * create an object in CompileEnv's object +				 * array. */ +    int length;			/* Number of bytes in the string. If < 0, +				 * the string consists of all bytes up to +				 * the first null character. */ +    int onHeap;			/* If 1 then the caller already malloc'd +				 * bytes and ownership is passed to this +				 * procedure. */ +{ +    Interp *iPtr = envPtr->iPtr; +    LiteralTable *globalTablePtr = &(iPtr->literalTable); +    LiteralTable *localTablePtr = &(envPtr->localLitTable); +    register LiteralEntry *globalPtr, *localPtr; +    register Tcl_Obj *objPtr; +    unsigned int hash; +    int localHash, globalHash, objIndex; +    long n; +    char buf[TCL_INTEGER_SPACE]; + +  +    if (length < 0) { +	length = (bytes? strlen(bytes) : 0); +    } +    hash = HashString(bytes, length); + +    /* +     * Is the literal already in the CompileEnv's local literal array? +     * If so, just return its index. +     */ + +    localHash = (hash & localTablePtr->mask); +    for (localPtr = localTablePtr->buckets[localHash]; +	  localPtr != NULL;  localPtr = localPtr->nextPtr) { +	objPtr = localPtr->objPtr; +	if ((objPtr->length == length) && ((length == 0) +		|| ((objPtr->bytes[0] == bytes[0]) +			&& (memcmp(objPtr->bytes, bytes, (unsigned) length) +				== 0)))) { +	    if (onHeap) { +		ckfree(bytes); +	    } +	    objIndex = (localPtr - envPtr->literalArrayPtr); +#ifdef TCL_COMPILE_DEBUG +	    TclVerifyLocalLiteralTable(envPtr); +#endif /*TCL_COMPILE_DEBUG*/ + +	    return objIndex; +	} +    } + +    /* +     * The literal is new to this CompileEnv. Is it in the interpreter's +     * global literal table? +     */ + +    globalHash = (hash & globalTablePtr->mask); +    for (globalPtr = globalTablePtr->buckets[globalHash]; +	 globalPtr != NULL;  globalPtr = globalPtr->nextPtr) { +	objPtr = globalPtr->objPtr; +	if ((objPtr->length == length) && ((length == 0) +		|| ((objPtr->bytes[0] == bytes[0]) +			&& (memcmp(objPtr->bytes, bytes, (unsigned) length) +				== 0)))) { +	    /* +	     * A global literal was found. Add an entry to the CompileEnv's +	     * local literal array. +	     */ +	     +	    if (onHeap) { +		ckfree(bytes); +	    } +	    objIndex = AddLocalLiteralEntry(envPtr, globalPtr, localHash); +#ifdef TCL_COMPILE_DEBUG +	    if (globalPtr->refCount < 1) { +		panic("TclRegisterLiteral: global literal \"%.*s\" had bad refCount %d", +			(length>60? 60 : length), bytes, +			globalPtr->refCount); +	    } +	    TclVerifyLocalLiteralTable(envPtr); +#endif /*TCL_COMPILE_DEBUG*/  +	    return objIndex; +	} +    } + +    /* +     * The literal is new to the interpreter. Add it to the global literal +     * table then add an entry to the CompileEnv's local literal array. +     * Convert the object to an integer object if possible. +     */ + +    TclNewObj(objPtr); +    Tcl_IncrRefCount(objPtr); +    if (onHeap) { +	objPtr->bytes = bytes; +	objPtr->length = length; +    } else { +	TclInitStringRep(objPtr, bytes, length); +    } + +    if (TclLooksLikeInt(bytes, length)) { +	if (TclGetLong((Tcl_Interp *) NULL, bytes, &n) == TCL_OK) { +	    TclFormatInt(buf, n); +	    if (strcmp(bytes, buf) == 0) { +		objPtr->internalRep.longValue = n; +		objPtr->typePtr = &tclIntType; +	    } +	} +    } +     +#ifdef TCL_COMPILE_DEBUG +    if (TclLookupLiteralEntry((Tcl_Interp *) iPtr, objPtr) != NULL) { +	panic("TclRegisterLiteral: literal \"%.*s\" found globally but shouldn't be", +	        (length>60? 60 : length), bytes); +    } +#endif + +    globalPtr = (LiteralEntry *) ckalloc((unsigned) sizeof(LiteralEntry)); +    globalPtr->objPtr = objPtr; +    globalPtr->refCount = 0; +    globalPtr->nextPtr = globalTablePtr->buckets[globalHash]; +    globalTablePtr->buckets[globalHash] = globalPtr; +    globalTablePtr->numEntries++; + +    /* +     * If the global literal table has exceeded a decent size, rebuild it +     * with more buckets. +     */ + +    if (globalTablePtr->numEntries >= globalTablePtr->rebuildSize) { +	RebuildLiteralTable(globalTablePtr); +    } +     +    objIndex = AddLocalLiteralEntry(envPtr, globalPtr, localHash); + +#ifdef TCL_COMPILE_DEBUG +    TclVerifyGlobalLiteralTable(iPtr); +    TclVerifyLocalLiteralTable(envPtr); +    { +	LiteralEntry *entryPtr; +	int found, i; +	found = 0; +	for (i = 0;  i < globalTablePtr->numBuckets;  i++) { +	    for (entryPtr = globalTablePtr->buckets[i]; +		    entryPtr != NULL;  entryPtr = entryPtr->nextPtr) { +		if ((entryPtr == globalPtr) +		        && (entryPtr->objPtr == objPtr)) { +		    found = 1; +		} +	    } +	} +	if (!found) { +	    panic("TclRegisterLiteral: literal \"%.*s\" wasn't global", +	            (length>60? 60 : length), bytes); +	} +    } +#endif /*TCL_COMPILE_DEBUG*/ +#ifdef TCL_COMPILE_STATS    +    iPtr->stats.numLiteralsCreated++; +    iPtr->stats.totalLitStringBytes   += (double) (length + 1); +    iPtr->stats.currentLitStringBytes += (double) (length + 1); +    iPtr->stats.literalCount[TclLog2(length)]++; +#endif /*TCL_COMPILE_STATS*/ +    return objIndex; +} + +/* + *---------------------------------------------------------------------- + * + * TclLookupLiteralEntry -- + * + *	Finds the LiteralEntry that corresponds to a literal Tcl object + *      holding a literal. + * + * Results: + *      Returns the matching LiteralEntry if found, otherwise NULL. + * + * Side effects: + *      None. + * + *---------------------------------------------------------------------- + */ + +LiteralEntry * +TclLookupLiteralEntry(interp, objPtr) +    Tcl_Interp *interp;		/* Interpreter for which objPtr was created +                                 * to hold a literal. */ +    register Tcl_Obj *objPtr;	/* Points to a Tcl object holding a +                                 * literal that was previously created by a +                                 * call to TclRegisterLiteral. */ +{ +    Interp *iPtr = (Interp *) interp; +    LiteralTable *globalTablePtr = &(iPtr->literalTable); +    register LiteralEntry *entryPtr; +    char *bytes; +    int length, globalHash; + +    bytes = Tcl_GetStringFromObj(objPtr, &length); +    globalHash = (HashString(bytes, length) & globalTablePtr->mask); +    for (entryPtr = globalTablePtr->buckets[globalHash]; +            entryPtr != NULL;  entryPtr = entryPtr->nextPtr) { +        if (entryPtr->objPtr == objPtr) { +            return entryPtr; +        } +    } +    return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TclHideLiteral -- + * + *	Remove a literal entry from the literal hash tables, leaving it in + *	the literal array so existing references continue to function. + *	This makes it possible to turn a shared literal into a private + *	literal that cannot be shared. + * + * Results: + *	None. + * + * Side effects: + *	Removes the literal from the local hash table and decrements the + *	global hash entry's reference count. + * + *---------------------------------------------------------------------- + */ + +void +TclHideLiteral(interp, envPtr, index) +    Tcl_Interp *interp;		 /* Interpreter for which objPtr was created +                                  * to hold a literal. */ +    register CompileEnv *envPtr; /* Points to CompileEnv whose literal array +				  * contains the entry being hidden. */ +    int index;			 /* The index of the entry in the literal +				  * array. */ +{ +    LiteralEntry **nextPtrPtr, *entryPtr, *lPtr; +    LiteralTable *localTablePtr = &(envPtr->localLitTable); +    int localHash, length; +    char *bytes; +    Tcl_Obj *newObjPtr; + +    lPtr = &(envPtr->literalArrayPtr[index]); + +    /* +     * To avoid unwanted sharing we need to copy the object and remove it from +     * the local and global literal tables.  It still has a slot in the literal +     * array so it can be referred to by byte codes, but it will not be matched +     * by literal searches. +     */ + +    newObjPtr = Tcl_DuplicateObj(lPtr->objPtr); +    Tcl_IncrRefCount(newObjPtr); +    TclReleaseLiteral(interp, lPtr->objPtr); +    lPtr->objPtr = newObjPtr; + +    bytes = Tcl_GetStringFromObj(newObjPtr, &length); +    localHash = (HashString(bytes, length) & localTablePtr->mask); +    nextPtrPtr = &localTablePtr->buckets[localHash]; + +    for (entryPtr = *nextPtrPtr; entryPtr != NULL; entryPtr = *nextPtrPtr) { +	if (entryPtr == lPtr) { +	    *nextPtrPtr = lPtr->nextPtr; +	    lPtr->nextPtr = NULL; +	    localTablePtr->numEntries--; +	    break; +	} +	nextPtrPtr = &entryPtr->nextPtr; +    } +} + +/* + *---------------------------------------------------------------------- + * + * TclAddLiteralObj -- + * + *	Add a single literal object to the literal array.  This + *	function does not add the literal to the local or global + *	literal tables.  The caller is expected to add the entry + *	to whatever tables are appropriate. + * + * Results: + *	The index in the CompileEnv's literal array that references the + *	literal.  Stores the pointer to the new literal entry in the + *	location referenced by the localPtrPtr argument. + * + * Side effects: + *	Expands the literal array if necessary.  Increments the refcount + *	on the literal object. + * + *---------------------------------------------------------------------- + */ + +int +TclAddLiteralObj(envPtr, objPtr, litPtrPtr) +    register CompileEnv *envPtr; /* Points to CompileEnv in whose literal +				  * array the object is to be inserted. */ +    Tcl_Obj *objPtr;		 /* The object to insert into the array. */ +    LiteralEntry **litPtrPtr;	 /* The location where the pointer to the +				  * new literal entry should be stored. +				  * May be NULL. */ +{ +    register LiteralEntry *lPtr; +    int objIndex; + +    if (envPtr->literalArrayNext >= envPtr->literalArrayEnd) { +	ExpandLocalLiteralArray(envPtr); +    } +    objIndex = envPtr->literalArrayNext; +    envPtr->literalArrayNext++; + +    lPtr = &(envPtr->literalArrayPtr[objIndex]); +    lPtr->objPtr = objPtr; +    Tcl_IncrRefCount(objPtr); +    lPtr->refCount = -1;	/* i.e., unused */ +    lPtr->nextPtr = NULL; + +    if (litPtrPtr) { +	*litPtrPtr = lPtr; +    } + +    return objIndex; +} + +/* + *---------------------------------------------------------------------- + * + * AddLocalLiteralEntry -- + * + *	Insert a new literal into a CompileEnv's local literal array. + * + * Results: + *	The index in the CompileEnv's literal array that references the + *	literal. + * + * Side effects: + *	Increments the ref count of the global LiteralEntry since the + *	CompileEnv now refers to the literal. Expands the literal array + *	if necessary. May rebuild the hash bucket array of the CompileEnv's + *	literal array if it becomes too large. + * + *---------------------------------------------------------------------- + */ + +static int +AddLocalLiteralEntry(envPtr, globalPtr, localHash) +    register CompileEnv *envPtr; /* Points to CompileEnv in whose literal +				  * array the object is to be inserted. */ +    LiteralEntry *globalPtr;	 /* Points to the global LiteralEntry for +				  * the literal to add to the CompileEnv. */ +    int localHash;		 /* Hash value for the literal's string. */ +{ +    register LiteralTable *localTablePtr = &(envPtr->localLitTable); +    LiteralEntry *localPtr; +    int objIndex; +     +    objIndex = TclAddLiteralObj(envPtr, globalPtr->objPtr, &localPtr); + +    /* +     * Add the literal to the local table. +     */ + +    localPtr->nextPtr = localTablePtr->buckets[localHash]; +    localTablePtr->buckets[localHash] = localPtr; +    localTablePtr->numEntries++; + +    globalPtr->refCount++; + +    /* +     * If the CompileEnv's local literal table has exceeded a decent size, +     * rebuild it with more buckets. +     */ + +    if (localTablePtr->numEntries >= localTablePtr->rebuildSize) { +	RebuildLiteralTable(localTablePtr); +    } + +#ifdef TCL_COMPILE_DEBUG +    TclVerifyLocalLiteralTable(envPtr); +    { +	char *bytes; +	int length, found, i; +	found = 0; +	for (i = 0;  i < localTablePtr->numBuckets;  i++) { +	    for (localPtr = localTablePtr->buckets[i]; +		    localPtr != NULL;  localPtr = localPtr->nextPtr) { +		if (localPtr->objPtr == globalPtr->objPtr) { +		    found = 1; +		} +	    } +	} +	if (!found) { +	    bytes = Tcl_GetStringFromObj(globalPtr->objPtr, &length); +	    panic("AddLocalLiteralEntry: literal \"%.*s\" wasn't found locally", +	            (length>60? 60 : length), bytes); +	} +    } +#endif /*TCL_COMPILE_DEBUG*/ +    return objIndex; +} + +/* + *---------------------------------------------------------------------- + * + * ExpandLocalLiteralArray -- + * + *	Procedure that uses malloc to allocate more storage for a + *	CompileEnv's local literal array. + * + * Results: + *	None. + * + * Side effects: + *	The literal array in *envPtr is reallocated to a new array of + *	double the size, and if envPtr->mallocedLiteralArray is non-zero + *	the old array is freed. Entries are copied from the old array + *	to the new one. The local literal table is updated to refer to + *	the new entries. + * + *---------------------------------------------------------------------- + */ + +static void +ExpandLocalLiteralArray(envPtr) +    register CompileEnv *envPtr; /* Points to the CompileEnv whose object +				  * array must be enlarged. */ +{ +    /* +     * The current allocated local literal entries are stored between +     * elements 0 and (envPtr->literalArrayNext - 1) [inclusive]. +     */ + +    LiteralTable *localTablePtr = &(envPtr->localLitTable); +    int currElems = envPtr->literalArrayNext; +    size_t currBytes = (currElems * sizeof(LiteralEntry)); +    register LiteralEntry *currArrayPtr = envPtr->literalArrayPtr; +    register LiteralEntry *newArrayPtr = +	    (LiteralEntry *) ckalloc((unsigned) (2 * currBytes)); +    int i; +     +    /* +     * Copy from the old literal array to the new, then update the local +     * literal table's bucket array. +     */ + +    memcpy((VOID *) newArrayPtr, (VOID *) currArrayPtr, currBytes); +    for (i = 0;  i < currElems;  i++) { +	if (currArrayPtr[i].nextPtr == NULL) { +	    newArrayPtr[i].nextPtr = NULL; +	} else { +	    newArrayPtr[i].nextPtr = newArrayPtr +		    + (currArrayPtr[i].nextPtr - currArrayPtr); +	} +    } +    for (i = 0;  i < localTablePtr->numBuckets;  i++) { +	if (localTablePtr->buckets[i] != NULL) { +	    localTablePtr->buckets[i] = newArrayPtr +	            + (localTablePtr->buckets[i] - currArrayPtr); +	} +    } + +    /* +     * Free the old literal array if needed, and mark the new literal +     * array as malloced. +     */ +     +    if (envPtr->mallocedLiteralArray) { +	ckfree((char *) currArrayPtr); +    } +    envPtr->literalArrayPtr = newArrayPtr; +    envPtr->literalArrayEnd = (2 * currElems); +    envPtr->mallocedLiteralArray = 1; +} + +/* + *---------------------------------------------------------------------- + * + * TclReleaseLiteral -- + * + *	This procedure releases a reference to one of the shared Tcl objects + *	that hold literals. It is called to release the literals referenced + *	by a ByteCode that is being destroyed, and it is also called by + *	TclDeleteLiteralTable. + * + * Results: + *	None. + * + * Side effects: + *	The reference count for the global LiteralTable entry that  + *	corresponds to the literal is decremented. If no other reference + *	to a global literal object remains, it is freed. + * + *---------------------------------------------------------------------- + */ + +void +TclReleaseLiteral(interp, objPtr) +    Tcl_Interp *interp;		/* Interpreter for which objPtr was created +				 * to hold a literal. */ +    register Tcl_Obj *objPtr;	/* Points to a literal object that was +				 * previously created by a call to +				 * TclRegisterLiteral. */ +{ +    Interp *iPtr = (Interp *) interp; +    LiteralTable *globalTablePtr = &(iPtr->literalTable); +    register LiteralEntry *entryPtr, *prevPtr; +    ByteCode* codePtr; +    char *bytes; +    int length, index; + +    bytes = Tcl_GetStringFromObj(objPtr, &length); +    index = (HashString(bytes, length) & globalTablePtr->mask); + +    /* +     * Check to see if the object is in the global literal table and  +     * remove this reference.  The object may not be in the table if +     * it is a hidden local literal. +     */ + +    for (prevPtr = NULL, entryPtr = globalTablePtr->buckets[index]; +	    entryPtr != NULL; +	    prevPtr = entryPtr, entryPtr = entryPtr->nextPtr) { +	if (entryPtr->objPtr == objPtr) { +	    entryPtr->refCount--; + +	    /* +	     * We found the matching LiteralEntry. Check if it's only being +	     * kept alive only by a circular reference from a ByteCode +	     * stored as its internal rep. +	     */ +	     +	    if ((entryPtr->refCount == 1) +		    && (objPtr->typePtr == &tclByteCodeType)) { +		codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; +		if ((codePtr->numLitObjects == 1) +		        && (codePtr->objArrayPtr[0] == objPtr)) { +		    entryPtr->refCount = 0; + +		    /* +		     * Set the ByteCode object array entry NULL to signal +		     * to TclCleanupByteCode to not try to release this +		     * about to be freed literal again. +		     */ + +		    codePtr->objArrayPtr[0] = NULL; +		} +	    } + +	    /* +	     * If the literal is no longer being used by any ByteCode, +	     * delete the entry then decrement the ref count of its object. +	     */ +		 +	    if (entryPtr->refCount == 0) { +		if (prevPtr == NULL) { +		    globalTablePtr->buckets[index] = entryPtr->nextPtr; +		} else { +		    prevPtr->nextPtr = entryPtr->nextPtr; +		} +#ifdef TCL_COMPILE_STATS +		iPtr->stats.currentLitStringBytes -= (double) (length + 1); +#endif /*TCL_COMPILE_STATS*/ +		ckfree((char *) entryPtr); +		globalTablePtr->numEntries--; + +		/* +		 * Remove the reference corresponding to the global  +		 * literal table entry. +		 */ + +		TclDecrRefCount(objPtr); +	    } +	    break; +	} +    } + +    /* +     * Remove the reference corresponding to the local literal table +     * entry. +     */ + +    Tcl_DecrRefCount(objPtr); +} + +/* + *---------------------------------------------------------------------- + * + * HashString -- + * + *	Compute a one-word summary of a text string, which can be + *	used to generate a hash index. + * + * Results: + *	The return value is a one-word summary of the information in + *	string. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +static unsigned int +HashString(bytes, length) +    register CONST char *bytes; /* String for which to compute hash +				 * value. */ +    int length;			/* Number of bytes in the string. */ +{ +    register unsigned int result; +    register int i; + +    /* +     * I tried a zillion different hash functions and asked many other +     * people for advice.  Many people had their own favorite functions, +     * all different, but no-one had much idea why they were good ones. +     * I chose the one below (multiply by 9 and add new character) +     * because of the following reasons: +     * +     * 1. Multiplying by 10 is perfect for keys that are decimal strings, +     *    and multiplying by 9 is just about as good. +     * 2. Times-9 is (shift-left-3) plus (old).  This means that each +     *    character's bits hang around in the low-order bits of the +     *    hash value for ever, plus they spread fairly rapidly up to +     *    the high-order bits to fill out the hash value.  This seems +     *    works well both for decimal and non-decimal strings. +     */ + +    result = 0; +    for (i = 0;  i < length;  i++) { +	result += (result<<3) + *bytes++; +    } +    return result; +} + +/* + *---------------------------------------------------------------------- + * + * RebuildLiteralTable -- + * + *	This procedure is invoked when the ratio of entries to hash buckets + *	becomes too large in a local or global literal table. It allocates + *	a larger bucket array and moves the entries into the new buckets. + * + * Results: + *	None. + * + * Side effects: + *	Memory gets reallocated and entries get rehashed into new buckets. + * + *---------------------------------------------------------------------- + */ + +static void +RebuildLiteralTable(tablePtr) +    register LiteralTable *tablePtr; /* Local or global table to enlarge. */ +{ +    LiteralEntry **oldBuckets; +    register LiteralEntry **oldChainPtr, **newChainPtr; +    register LiteralEntry *entryPtr; +    LiteralEntry **bucketPtr; +    char *bytes; +    int oldSize, count, index, length; + +    oldSize = tablePtr->numBuckets; +    oldBuckets = tablePtr->buckets; + +    /* +     * Allocate and initialize the new bucket array, and set up +     * hashing constants for new array size. +     */ + +    tablePtr->numBuckets *= 4; +    tablePtr->buckets = (LiteralEntry **) ckalloc((unsigned) +	    (tablePtr->numBuckets * sizeof(LiteralEntry *))); +    for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets; +	    count > 0; +	    count--, newChainPtr++) { +	*newChainPtr = NULL; +    } +    tablePtr->rebuildSize *= 4; +    tablePtr->mask = (tablePtr->mask << 2) + 3; + +    /* +     * Rehash all of the existing entries into the new bucket array. +     */ + +    for (oldChainPtr = oldBuckets; +	    oldSize > 0; +	    oldSize--, oldChainPtr++) { +	for (entryPtr = *oldChainPtr;  entryPtr != NULL; +	        entryPtr = *oldChainPtr) { +	    bytes = Tcl_GetStringFromObj(entryPtr->objPtr, &length); +	    index = (HashString(bytes, length) & tablePtr->mask); +	     +	    *oldChainPtr = entryPtr->nextPtr; +	    bucketPtr = &(tablePtr->buckets[index]); +	    entryPtr->nextPtr = *bucketPtr; +	    *bucketPtr = entryPtr; +	} +    } + +    /* +     * Free up the old bucket array, if it was dynamically allocated. +     */ + +    if (oldBuckets != tablePtr->staticBuckets) { +	ckfree((char *) oldBuckets); +    } +} + +#ifdef TCL_COMPILE_STATS +/* + *---------------------------------------------------------------------- + * + * TclLiteralStats -- + * + *	Return statistics describing the layout of the hash table + *	in its hash buckets. + * + * Results: + *	The return value is a malloc-ed string containing information + *	about tablePtr.  It is the caller's responsibility to free + *	this string. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +char * +TclLiteralStats(tablePtr) +    LiteralTable *tablePtr;	/* Table for which to produce stats. */ +{ +#define NUM_COUNTERS 10 +    int count[NUM_COUNTERS], overflow, i, j; +    double average, tmp; +    register LiteralEntry *entryPtr; +    char *result, *p; + +    /* +     * Compute a histogram of bucket usage. For each bucket chain i, +     * j is the number of entries in the chain. +     */ + +    for (i = 0;  i < NUM_COUNTERS;  i++) { +	count[i] = 0; +    } +    overflow = 0; +    average = 0.0; +    for (i = 0;  i < tablePtr->numBuckets;  i++) { +	j = 0; +	for (entryPtr = tablePtr->buckets[i];  entryPtr != NULL; +	        entryPtr = entryPtr->nextPtr) { +	    j++; +	} +	if (j < NUM_COUNTERS) { +	    count[j]++; +	} else { +	    overflow++; +	} +	tmp = j; +	average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0; +    } + +    /* +     * Print out the histogram and a few other pieces of information. +     */ + +    result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300)); +    sprintf(result, "%d entries in table, %d buckets\n", +	    tablePtr->numEntries, tablePtr->numBuckets); +    p = result + strlen(result); +    for (i = 0; i < NUM_COUNTERS; i++) { +	sprintf(p, "number of buckets with %d entries: %d\n", +		i, count[i]); +	p += strlen(p); +    } +    sprintf(p, "number of buckets with %d or more entries: %d\n", +	    NUM_COUNTERS, overflow); +    p += strlen(p); +    sprintf(p, "average search distance for entry: %.1f", average); +    return result; +} +#endif /*TCL_COMPILE_STATS*/ + +#ifdef TCL_COMPILE_DEBUG +/* + *---------------------------------------------------------------------- + * + * TclVerifyLocalLiteralTable -- + * + *	Check a CompileEnv's local literal table for consistency. + * + * Results: + *	None. + * + * Side effects: + *	Panics if problems are found. + * + *---------------------------------------------------------------------- + */ + +void +TclVerifyLocalLiteralTable(envPtr) +    CompileEnv *envPtr;		/* Points to CompileEnv whose literal +				 * table is to be validated. */ +{ +    register LiteralTable *localTablePtr = &(envPtr->localLitTable); +    register LiteralEntry *localPtr; +    char *bytes; +    register int i; +    int length, count; + +    count = 0; +    for (i = 0;  i < localTablePtr->numBuckets;  i++) { +	for (localPtr = localTablePtr->buckets[i]; +	        localPtr != NULL;  localPtr = localPtr->nextPtr) { +	    count++; +	    if (localPtr->refCount != -1) { +		bytes = Tcl_GetStringFromObj(localPtr->objPtr, &length); +		panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" had bad refCount %d", +		        (length>60? 60 : length), bytes, +		        localPtr->refCount); +	    } +	    if (TclLookupLiteralEntry((Tcl_Interp *) envPtr->iPtr, +		    localPtr->objPtr) == NULL) { +		bytes = Tcl_GetStringFromObj(localPtr->objPtr, &length); +		panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" is not global", +		         (length>60? 60 : length), bytes); +	    } +	    if (localPtr->objPtr->bytes == NULL) { +		panic("TclVerifyLocalLiteralTable: literal has NULL string rep"); +	    } +	} +    } +    if (count != localTablePtr->numEntries) { +	panic("TclVerifyLocalLiteralTable: local literal table had %d entries, should be %d", +	      count, localTablePtr->numEntries); +    } +} + +/* + *---------------------------------------------------------------------- + * + * TclVerifyGlobalLiteralTable -- + * + *	Check an interpreter's global literal table literal for consistency. + * + * Results: + *	None. + * + * Side effects: + *	Panics if problems are found. + * + *---------------------------------------------------------------------- + */ + +void +TclVerifyGlobalLiteralTable(iPtr) +    Interp *iPtr;		/* Points to interpreter whose global +				 * literal table is to be validated. */ +{ +    register LiteralTable *globalTablePtr = &(iPtr->literalTable); +    register LiteralEntry *globalPtr; +    char *bytes; +    register int i; +    int length, count; + +    count = 0; +    for (i = 0;  i < globalTablePtr->numBuckets;  i++) { +	for (globalPtr = globalTablePtr->buckets[i]; +	        globalPtr != NULL;  globalPtr = globalPtr->nextPtr) { +	    count++; +	    if (globalPtr->refCount < 1) { +		bytes = Tcl_GetStringFromObj(globalPtr->objPtr, &length); +		panic("TclVerifyGlobalLiteralTable: global literal \"%.*s\" had bad refCount %d", +		        (length>60? 60 : length), bytes, +		        globalPtr->refCount); +	    } +	    if (globalPtr->objPtr->bytes == NULL) { +		panic("TclVerifyGlobalLiteralTable: literal has NULL string rep"); +	    } +	} +    } +    if (count != globalTablePtr->numEntries) { +	panic("TclVerifyGlobalLiteralTable: global literal table had %d entries, should be %d", +	      count, globalTablePtr->numEntries); +    } +} +#endif /*TCL_COMPILE_DEBUG*/  | 
