Logo Search packages:      
Sourcecode: tcl8.5 version File versions  Download package

tclLiteral.c

/*
 * 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.
 * Copyright (c) 2004 by Kevin B. Kenny.  All rights reserved.
 *
 * 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.33 2007/12/13 15:23:19 dgp Exp $
 */

#include "tclInt.h"
#include "tclCompile.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

/*
 * Function prototypes for static functions in this file:
 */

static int        AddLocalLiteralEntry(CompileEnv *envPtr,
                      Tcl_Obj *objPtr, int localHash);
static void       ExpandLocalLiteralArray(CompileEnv *envPtr);
static unsigned int     HashString(const char *bytes, int length);
static void       RebuildLiteralTable(LiteralTable *tablePtr);

/*
 *----------------------------------------------------------------------
 *
 * TclInitLiteralTable --
 *
 *    This function 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(
    register LiteralTable *tablePtr)
                        /* Pointer to table structure, which is
                         * supplied by the caller. */
{
#if (TCL_SMALL_HASH_TABLE != 4)
    Tcl_Panic("TclInitLiteralTable: TCL_SMALL_HASH_TABLE is %d, not 4",
          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;
}

/*
 *----------------------------------------------------------------------
 *
 * TclCleanupLiteralTable --
 *
 *    This function frees the internal representation of every literal in a
 *    literal table. It is called prior to deleting an interp, so that
 *    variable refs will be cleaned up properly.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Each literal in the table has its internal representation freed.
 *
 *----------------------------------------------------------------------
 */

void
TclCleanupLiteralTable(
    Tcl_Interp *interp,       /* Interpreter containing literals to purge */
    LiteralTable *tablePtr)   /* Points to the literal table being
                         * cleaned. */
{
    int i;
    LiteralEntry* entryPtr;   /* Pointer to the current entry in the hash
                         * table of literals. */
    LiteralEntry* nextPtr;    /* Pointer to the next entry in the bucket. */
    Tcl_Obj* objPtr;          /* Pointer to a literal object whose internal
                         * rep is being freed. */
    const Tcl_ObjType* typePtr;     /* Pointer to the object's type. */
    int didOne;               /* Flag for whether we've removed a literal in
                         * the current bucket. */

#ifdef TCL_COMPILE_DEBUG
    TclVerifyGlobalLiteralTable((Interp *) interp);
#endif /* TCL_COMPILE_DEBUG */

    for (i=0 ; i<tablePtr->numBuckets ; i++) {
      /*
       * It is tempting simply to walk each hash bucket once and delete the
       * internal representations of each literal in turn. It's also wrong.
       * The problem is that freeing a literal's internal representation can
       * delete other literals to which it refers, making nextPtr invalid.
       * So each time we free an internal rep, we start its bucket over
       * again.
       */

      do {
          didOne = 0;
          entryPtr = tablePtr->buckets[i];
          while (entryPtr != NULL) {
            objPtr = entryPtr->objPtr;
            nextPtr = entryPtr->nextPtr;
            typePtr = objPtr->typePtr;
            if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
                if (objPtr->bytes == NULL) {
                  Tcl_Panic( "literal without a string rep" );
                }
                objPtr->typePtr = NULL;
                typePtr->freeIntRepProc(objPtr);
                didOne = 1;
            } else {
                entryPtr = nextPtr;
            }
          }
      } while (didOne);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclDeleteLiteralTable --
 *
 *    This function frees up everything associated with a literal table
 *    except for the table's structure itself. It is called when the
 *    interpreter is deleted.
 *
 * 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(
    Tcl_Interp *interp,       /* Interpreter containing shared literals
                         * referenced by the table to delete. */
    LiteralTable *tablePtr)   /* Points to the literal table to delete. */
{
    LiteralEntry *entryPtr, *nextPtr;
    Tcl_Obj *objPtr;
    int i;

    /*
     * 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*/

    /*
     * We used to call TclReleaseLiteral for each literal in the table, which
     * is rather inefficient as it causes one lookup-by-hash for each
     * reference to the literal. We now rely at interp-deletion on each
     * bytecode object to release its references to the literal Tcl_Obj
     * without requiring that it updates the global table itself, and deal
     * here only with the table.
     */

    for (i=0 ; i<tablePtr->numBuckets ; i++) {
      entryPtr = tablePtr->buckets[i];
      while (entryPtr != NULL) {
          objPtr = entryPtr->objPtr;
          TclDecrRefCount(objPtr);
          nextPtr = entryPtr->nextPtr;
          ckfree((char *) entryPtr);
          entryPtr = nextPtr;
      }
    }

    /*
     * Free up the table's bucket array if it was dynamically allocated.
     */

    if (tablePtr->buckets != tablePtr->staticBuckets) {
      ckfree((char *) tablePtr->buckets);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclCreateLiteral --
 *
 *    Find, or if necessary create, an object in the interpreter's literal
 *    table that has a string representation matching the argument
 *    string. If nsPtr!=NULL then only literals stored for the namespace are
 *    considered. 
 *
 * Results:
 *    The literal object. If it was created in this call *newPtr is set to
 *      1, else 0. NULL is returned if newPtr==NULL and no literal is found.
 *
 * Side effects:
 *      Increments the ref count of the global LiteralEntry since the caller
 *      now holds a reference. 
 *    If LITERAL_ON_HEAP is set in flags, this function 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 LITERAL_ON_HEAP if "string" is an already heap-allocated
 *    buffer holding the result of backslash substitutions.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclCreateLiteral(
    Interp *iPtr,
    char *bytes,
    int length,
    unsigned int hash,       /* The string's hash. If -1, it will be computed here */
    int *newPtr,
    Namespace *nsPtr,
    int flags,
    LiteralEntry **globalPtrPtr)
{
    LiteralTable *globalTablePtr = &(iPtr->literalTable);
    LiteralEntry *globalPtr;
    int globalHash;
    Tcl_Obj *objPtr;
    
    /*
     * Is it in the interpreter's global literal table?
     */

    if (hash == (unsigned int) -1) {
      hash = HashString(bytes, length);
    }
    globalHash = (hash & globalTablePtr->mask);
    for (globalPtr=globalTablePtr->buckets[globalHash] ; globalPtr!=NULL;
          globalPtr = globalPtr->nextPtr) {
      objPtr = globalPtr->objPtr;
      if ((globalPtr->nsPtr == nsPtr)
            && (objPtr->length == length) && ((length == 0)
            || ((objPtr->bytes[0] == bytes[0])
            && (memcmp(objPtr->bytes, bytes, (unsigned) length) == 0)))) {
          /*
           * A literal was found: return it
           */

          if (newPtr) {
            *newPtr = 0;
          }
          if (globalPtrPtr) {
            *globalPtrPtr = globalPtr;
          }
          if (flags & LITERAL_ON_HEAP) {
            ckfree(bytes);
          }
          globalPtr->refCount++;
          return objPtr;
      }
    }
    if (!newPtr) {
      if (flags & LITERAL_ON_HEAP) {
          ckfree(bytes);
      }
      return NULL;
    }

    /*
     * The literal is new to the interpreter. Add it to the global literal
     * table.
     */

    TclNewObj(objPtr);
    Tcl_IncrRefCount(objPtr);
    if (flags & LITERAL_ON_HEAP) {
      objPtr->bytes = bytes;
      objPtr->length = length;
    } else {
      TclInitStringRep(objPtr, bytes, length);
    }

#ifdef TCL_COMPILE_DEBUG
    if (TclLookupLiteralEntry((Tcl_Interp *) iPtr, objPtr) != NULL) {
      Tcl_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 = 1;
    globalPtr->nsPtr = nsPtr;
    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);
    }

#ifdef TCL_COMPILE_DEBUG
    TclVerifyGlobalLiteralTable(iPtr);
    {
      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) {
          Tcl_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*/

    if (globalPtrPtr) {
      *globalPtrPtr = globalPtr;
    }
    *newPtr = 1;
    return objPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * 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 LITERAL_ON_HEAP is set in flags, this function 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 LITERAL_ON_HEAP if "string" is an already heap-allocated
 *    buffer holding the result of backslash substitutions.
 *
 *----------------------------------------------------------------------
 */

int
TclRegisterLiteral(
    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 flags)                /* If LITERAL_ON_HEAP then the caller already
                         * malloc'd bytes and ownership is passed to
                         * this function. If LITERAL_NS_SCOPE then
                         * the literal shouldnot be shared accross
                         * namespaces. */
{
    Interp *iPtr = envPtr->iPtr;
    LiteralTable *localTablePtr = &(envPtr->localLitTable);
    LiteralEntry *globalPtr, *localPtr;
    Tcl_Obj *objPtr;
    unsigned int hash;
    int localHash, objIndex, new;
    Namespace *nsPtr;

    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 (flags & LITERAL_ON_HEAP) {
            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. Should it be shared accross
     * namespaces? If it is a fully qualified name, the namespace
     * specification is not needed to avoid sharing.
     */

    if ((flags & LITERAL_NS_SCOPE) && iPtr->varFramePtr
          && ((length <2) || (bytes[0] != ':') || (bytes[1] != ':'))) {
      nsPtr = iPtr->varFramePtr->nsPtr;
    } else {
      nsPtr = NULL;
    }

    /*
     * Is it in the interpreter's global literal table? If not, create it.
     */

    objPtr = TclCreateLiteral(iPtr, bytes, length, hash, &new, nsPtr,
          flags, &globalPtr);
    objIndex = AddLocalLiteralEntry(envPtr, objPtr, localHash);

#ifdef TCL_COMPILE_DEBUG
    if (globalPtr->refCount < 1) {
      Tcl_Panic("TclRegisterLiteral: global literal \"%.*s\" had bad refCount %d",
            (length>60? 60 : length), bytes, globalPtr->refCount);
    }
    TclVerifyLocalLiteralTable(envPtr);
#endif /*TCL_COMPILE_DEBUG*/
    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(
    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 = TclGetStringFromObj(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(
    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 = TclGetStringFromObj(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(
    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:
 *    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(
    register CompileEnv *envPtr,/* Points to CompileEnv in whose literal array
                         * the object is to be inserted. */
    Tcl_Obj *objPtr,            /* 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, objPtr, &localPtr);

    /*
     * Add the literal to the local table.
     */

    localPtr->nextPtr = localTablePtr->buckets[localHash];
    localTablePtr->buckets[localHash] = localPtr;
    localTablePtr->numEntries++;

    /*
     * 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 == objPtr) {
                found = 1;
            }
          }
      }

      if (!found) {
          bytes = Tcl_GetStringFromObj(objPtr, &length);
          Tcl_Panic("AddLocalLiteralEntry: literal \"%.*s\" wasn't found locally",
                (length>60? 60 : length), bytes);
      }
    }
#endif /*TCL_COMPILE_DEBUG*/

    return objIndex;
}

/*
 *----------------------------------------------------------------------
 *
 * ExpandLocalLiteralArray --
 *
 *    Function 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(
    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));
    LiteralEntry *currArrayPtr = envPtr->literalArrayPtr;
    LiteralEntry *newArrayPtr;
    int i;

    if (envPtr->mallocedLiteralArray) {
      newArrayPtr = (LiteralEntry *) ckrealloc(
            (char *)currArrayPtr, 2 * currBytes);
    } else {
      /*
       * envPtr->literalArrayPtr isn't a ckalloc'd pointer, so we must
       * code a ckrealloc equivalent for ourselves
       */
      newArrayPtr = (LiteralEntry *) ckalloc(2 * currBytes);
      memcpy(newArrayPtr, currArrayPtr, currBytes);
      envPtr->mallocedLiteralArray = 1;
    }

    /*
     * Update the local literal table's bucket array.
     */

    if (currArrayPtr != newArrayPtr) {
      for (i=0 ; i<currElems ; i++) {
          if (newArrayPtr[i].nextPtr != NULL) {
            newArrayPtr[i].nextPtr = newArrayPtr 
                  + (newArrayPtr[i].nextPtr - currArrayPtr);
          }
      }
      for (i=0 ; i<localTablePtr->numBuckets ; i++) {
          if (localTablePtr->buckets[i] != NULL) {
            localTablePtr->buckets[i] = newArrayPtr
                  + (localTablePtr->buckets[i] - currArrayPtr);
          }
      }
    }

    envPtr->literalArrayPtr = newArrayPtr;
    envPtr->literalArrayEnd = (2 * currElems);
}

/*
 *----------------------------------------------------------------------
 *
 * TclReleaseLiteral --
 *
 *    This function 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(
    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;
    char *bytes;
    int length, index;

    bytes = TclGetStringFromObj(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--;

          /*
           * If the literal is no longer being used by any ByteCode, delete
           * the entry then remove the reference corresponding to the global
           * literal table entry (decrement the ref count of the object).
           */

          if (entryPtr->refCount == 0) {
            if (prevPtr == NULL) {
                globalTablePtr->buckets[index] = entryPtr->nextPtr;
            } else {
                prevPtr->nextPtr = entryPtr->nextPtr;
            }
            ckfree((char *) entryPtr);
            globalTablePtr->numEntries--;

            TclDecrRefCount(objPtr);

#ifdef TCL_COMPILE_STATS
            iPtr->stats.currentLitStringBytes -= (double) (length + 1);
#endif /*TCL_COMPILE_STATS*/
          }
          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(
    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[i];
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * RebuildLiteralTable --
 *
 *    This function 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(
    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 = TclGetStringFromObj(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(
    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:
 *    Tcl_Panic if problems are found.
 *
 *----------------------------------------------------------------------
 */

void
TclVerifyLocalLiteralTable(
    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);
            Tcl_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);
            Tcl_Panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" is not global",
                  (length>60? 60 : length), bytes);
          }
          if (localPtr->objPtr->bytes == NULL) {
            Tcl_Panic("TclVerifyLocalLiteralTable: literal has NULL string rep");
          }
      }
    }
    if (count != localTablePtr->numEntries) {
      Tcl_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:
 *    Tcl_Panic if problems are found.
 *
 *----------------------------------------------------------------------
 */

void
TclVerifyGlobalLiteralTable(
    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);
            Tcl_Panic("TclVerifyGlobalLiteralTable: global literal \"%.*s\" had bad refCount %d",
                  (length>60? 60 : length), bytes, globalPtr->refCount);
          }
          if (globalPtr->objPtr->bytes == NULL) {
            Tcl_Panic("TclVerifyGlobalLiteralTable: literal has NULL string rep");
          }
      }
    }
    if (count != globalTablePtr->numEntries) {
      Tcl_Panic("TclVerifyGlobalLiteralTable: global literal table had %d entries, should be %d",
            count, globalTablePtr->numEntries);
    }
}
#endif /*TCL_COMPILE_DEBUG*/

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */

Generated by  Doxygen 1.6.0   Back to index