os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclLiteral.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/* 
sl@0
     2
 * tclLiteral.c --
sl@0
     3
 *
sl@0
     4
 *	Implementation of the global and ByteCode-local literal tables
sl@0
     5
 *	used to manage the Tcl objects created for literal values during
sl@0
     6
 *	compilation of Tcl scripts. This implementation borrows heavily
sl@0
     7
 *	from the more general hashtable implementation of Tcl hash tables
sl@0
     8
 *	that appears in tclHash.c.
sl@0
     9
 *
sl@0
    10
 * Copyright (c) 1997-1998 Sun Microsystems, Inc.
sl@0
    11
 *
sl@0
    12
 * See the file "license.terms" for information on usage and redistribution
sl@0
    13
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    14
 *
sl@0
    15
 * RCS: @(#) $Id: tclLiteral.c,v 1.11 2001/10/11 22:28:01 msofer Exp $
sl@0
    16
 */
sl@0
    17
sl@0
    18
#include "tclInt.h"
sl@0
    19
#include "tclCompile.h"
sl@0
    20
#include "tclPort.h"
sl@0
    21
/*
sl@0
    22
 * When there are this many entries per bucket, on average, rebuild
sl@0
    23
 * a literal's hash table to make it larger.
sl@0
    24
 */
sl@0
    25
sl@0
    26
#define REBUILD_MULTIPLIER	3
sl@0
    27
sl@0
    28
/*
sl@0
    29
 * Procedure prototypes for static procedures in this file:
sl@0
    30
 */
sl@0
    31
sl@0
    32
static int		AddLocalLiteralEntry _ANSI_ARGS_((
sl@0
    33
			    CompileEnv *envPtr, LiteralEntry *globalPtr,
sl@0
    34
			    int localHash));
sl@0
    35
static void		ExpandLocalLiteralArray _ANSI_ARGS_((
sl@0
    36
			    CompileEnv *envPtr));
sl@0
    37
static unsigned int	HashString _ANSI_ARGS_((CONST char *bytes,
sl@0
    38
			    int length));
sl@0
    39
static void		RebuildLiteralTable _ANSI_ARGS_((
sl@0
    40
			    LiteralTable *tablePtr));
sl@0
    41

sl@0
    42
/*
sl@0
    43
 *----------------------------------------------------------------------
sl@0
    44
 *
sl@0
    45
 * TclInitLiteralTable --
sl@0
    46
 *
sl@0
    47
 *	This procedure is called to initialize the fields of a literal table
sl@0
    48
 *	structure for either an interpreter or a compilation's CompileEnv
sl@0
    49
 *	structure.
sl@0
    50
 *
sl@0
    51
 * Results:
sl@0
    52
 *	None.
sl@0
    53
 *
sl@0
    54
 * Side effects: 
sl@0
    55
 *	The literal table is made ready for use.
sl@0
    56
 *
sl@0
    57
 *----------------------------------------------------------------------
sl@0
    58
 */
sl@0
    59
sl@0
    60
void
sl@0
    61
TclInitLiteralTable(tablePtr)
sl@0
    62
    register LiteralTable *tablePtr; /* Pointer to table structure, which
sl@0
    63
				      * is supplied by the caller. */
sl@0
    64
{
sl@0
    65
#if (TCL_SMALL_HASH_TABLE != 4) 
sl@0
    66
    panic("TclInitLiteralTable: TCL_SMALL_HASH_TABLE is %d, not 4\n",
sl@0
    67
	    TCL_SMALL_HASH_TABLE);
sl@0
    68
#endif
sl@0
    69
    
sl@0
    70
    tablePtr->buckets = tablePtr->staticBuckets;
sl@0
    71
    tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
sl@0
    72
    tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
sl@0
    73
    tablePtr->numBuckets = TCL_SMALL_HASH_TABLE;
sl@0
    74
    tablePtr->numEntries = 0;
sl@0
    75
    tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER;
sl@0
    76
    tablePtr->mask = 3;
sl@0
    77
}
sl@0
    78

sl@0
    79
/*
sl@0
    80
 *----------------------------------------------------------------------
sl@0
    81
 *
sl@0
    82
 * TclDeleteLiteralTable --
sl@0
    83
 *
sl@0
    84
 *	This procedure frees up everything associated with a literal table
sl@0
    85
 *	except for the table's structure itself.
sl@0
    86
 *
sl@0
    87
 * Results:
sl@0
    88
 *	None.
sl@0
    89
 *
sl@0
    90
 * Side effects:
sl@0
    91
 *	Each literal in the table is released: i.e., its reference count
sl@0
    92
 *	in the global literal table is decremented and, if it becomes zero,
sl@0
    93
 *	the literal is freed. In addition, the table's bucket array is
sl@0
    94
 *	freed.
sl@0
    95
 *
sl@0
    96
 *----------------------------------------------------------------------
sl@0
    97
 */
sl@0
    98
sl@0
    99
void
sl@0
   100
TclDeleteLiteralTable(interp, tablePtr)
sl@0
   101
    Tcl_Interp *interp;		/* Interpreter containing shared literals
sl@0
   102
				 * referenced by the table to delete. */
sl@0
   103
    LiteralTable *tablePtr;	/* Points to the literal table to delete. */
sl@0
   104
{
sl@0
   105
    LiteralEntry *entryPtr;
sl@0
   106
    int i, start;
sl@0
   107
sl@0
   108
    /*
sl@0
   109
     * Release remaining literals in the table. Note that releasing a
sl@0
   110
     * literal might release other literals, modifying the table, so we
sl@0
   111
     * restart the search from the bucket chain we last found an entry.
sl@0
   112
     */
sl@0
   113
sl@0
   114
#ifdef TCL_COMPILE_DEBUG
sl@0
   115
    TclVerifyGlobalLiteralTable((Interp *) interp);
sl@0
   116
#endif /*TCL_COMPILE_DEBUG*/
sl@0
   117
sl@0
   118
    start = 0;
sl@0
   119
    while (tablePtr->numEntries > 0) {
sl@0
   120
	for (i = start;  i < tablePtr->numBuckets;  i++) {
sl@0
   121
	    entryPtr = tablePtr->buckets[i];
sl@0
   122
	    if (entryPtr != NULL) {
sl@0
   123
		TclReleaseLiteral(interp, entryPtr->objPtr);
sl@0
   124
		start = i;
sl@0
   125
		break;
sl@0
   126
	    }
sl@0
   127
	}
sl@0
   128
    }
sl@0
   129
sl@0
   130
    /*
sl@0
   131
     * Free up the table's bucket array if it was dynamically allocated.
sl@0
   132
     */
sl@0
   133
sl@0
   134
    if (tablePtr->buckets != tablePtr->staticBuckets) {
sl@0
   135
	ckfree((char *) tablePtr->buckets);
sl@0
   136
    }
sl@0
   137
}
sl@0
   138

sl@0
   139
/*
sl@0
   140
 *----------------------------------------------------------------------
sl@0
   141
 *
sl@0
   142
 * TclRegisterLiteral --
sl@0
   143
 *
sl@0
   144
 *	Find, or if necessary create, an object in a CompileEnv literal
sl@0
   145
 *	array that has a string representation matching the argument string.
sl@0
   146
 *
sl@0
   147
 * Results:
sl@0
   148
 *	The index in the CompileEnv's literal array that references a
sl@0
   149
 *	shared literal matching the string. The object is created if
sl@0
   150
 *	necessary.
sl@0
   151
 *
sl@0
   152
 * Side effects:
sl@0
   153
 *	To maximize sharing, we look up the string in the interpreter's
sl@0
   154
 *	global literal table. If not found, we create a new shared literal
sl@0
   155
 *	in the global table. We then add a reference to the shared
sl@0
   156
 *	literal in the CompileEnv's literal array. 
sl@0
   157
 *
sl@0
   158
 *	If onHeap is 1, this procedure is given ownership of the string: if
sl@0
   159
 *	an object is created then its string representation is set directly
sl@0
   160
 *	from string, otherwise the string is freed. Typically, a caller sets
sl@0
   161
 *	onHeap 1 if "string" is an already heap-allocated buffer holding the
sl@0
   162
 *	result of backslash substitutions.
sl@0
   163
 *
sl@0
   164
 *----------------------------------------------------------------------
sl@0
   165
 */
sl@0
   166
sl@0
   167
int
sl@0
   168
TclRegisterLiteral(envPtr, bytes, length, onHeap)
sl@0
   169
    CompileEnv *envPtr;		/* Points to the CompileEnv in whose object
sl@0
   170
				 * array an object is found or created. */
sl@0
   171
    register char *bytes;	/* Points to string for which to find or
sl@0
   172
				 * create an object in CompileEnv's object
sl@0
   173
				 * array. */
sl@0
   174
    int length;			/* Number of bytes in the string. If < 0,
sl@0
   175
				 * the string consists of all bytes up to
sl@0
   176
				 * the first null character. */
sl@0
   177
    int onHeap;			/* If 1 then the caller already malloc'd
sl@0
   178
				 * bytes and ownership is passed to this
sl@0
   179
				 * procedure. */
sl@0
   180
{
sl@0
   181
    Interp *iPtr = envPtr->iPtr;
sl@0
   182
    LiteralTable *globalTablePtr = &(iPtr->literalTable);
sl@0
   183
    LiteralTable *localTablePtr = &(envPtr->localLitTable);
sl@0
   184
    register LiteralEntry *globalPtr, *localPtr;
sl@0
   185
    register Tcl_Obj *objPtr;
sl@0
   186
    unsigned int hash;
sl@0
   187
    int localHash, globalHash, objIndex;
sl@0
   188
    long n;
sl@0
   189
    char buf[TCL_INTEGER_SPACE];
sl@0
   190
sl@0
   191
    if (length < 0) {
sl@0
   192
	length = (bytes? strlen(bytes) : 0);
sl@0
   193
    }
sl@0
   194
    hash = HashString(bytes, length);
sl@0
   195
sl@0
   196
    /*
sl@0
   197
     * Is the literal already in the CompileEnv's local literal array?
sl@0
   198
     * If so, just return its index.
sl@0
   199
     */
sl@0
   200
sl@0
   201
    localHash = (hash & localTablePtr->mask);
sl@0
   202
    for (localPtr = localTablePtr->buckets[localHash];
sl@0
   203
	  localPtr != NULL;  localPtr = localPtr->nextPtr) {
sl@0
   204
	objPtr = localPtr->objPtr;
sl@0
   205
	if ((objPtr->length == length) && ((length == 0)
sl@0
   206
		|| ((objPtr->bytes[0] == bytes[0])
sl@0
   207
			&& (memcmp(objPtr->bytes, bytes, (unsigned) length)
sl@0
   208
				== 0)))) {
sl@0
   209
	    if (onHeap) {
sl@0
   210
		ckfree(bytes);
sl@0
   211
	    }
sl@0
   212
	    objIndex = (localPtr - envPtr->literalArrayPtr);
sl@0
   213
#ifdef TCL_COMPILE_DEBUG
sl@0
   214
	    TclVerifyLocalLiteralTable(envPtr);
sl@0
   215
#endif /*TCL_COMPILE_DEBUG*/
sl@0
   216
sl@0
   217
	    return objIndex;
sl@0
   218
	}
sl@0
   219
    }
sl@0
   220
sl@0
   221
    /*
sl@0
   222
     * The literal is new to this CompileEnv. Is it in the interpreter's
sl@0
   223
     * global literal table?
sl@0
   224
     */
sl@0
   225
sl@0
   226
    globalHash = (hash & globalTablePtr->mask);
sl@0
   227
    for (globalPtr = globalTablePtr->buckets[globalHash];
sl@0
   228
	 globalPtr != NULL;  globalPtr = globalPtr->nextPtr) {
sl@0
   229
	objPtr = globalPtr->objPtr;
sl@0
   230
	if ((objPtr->length == length) && ((length == 0)
sl@0
   231
		|| ((objPtr->bytes[0] == bytes[0])
sl@0
   232
			&& (memcmp(objPtr->bytes, bytes, (unsigned) length)
sl@0
   233
				== 0)))) {
sl@0
   234
	    /*
sl@0
   235
	     * A global literal was found. Add an entry to the CompileEnv's
sl@0
   236
	     * local literal array.
sl@0
   237
	     */
sl@0
   238
	    
sl@0
   239
	    if (onHeap) {
sl@0
   240
		ckfree(bytes);
sl@0
   241
	    }
sl@0
   242
	    objIndex = AddLocalLiteralEntry(envPtr, globalPtr, localHash);
sl@0
   243
#ifdef TCL_COMPILE_DEBUG
sl@0
   244
	    if (globalPtr->refCount < 1) {
sl@0
   245
		panic("TclRegisterLiteral: global literal \"%.*s\" had bad refCount %d",
sl@0
   246
			(length>60? 60 : length), bytes,
sl@0
   247
			globalPtr->refCount);
sl@0
   248
	    }
sl@0
   249
	    TclVerifyLocalLiteralTable(envPtr);
sl@0
   250
#endif /*TCL_COMPILE_DEBUG*/ 
sl@0
   251
	    return objIndex;
sl@0
   252
	}
sl@0
   253
    }
sl@0
   254
sl@0
   255
    /*
sl@0
   256
     * The literal is new to the interpreter. Add it to the global literal
sl@0
   257
     * table then add an entry to the CompileEnv's local literal array.
sl@0
   258
     * Convert the object to an integer object if possible.
sl@0
   259
     */
sl@0
   260
sl@0
   261
    TclNewObj(objPtr);
sl@0
   262
    Tcl_IncrRefCount(objPtr);
sl@0
   263
    if (onHeap) {
sl@0
   264
	objPtr->bytes = bytes;
sl@0
   265
	objPtr->length = length;
sl@0
   266
    } else {
sl@0
   267
	TclInitStringRep(objPtr, bytes, length);
sl@0
   268
    }
sl@0
   269
sl@0
   270
    if (TclLooksLikeInt(bytes, length)) {
sl@0
   271
	/*
sl@0
   272
	 * From here we use the objPtr, because it is NULL terminated
sl@0
   273
	 */
sl@0
   274
	if (TclGetLong((Tcl_Interp *) NULL, objPtr->bytes, &n) == TCL_OK) {
sl@0
   275
	    TclFormatInt(buf, n);
sl@0
   276
	    if (strcmp(objPtr->bytes, buf) == 0) {
sl@0
   277
		objPtr->internalRep.longValue = n;
sl@0
   278
		objPtr->typePtr = &tclIntType;
sl@0
   279
	    }
sl@0
   280
	}
sl@0
   281
    }
sl@0
   282
    
sl@0
   283
#ifdef TCL_COMPILE_DEBUG
sl@0
   284
    if (TclLookupLiteralEntry((Tcl_Interp *) iPtr, objPtr) != NULL) {
sl@0
   285
	panic("TclRegisterLiteral: literal \"%.*s\" found globally but shouldn't be",
sl@0
   286
	        (length>60? 60 : length), bytes);
sl@0
   287
    }
sl@0
   288
#endif
sl@0
   289
sl@0
   290
    globalPtr = (LiteralEntry *) ckalloc((unsigned) sizeof(LiteralEntry));
sl@0
   291
    globalPtr->objPtr = objPtr;
sl@0
   292
    globalPtr->refCount = 0;
sl@0
   293
    globalPtr->nextPtr = globalTablePtr->buckets[globalHash];
sl@0
   294
    globalTablePtr->buckets[globalHash] = globalPtr;
sl@0
   295
    globalTablePtr->numEntries++;
sl@0
   296
sl@0
   297
    /*
sl@0
   298
     * If the global literal table has exceeded a decent size, rebuild it
sl@0
   299
     * with more buckets.
sl@0
   300
     */
sl@0
   301
sl@0
   302
    if (globalTablePtr->numEntries >= globalTablePtr->rebuildSize) {
sl@0
   303
	RebuildLiteralTable(globalTablePtr);
sl@0
   304
    }
sl@0
   305
    objIndex = AddLocalLiteralEntry(envPtr, globalPtr, localHash);
sl@0
   306
sl@0
   307
#ifdef TCL_COMPILE_DEBUG
sl@0
   308
    TclVerifyGlobalLiteralTable(iPtr);
sl@0
   309
    TclVerifyLocalLiteralTable(envPtr);
sl@0
   310
    {
sl@0
   311
	LiteralEntry *entryPtr;
sl@0
   312
	int found, i;
sl@0
   313
	found = 0;
sl@0
   314
	for (i = 0;  i < globalTablePtr->numBuckets;  i++) {
sl@0
   315
	    for (entryPtr = globalTablePtr->buckets[i];
sl@0
   316
		    entryPtr != NULL;  entryPtr = entryPtr->nextPtr) {
sl@0
   317
		if ((entryPtr == globalPtr)
sl@0
   318
		        && (entryPtr->objPtr == objPtr)) {
sl@0
   319
		    found = 1;
sl@0
   320
		}
sl@0
   321
	    }
sl@0
   322
	}
sl@0
   323
	if (!found) {
sl@0
   324
	    panic("TclRegisterLiteral: literal \"%.*s\" wasn't global",
sl@0
   325
	            (length>60? 60 : length), bytes);
sl@0
   326
	}
sl@0
   327
    }
sl@0
   328
#endif /*TCL_COMPILE_DEBUG*/
sl@0
   329
#ifdef TCL_COMPILE_STATS   
sl@0
   330
    iPtr->stats.numLiteralsCreated++;
sl@0
   331
    iPtr->stats.totalLitStringBytes   += (double) (length + 1);
sl@0
   332
    iPtr->stats.currentLitStringBytes += (double) (length + 1);
sl@0
   333
    iPtr->stats.literalCount[TclLog2(length)]++;
sl@0
   334
#endif /*TCL_COMPILE_STATS*/
sl@0
   335
    return objIndex;
sl@0
   336
}
sl@0
   337

sl@0
   338
/*
sl@0
   339
 *----------------------------------------------------------------------
sl@0
   340
 *
sl@0
   341
 * TclLookupLiteralEntry --
sl@0
   342
 *
sl@0
   343
 *	Finds the LiteralEntry that corresponds to a literal Tcl object
sl@0
   344
 *      holding a literal.
sl@0
   345
 *
sl@0
   346
 * Results:
sl@0
   347
 *      Returns the matching LiteralEntry if found, otherwise NULL.
sl@0
   348
 *
sl@0
   349
 * Side effects:
sl@0
   350
 *      None.
sl@0
   351
 *
sl@0
   352
 *----------------------------------------------------------------------
sl@0
   353
 */
sl@0
   354
sl@0
   355
LiteralEntry *
sl@0
   356
TclLookupLiteralEntry(interp, objPtr)
sl@0
   357
    Tcl_Interp *interp;		/* Interpreter for which objPtr was created
sl@0
   358
                                 * to hold a literal. */
sl@0
   359
    register Tcl_Obj *objPtr;	/* Points to a Tcl object holding a
sl@0
   360
                                 * literal that was previously created by a
sl@0
   361
                                 * call to TclRegisterLiteral. */
sl@0
   362
{
sl@0
   363
    Interp *iPtr = (Interp *) interp;
sl@0
   364
    LiteralTable *globalTablePtr = &(iPtr->literalTable);
sl@0
   365
    register LiteralEntry *entryPtr;
sl@0
   366
    char *bytes;
sl@0
   367
    int length, globalHash;
sl@0
   368
sl@0
   369
    bytes = Tcl_GetStringFromObj(objPtr, &length);
sl@0
   370
    globalHash = (HashString(bytes, length) & globalTablePtr->mask);
sl@0
   371
    for (entryPtr = globalTablePtr->buckets[globalHash];
sl@0
   372
            entryPtr != NULL;  entryPtr = entryPtr->nextPtr) {
sl@0
   373
        if (entryPtr->objPtr == objPtr) {
sl@0
   374
            return entryPtr;
sl@0
   375
        }
sl@0
   376
    }
sl@0
   377
    return NULL;
sl@0
   378
}
sl@0
   379

sl@0
   380
/*
sl@0
   381
 *----------------------------------------------------------------------
sl@0
   382
 *
sl@0
   383
 * TclHideLiteral --
sl@0
   384
 *
sl@0
   385
 *	Remove a literal entry from the literal hash tables, leaving it in
sl@0
   386
 *	the literal array so existing references continue to function.
sl@0
   387
 *	This makes it possible to turn a shared literal into a private
sl@0
   388
 *	literal that cannot be shared.
sl@0
   389
 *
sl@0
   390
 * Results:
sl@0
   391
 *	None.
sl@0
   392
 *
sl@0
   393
 * Side effects:
sl@0
   394
 *	Removes the literal from the local hash table and decrements the
sl@0
   395
 *	global hash entry's reference count.
sl@0
   396
 *
sl@0
   397
 *----------------------------------------------------------------------
sl@0
   398
 */
sl@0
   399
sl@0
   400
void
sl@0
   401
TclHideLiteral(interp, envPtr, index)
sl@0
   402
    Tcl_Interp *interp;		 /* Interpreter for which objPtr was created
sl@0
   403
                                  * to hold a literal. */
sl@0
   404
    register CompileEnv *envPtr; /* Points to CompileEnv whose literal array
sl@0
   405
				  * contains the entry being hidden. */
sl@0
   406
    int index;			 /* The index of the entry in the literal
sl@0
   407
				  * array. */
sl@0
   408
{
sl@0
   409
    LiteralEntry **nextPtrPtr, *entryPtr, *lPtr;
sl@0
   410
    LiteralTable *localTablePtr = &(envPtr->localLitTable);
sl@0
   411
    int localHash, length;
sl@0
   412
    char *bytes;
sl@0
   413
    Tcl_Obj *newObjPtr;
sl@0
   414
sl@0
   415
    lPtr = &(envPtr->literalArrayPtr[index]);
sl@0
   416
sl@0
   417
    /*
sl@0
   418
     * To avoid unwanted sharing we need to copy the object and remove it from
sl@0
   419
     * the local and global literal tables.  It still has a slot in the literal
sl@0
   420
     * array so it can be referred to by byte codes, but it will not be matched
sl@0
   421
     * by literal searches.
sl@0
   422
     */
sl@0
   423
sl@0
   424
    newObjPtr = Tcl_DuplicateObj(lPtr->objPtr);
sl@0
   425
    Tcl_IncrRefCount(newObjPtr);
sl@0
   426
    TclReleaseLiteral(interp, lPtr->objPtr);
sl@0
   427
    lPtr->objPtr = newObjPtr;
sl@0
   428
sl@0
   429
    bytes = Tcl_GetStringFromObj(newObjPtr, &length);
sl@0
   430
    localHash = (HashString(bytes, length) & localTablePtr->mask);
sl@0
   431
    nextPtrPtr = &localTablePtr->buckets[localHash];
sl@0
   432
sl@0
   433
    for (entryPtr = *nextPtrPtr; entryPtr != NULL; entryPtr = *nextPtrPtr) {
sl@0
   434
	if (entryPtr == lPtr) {
sl@0
   435
	    *nextPtrPtr = lPtr->nextPtr;
sl@0
   436
	    lPtr->nextPtr = NULL;
sl@0
   437
	    localTablePtr->numEntries--;
sl@0
   438
	    break;
sl@0
   439
	}
sl@0
   440
	nextPtrPtr = &entryPtr->nextPtr;
sl@0
   441
    }
sl@0
   442
}
sl@0
   443

sl@0
   444
/*
sl@0
   445
 *----------------------------------------------------------------------
sl@0
   446
 *
sl@0
   447
 * TclAddLiteralObj --
sl@0
   448
 *
sl@0
   449
 *	Add a single literal object to the literal array.  This
sl@0
   450
 *	function does not add the literal to the local or global
sl@0
   451
 *	literal tables.  The caller is expected to add the entry
sl@0
   452
 *	to whatever tables are appropriate.
sl@0
   453
 *
sl@0
   454
 * Results:
sl@0
   455
 *	The index in the CompileEnv's literal array that references the
sl@0
   456
 *	literal.  Stores the pointer to the new literal entry in the
sl@0
   457
 *	location referenced by the localPtrPtr argument.
sl@0
   458
 *
sl@0
   459
 * Side effects:
sl@0
   460
 *	Expands the literal array if necessary.  Increments the refcount
sl@0
   461
 *	on the literal object.
sl@0
   462
 *
sl@0
   463
 *----------------------------------------------------------------------
sl@0
   464
 */
sl@0
   465
sl@0
   466
int
sl@0
   467
TclAddLiteralObj(envPtr, objPtr, litPtrPtr)
sl@0
   468
    register CompileEnv *envPtr; /* Points to CompileEnv in whose literal
sl@0
   469
				  * array the object is to be inserted. */
sl@0
   470
    Tcl_Obj *objPtr;		 /* The object to insert into the array. */
sl@0
   471
    LiteralEntry **litPtrPtr;	 /* The location where the pointer to the
sl@0
   472
				  * new literal entry should be stored.
sl@0
   473
				  * May be NULL. */
sl@0
   474
{
sl@0
   475
    register LiteralEntry *lPtr;
sl@0
   476
    int objIndex;
sl@0
   477
sl@0
   478
    if (envPtr->literalArrayNext >= envPtr->literalArrayEnd) {
sl@0
   479
	ExpandLocalLiteralArray(envPtr);
sl@0
   480
    }
sl@0
   481
    objIndex = envPtr->literalArrayNext;
sl@0
   482
    envPtr->literalArrayNext++;
sl@0
   483
sl@0
   484
    lPtr = &(envPtr->literalArrayPtr[objIndex]);
sl@0
   485
    lPtr->objPtr = objPtr;
sl@0
   486
    Tcl_IncrRefCount(objPtr);
sl@0
   487
    lPtr->refCount = -1;	/* i.e., unused */
sl@0
   488
    lPtr->nextPtr = NULL;
sl@0
   489
sl@0
   490
    if (litPtrPtr) {
sl@0
   491
	*litPtrPtr = lPtr;
sl@0
   492
    }
sl@0
   493
sl@0
   494
    return objIndex;
sl@0
   495
}
sl@0
   496

sl@0
   497
/*
sl@0
   498
 *----------------------------------------------------------------------
sl@0
   499
 *
sl@0
   500
 * AddLocalLiteralEntry --
sl@0
   501
 *
sl@0
   502
 *	Insert a new literal into a CompileEnv's local literal array.
sl@0
   503
 *
sl@0
   504
 * Results:
sl@0
   505
 *	The index in the CompileEnv's literal array that references the
sl@0
   506
 *	literal.
sl@0
   507
 *
sl@0
   508
 * Side effects:
sl@0
   509
 *	Increments the ref count of the global LiteralEntry since the
sl@0
   510
 *	CompileEnv now refers to the literal. Expands the literal array
sl@0
   511
 *	if necessary. May rebuild the hash bucket array of the CompileEnv's
sl@0
   512
 *	literal array if it becomes too large.
sl@0
   513
 *
sl@0
   514
 *----------------------------------------------------------------------
sl@0
   515
 */
sl@0
   516
sl@0
   517
static int
sl@0
   518
AddLocalLiteralEntry(envPtr, globalPtr, localHash)
sl@0
   519
    register CompileEnv *envPtr; /* Points to CompileEnv in whose literal
sl@0
   520
				  * array the object is to be inserted. */
sl@0
   521
    LiteralEntry *globalPtr;	 /* Points to the global LiteralEntry for
sl@0
   522
				  * the literal to add to the CompileEnv. */
sl@0
   523
    int localHash;		 /* Hash value for the literal's string. */
sl@0
   524
{
sl@0
   525
    register LiteralTable *localTablePtr = &(envPtr->localLitTable);
sl@0
   526
    LiteralEntry *localPtr;
sl@0
   527
    int objIndex;
sl@0
   528
    
sl@0
   529
    objIndex = TclAddLiteralObj(envPtr, globalPtr->objPtr, &localPtr);
sl@0
   530
sl@0
   531
    /*
sl@0
   532
     * Add the literal to the local table.
sl@0
   533
     */
sl@0
   534
sl@0
   535
    localPtr->nextPtr = localTablePtr->buckets[localHash];
sl@0
   536
    localTablePtr->buckets[localHash] = localPtr;
sl@0
   537
    localTablePtr->numEntries++;
sl@0
   538
sl@0
   539
    globalPtr->refCount++;
sl@0
   540
sl@0
   541
    /*
sl@0
   542
     * If the CompileEnv's local literal table has exceeded a decent size,
sl@0
   543
     * rebuild it with more buckets.
sl@0
   544
     */
sl@0
   545
sl@0
   546
    if (localTablePtr->numEntries >= localTablePtr->rebuildSize) {
sl@0
   547
	RebuildLiteralTable(localTablePtr);
sl@0
   548
    }
sl@0
   549
sl@0
   550
#ifdef TCL_COMPILE_DEBUG
sl@0
   551
    TclVerifyLocalLiteralTable(envPtr);
sl@0
   552
    {
sl@0
   553
	char *bytes;
sl@0
   554
	int length, found, i;
sl@0
   555
	found = 0;
sl@0
   556
	for (i = 0;  i < localTablePtr->numBuckets;  i++) {
sl@0
   557
	    for (localPtr = localTablePtr->buckets[i];
sl@0
   558
		    localPtr != NULL;  localPtr = localPtr->nextPtr) {
sl@0
   559
		if (localPtr->objPtr == globalPtr->objPtr) {
sl@0
   560
		    found = 1;
sl@0
   561
		}
sl@0
   562
	    }
sl@0
   563
	}
sl@0
   564
	if (!found) {
sl@0
   565
	    bytes = Tcl_GetStringFromObj(globalPtr->objPtr, &length);
sl@0
   566
	    panic("AddLocalLiteralEntry: literal \"%.*s\" wasn't found locally",
sl@0
   567
	            (length>60? 60 : length), bytes);
sl@0
   568
	}
sl@0
   569
    }
sl@0
   570
#endif /*TCL_COMPILE_DEBUG*/
sl@0
   571
    return objIndex;
sl@0
   572
}
sl@0
   573

sl@0
   574
/*
sl@0
   575
 *----------------------------------------------------------------------
sl@0
   576
 *
sl@0
   577
 * ExpandLocalLiteralArray --
sl@0
   578
 *
sl@0
   579
 *	Procedure that uses malloc to allocate more storage for a
sl@0
   580
 *	CompileEnv's local literal array.
sl@0
   581
 *
sl@0
   582
 * Results:
sl@0
   583
 *	None.
sl@0
   584
 *
sl@0
   585
 * Side effects:
sl@0
   586
 *	The literal array in *envPtr is reallocated to a new array of
sl@0
   587
 *	double the size, and if envPtr->mallocedLiteralArray is non-zero
sl@0
   588
 *	the old array is freed. Entries are copied from the old array
sl@0
   589
 *	to the new one. The local literal table is updated to refer to
sl@0
   590
 *	the new entries.
sl@0
   591
 *
sl@0
   592
 *----------------------------------------------------------------------
sl@0
   593
 */
sl@0
   594
sl@0
   595
static void
sl@0
   596
ExpandLocalLiteralArray(envPtr)
sl@0
   597
    register CompileEnv *envPtr; /* Points to the CompileEnv whose object
sl@0
   598
				  * array must be enlarged. */
sl@0
   599
{
sl@0
   600
    /*
sl@0
   601
     * The current allocated local literal entries are stored between
sl@0
   602
     * elements 0 and (envPtr->literalArrayNext - 1) [inclusive].
sl@0
   603
     */
sl@0
   604
sl@0
   605
    LiteralTable *localTablePtr = &(envPtr->localLitTable);
sl@0
   606
    int currElems = envPtr->literalArrayNext;
sl@0
   607
    size_t currBytes = (currElems * sizeof(LiteralEntry));
sl@0
   608
    register LiteralEntry *currArrayPtr = envPtr->literalArrayPtr;
sl@0
   609
    register LiteralEntry *newArrayPtr =
sl@0
   610
	    (LiteralEntry *) ckalloc((unsigned) (2 * currBytes));
sl@0
   611
    int i;
sl@0
   612
    
sl@0
   613
    /*
sl@0
   614
     * Copy from the old literal array to the new, then update the local
sl@0
   615
     * literal table's bucket array.
sl@0
   616
     */
sl@0
   617
sl@0
   618
    memcpy((VOID *) newArrayPtr, (VOID *) currArrayPtr, currBytes);
sl@0
   619
    for (i = 0;  i < currElems;  i++) {
sl@0
   620
	if (currArrayPtr[i].nextPtr == NULL) {
sl@0
   621
	    newArrayPtr[i].nextPtr = NULL;
sl@0
   622
	} else {
sl@0
   623
	    newArrayPtr[i].nextPtr = newArrayPtr
sl@0
   624
		    + (currArrayPtr[i].nextPtr - currArrayPtr);
sl@0
   625
	}
sl@0
   626
    }
sl@0
   627
    for (i = 0;  i < localTablePtr->numBuckets;  i++) {
sl@0
   628
	if (localTablePtr->buckets[i] != NULL) {
sl@0
   629
	    localTablePtr->buckets[i] = newArrayPtr
sl@0
   630
	            + (localTablePtr->buckets[i] - currArrayPtr);
sl@0
   631
	}
sl@0
   632
    }
sl@0
   633
sl@0
   634
    /*
sl@0
   635
     * Free the old literal array if needed, and mark the new literal
sl@0
   636
     * array as malloced.
sl@0
   637
     */
sl@0
   638
    
sl@0
   639
    if (envPtr->mallocedLiteralArray) {
sl@0
   640
	ckfree((char *) currArrayPtr);
sl@0
   641
    }
sl@0
   642
    envPtr->literalArrayPtr = newArrayPtr;
sl@0
   643
    envPtr->literalArrayEnd = (2 * currElems);
sl@0
   644
    envPtr->mallocedLiteralArray = 1;
sl@0
   645
}
sl@0
   646

sl@0
   647
/*
sl@0
   648
 *----------------------------------------------------------------------
sl@0
   649
 *
sl@0
   650
 * TclReleaseLiteral --
sl@0
   651
 *
sl@0
   652
 *	This procedure releases a reference to one of the shared Tcl objects
sl@0
   653
 *	that hold literals. It is called to release the literals referenced
sl@0
   654
 *	by a ByteCode that is being destroyed, and it is also called by
sl@0
   655
 *	TclDeleteLiteralTable.
sl@0
   656
 *
sl@0
   657
 * Results:
sl@0
   658
 *	None.
sl@0
   659
 *
sl@0
   660
 * Side effects:
sl@0
   661
 *	The reference count for the global LiteralTable entry that 
sl@0
   662
 *	corresponds to the literal is decremented. If no other reference
sl@0
   663
 *	to a global literal object remains, it is freed.
sl@0
   664
 *
sl@0
   665
 *----------------------------------------------------------------------
sl@0
   666
 */
sl@0
   667
sl@0
   668
void
sl@0
   669
TclReleaseLiteral(interp, objPtr)
sl@0
   670
    Tcl_Interp *interp;		/* Interpreter for which objPtr was created
sl@0
   671
				 * to hold a literal. */
sl@0
   672
    register Tcl_Obj *objPtr;	/* Points to a literal object that was
sl@0
   673
				 * previously created by a call to
sl@0
   674
				 * TclRegisterLiteral. */
sl@0
   675
{
sl@0
   676
    Interp *iPtr = (Interp *) interp;
sl@0
   677
    LiteralTable *globalTablePtr = &(iPtr->literalTable);
sl@0
   678
    register LiteralEntry *entryPtr, *prevPtr;
sl@0
   679
    ByteCode* codePtr;
sl@0
   680
    char *bytes;
sl@0
   681
    int length, index;
sl@0
   682
sl@0
   683
    bytes = Tcl_GetStringFromObj(objPtr, &length);
sl@0
   684
    index = (HashString(bytes, length) & globalTablePtr->mask);
sl@0
   685
sl@0
   686
    /*
sl@0
   687
     * Check to see if the object is in the global literal table and 
sl@0
   688
     * remove this reference.  The object may not be in the table if
sl@0
   689
     * it is a hidden local literal.
sl@0
   690
     */
sl@0
   691
sl@0
   692
    for (prevPtr = NULL, entryPtr = globalTablePtr->buckets[index];
sl@0
   693
	    entryPtr != NULL;
sl@0
   694
	    prevPtr = entryPtr, entryPtr = entryPtr->nextPtr) {
sl@0
   695
	if (entryPtr->objPtr == objPtr) {
sl@0
   696
	    entryPtr->refCount--;
sl@0
   697
sl@0
   698
	    /*
sl@0
   699
	     * If the literal is no longer being used by any ByteCode,
sl@0
   700
	     * delete the entry then remove the reference corresponding 
sl@0
   701
	     * to the global literal table entry (decrement the ref count 
sl@0
   702
	     * of the object).
sl@0
   703
	     */
sl@0
   704
		
sl@0
   705
	    if (entryPtr->refCount == 0) {
sl@0
   706
		if (prevPtr == NULL) {
sl@0
   707
		    globalTablePtr->buckets[index] = entryPtr->nextPtr;
sl@0
   708
		} else {
sl@0
   709
		    prevPtr->nextPtr = entryPtr->nextPtr;
sl@0
   710
		}
sl@0
   711
		ckfree((char *) entryPtr);
sl@0
   712
		globalTablePtr->numEntries--;
sl@0
   713
sl@0
   714
		TclDecrRefCount(objPtr);
sl@0
   715
sl@0
   716
		/*
sl@0
   717
		 * Check if the LiteralEntry is only being kept alive by 
sl@0
   718
		 * a circular reference from a ByteCode stored as its 
sl@0
   719
		 * internal rep. In that case, set the ByteCode object array 
sl@0
   720
		 * entry NULL to signal to TclCleanupByteCode to not try to 
sl@0
   721
		 * release this about to be freed literal again.
sl@0
   722
		 */
sl@0
   723
	    
sl@0
   724
		if (objPtr->typePtr == &tclByteCodeType) {
sl@0
   725
		    codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
sl@0
   726
		    if ((codePtr->numLitObjects == 1)
sl@0
   727
		            && (codePtr->objArrayPtr[0] == objPtr)) {			
sl@0
   728
			codePtr->objArrayPtr[0] = NULL;
sl@0
   729
		    }
sl@0
   730
		}
sl@0
   731
sl@0
   732
#ifdef TCL_COMPILE_STATS
sl@0
   733
		iPtr->stats.currentLitStringBytes -= (double) (length + 1);
sl@0
   734
#endif /*TCL_COMPILE_STATS*/
sl@0
   735
	    }
sl@0
   736
	    break;
sl@0
   737
	}
sl@0
   738
    }
sl@0
   739
    
sl@0
   740
    /*
sl@0
   741
     * Remove the reference corresponding to the local literal table
sl@0
   742
     * entry.
sl@0
   743
     */
sl@0
   744
sl@0
   745
    Tcl_DecrRefCount(objPtr);
sl@0
   746
}
sl@0
   747

sl@0
   748
/*
sl@0
   749
 *----------------------------------------------------------------------
sl@0
   750
 *
sl@0
   751
 * HashString --
sl@0
   752
 *
sl@0
   753
 *	Compute a one-word summary of a text string, which can be
sl@0
   754
 *	used to generate a hash index.
sl@0
   755
 *
sl@0
   756
 * Results:
sl@0
   757
 *	The return value is a one-word summary of the information in
sl@0
   758
 *	string.
sl@0
   759
 *
sl@0
   760
 * Side effects:
sl@0
   761
 *	None.
sl@0
   762
 *
sl@0
   763
 *----------------------------------------------------------------------
sl@0
   764
 */
sl@0
   765
sl@0
   766
static unsigned int
sl@0
   767
HashString(bytes, length)
sl@0
   768
    register CONST char *bytes; /* String for which to compute hash
sl@0
   769
				 * value. */
sl@0
   770
    int length;			/* Number of bytes in the string. */
sl@0
   771
{
sl@0
   772
    register unsigned int result;
sl@0
   773
    register int i;
sl@0
   774
sl@0
   775
    /*
sl@0
   776
     * I tried a zillion different hash functions and asked many other
sl@0
   777
     * people for advice.  Many people had their own favorite functions,
sl@0
   778
     * all different, but no-one had much idea why they were good ones.
sl@0
   779
     * I chose the one below (multiply by 9 and add new character)
sl@0
   780
     * because of the following reasons:
sl@0
   781
     *
sl@0
   782
     * 1. Multiplying by 10 is perfect for keys that are decimal strings,
sl@0
   783
     *    and multiplying by 9 is just about as good.
sl@0
   784
     * 2. Times-9 is (shift-left-3) plus (old).  This means that each
sl@0
   785
     *    character's bits hang around in the low-order bits of the
sl@0
   786
     *    hash value for ever, plus they spread fairly rapidly up to
sl@0
   787
     *    the high-order bits to fill out the hash value.  This seems
sl@0
   788
     *    works well both for decimal and non-decimal strings.
sl@0
   789
     */
sl@0
   790
sl@0
   791
    result = 0;
sl@0
   792
    for (i = 0;  i < length;  i++) {
sl@0
   793
	result += (result<<3) + *bytes++;
sl@0
   794
    }
sl@0
   795
    return result;
sl@0
   796
}
sl@0
   797

sl@0
   798
/*
sl@0
   799
 *----------------------------------------------------------------------
sl@0
   800
 *
sl@0
   801
 * RebuildLiteralTable --
sl@0
   802
 *
sl@0
   803
 *	This procedure is invoked when the ratio of entries to hash buckets
sl@0
   804
 *	becomes too large in a local or global literal table. It allocates
sl@0
   805
 *	a larger bucket array and moves the entries into the new buckets.
sl@0
   806
 *
sl@0
   807
 * Results:
sl@0
   808
 *	None.
sl@0
   809
 *
sl@0
   810
 * Side effects:
sl@0
   811
 *	Memory gets reallocated and entries get rehashed into new buckets.
sl@0
   812
 *
sl@0
   813
 *----------------------------------------------------------------------
sl@0
   814
 */
sl@0
   815
sl@0
   816
static void
sl@0
   817
RebuildLiteralTable(tablePtr)
sl@0
   818
    register LiteralTable *tablePtr; /* Local or global table to enlarge. */
sl@0
   819
{
sl@0
   820
    LiteralEntry **oldBuckets;
sl@0
   821
    register LiteralEntry **oldChainPtr, **newChainPtr;
sl@0
   822
    register LiteralEntry *entryPtr;
sl@0
   823
    LiteralEntry **bucketPtr;
sl@0
   824
    char *bytes;
sl@0
   825
    int oldSize, count, index, length;
sl@0
   826
sl@0
   827
    oldSize = tablePtr->numBuckets;
sl@0
   828
    oldBuckets = tablePtr->buckets;
sl@0
   829
sl@0
   830
    /*
sl@0
   831
     * Allocate and initialize the new bucket array, and set up
sl@0
   832
     * hashing constants for new array size.
sl@0
   833
     */
sl@0
   834
sl@0
   835
    tablePtr->numBuckets *= 4;
sl@0
   836
    tablePtr->buckets = (LiteralEntry **) ckalloc((unsigned)
sl@0
   837
	    (tablePtr->numBuckets * sizeof(LiteralEntry *)));
sl@0
   838
    for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets;
sl@0
   839
	    count > 0;
sl@0
   840
	    count--, newChainPtr++) {
sl@0
   841
	*newChainPtr = NULL;
sl@0
   842
    }
sl@0
   843
    tablePtr->rebuildSize *= 4;
sl@0
   844
    tablePtr->mask = (tablePtr->mask << 2) + 3;
sl@0
   845
sl@0
   846
    /*
sl@0
   847
     * Rehash all of the existing entries into the new bucket array.
sl@0
   848
     */
sl@0
   849
sl@0
   850
    for (oldChainPtr = oldBuckets;
sl@0
   851
	    oldSize > 0;
sl@0
   852
	    oldSize--, oldChainPtr++) {
sl@0
   853
	for (entryPtr = *oldChainPtr;  entryPtr != NULL;
sl@0
   854
	        entryPtr = *oldChainPtr) {
sl@0
   855
	    bytes = Tcl_GetStringFromObj(entryPtr->objPtr, &length);
sl@0
   856
	    index = (HashString(bytes, length) & tablePtr->mask);
sl@0
   857
	    
sl@0
   858
	    *oldChainPtr = entryPtr->nextPtr;
sl@0
   859
	    bucketPtr = &(tablePtr->buckets[index]);
sl@0
   860
	    entryPtr->nextPtr = *bucketPtr;
sl@0
   861
	    *bucketPtr = entryPtr;
sl@0
   862
	}
sl@0
   863
    }
sl@0
   864
sl@0
   865
    /*
sl@0
   866
     * Free up the old bucket array, if it was dynamically allocated.
sl@0
   867
     */
sl@0
   868
sl@0
   869
    if (oldBuckets != tablePtr->staticBuckets) {
sl@0
   870
	ckfree((char *) oldBuckets);
sl@0
   871
    }
sl@0
   872
}
sl@0
   873

sl@0
   874
#ifdef TCL_COMPILE_STATS
sl@0
   875
/*
sl@0
   876
 *----------------------------------------------------------------------
sl@0
   877
 *
sl@0
   878
 * TclLiteralStats --
sl@0
   879
 *
sl@0
   880
 *	Return statistics describing the layout of the hash table
sl@0
   881
 *	in its hash buckets.
sl@0
   882
 *
sl@0
   883
 * Results:
sl@0
   884
 *	The return value is a malloc-ed string containing information
sl@0
   885
 *	about tablePtr.  It is the caller's responsibility to free
sl@0
   886
 *	this string.
sl@0
   887
 *
sl@0
   888
 * Side effects:
sl@0
   889
 *	None.
sl@0
   890
 *
sl@0
   891
 *----------------------------------------------------------------------
sl@0
   892
 */
sl@0
   893
sl@0
   894
char *
sl@0
   895
TclLiteralStats(tablePtr)
sl@0
   896
    LiteralTable *tablePtr;	/* Table for which to produce stats. */
sl@0
   897
{
sl@0
   898
#define NUM_COUNTERS 10
sl@0
   899
    int count[NUM_COUNTERS], overflow, i, j;
sl@0
   900
    double average, tmp;
sl@0
   901
    register LiteralEntry *entryPtr;
sl@0
   902
    char *result, *p;
sl@0
   903
sl@0
   904
    /*
sl@0
   905
     * Compute a histogram of bucket usage. For each bucket chain i,
sl@0
   906
     * j is the number of entries in the chain.
sl@0
   907
     */
sl@0
   908
sl@0
   909
    for (i = 0;  i < NUM_COUNTERS;  i++) {
sl@0
   910
	count[i] = 0;
sl@0
   911
    }
sl@0
   912
    overflow = 0;
sl@0
   913
    average = 0.0;
sl@0
   914
    for (i = 0;  i < tablePtr->numBuckets;  i++) {
sl@0
   915
	j = 0;
sl@0
   916
	for (entryPtr = tablePtr->buckets[i];  entryPtr != NULL;
sl@0
   917
	        entryPtr = entryPtr->nextPtr) {
sl@0
   918
	    j++;
sl@0
   919
	}
sl@0
   920
	if (j < NUM_COUNTERS) {
sl@0
   921
	    count[j]++;
sl@0
   922
	} else {
sl@0
   923
	    overflow++;
sl@0
   924
	}
sl@0
   925
	tmp = j;
sl@0
   926
	average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
sl@0
   927
    }
sl@0
   928
sl@0
   929
    /*
sl@0
   930
     * Print out the histogram and a few other pieces of information.
sl@0
   931
     */
sl@0
   932
sl@0
   933
    result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300));
sl@0
   934
    sprintf(result, "%d entries in table, %d buckets\n",
sl@0
   935
	    tablePtr->numEntries, tablePtr->numBuckets);
sl@0
   936
    p = result + strlen(result);
sl@0
   937
    for (i = 0; i < NUM_COUNTERS; i++) {
sl@0
   938
	sprintf(p, "number of buckets with %d entries: %d\n",
sl@0
   939
		i, count[i]);
sl@0
   940
	p += strlen(p);
sl@0
   941
    }
sl@0
   942
    sprintf(p, "number of buckets with %d or more entries: %d\n",
sl@0
   943
	    NUM_COUNTERS, overflow);
sl@0
   944
    p += strlen(p);
sl@0
   945
    sprintf(p, "average search distance for entry: %.1f", average);
sl@0
   946
    return result;
sl@0
   947
}
sl@0
   948
#endif /*TCL_COMPILE_STATS*/
sl@0
   949

sl@0
   950
#ifdef TCL_COMPILE_DEBUG
sl@0
   951
/*
sl@0
   952
 *----------------------------------------------------------------------
sl@0
   953
 *
sl@0
   954
 * TclVerifyLocalLiteralTable --
sl@0
   955
 *
sl@0
   956
 *	Check a CompileEnv's local literal table for consistency.
sl@0
   957
 *
sl@0
   958
 * Results:
sl@0
   959
 *	None.
sl@0
   960
 *
sl@0
   961
 * Side effects:
sl@0
   962
 *	Panics if problems are found.
sl@0
   963
 *
sl@0
   964
 *----------------------------------------------------------------------
sl@0
   965
 */
sl@0
   966
sl@0
   967
void
sl@0
   968
TclVerifyLocalLiteralTable(envPtr)
sl@0
   969
    CompileEnv *envPtr;		/* Points to CompileEnv whose literal
sl@0
   970
				 * table is to be validated. */
sl@0
   971
{
sl@0
   972
    register LiteralTable *localTablePtr = &(envPtr->localLitTable);
sl@0
   973
    register LiteralEntry *localPtr;
sl@0
   974
    char *bytes;
sl@0
   975
    register int i;
sl@0
   976
    int length, count;
sl@0
   977
sl@0
   978
    count = 0;
sl@0
   979
    for (i = 0;  i < localTablePtr->numBuckets;  i++) {
sl@0
   980
	for (localPtr = localTablePtr->buckets[i];
sl@0
   981
	        localPtr != NULL;  localPtr = localPtr->nextPtr) {
sl@0
   982
	    count++;
sl@0
   983
	    if (localPtr->refCount != -1) {
sl@0
   984
		bytes = Tcl_GetStringFromObj(localPtr->objPtr, &length);
sl@0
   985
		panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" had bad refCount %d",
sl@0
   986
		        (length>60? 60 : length), bytes,
sl@0
   987
		        localPtr->refCount);
sl@0
   988
	    }
sl@0
   989
	    if (TclLookupLiteralEntry((Tcl_Interp *) envPtr->iPtr,
sl@0
   990
		    localPtr->objPtr) == NULL) {
sl@0
   991
		bytes = Tcl_GetStringFromObj(localPtr->objPtr, &length);
sl@0
   992
		panic("TclVerifyLocalLiteralTable: local literal \"%.*s\" is not global",
sl@0
   993
		         (length>60? 60 : length), bytes);
sl@0
   994
	    }
sl@0
   995
	    if (localPtr->objPtr->bytes == NULL) {
sl@0
   996
		panic("TclVerifyLocalLiteralTable: literal has NULL string rep");
sl@0
   997
	    }
sl@0
   998
	}
sl@0
   999
    }
sl@0
  1000
    if (count != localTablePtr->numEntries) {
sl@0
  1001
	panic("TclVerifyLocalLiteralTable: local literal table had %d entries, should be %d",
sl@0
  1002
	      count, localTablePtr->numEntries);
sl@0
  1003
    }
sl@0
  1004
}
sl@0
  1005

sl@0
  1006
/*
sl@0
  1007
 *----------------------------------------------------------------------
sl@0
  1008
 *
sl@0
  1009
 * TclVerifyGlobalLiteralTable --
sl@0
  1010
 *
sl@0
  1011
 *	Check an interpreter's global literal table literal for consistency.
sl@0
  1012
 *
sl@0
  1013
 * Results:
sl@0
  1014
 *	None.
sl@0
  1015
 *
sl@0
  1016
 * Side effects:
sl@0
  1017
 *	Panics if problems are found.
sl@0
  1018
 *
sl@0
  1019
 *----------------------------------------------------------------------
sl@0
  1020
 */
sl@0
  1021
sl@0
  1022
void
sl@0
  1023
TclVerifyGlobalLiteralTable(iPtr)
sl@0
  1024
    Interp *iPtr;		/* Points to interpreter whose global
sl@0
  1025
				 * literal table is to be validated. */
sl@0
  1026
{
sl@0
  1027
    register LiteralTable *globalTablePtr = &(iPtr->literalTable);
sl@0
  1028
    register LiteralEntry *globalPtr;
sl@0
  1029
    char *bytes;
sl@0
  1030
    register int i;
sl@0
  1031
    int length, count;
sl@0
  1032
sl@0
  1033
    count = 0;
sl@0
  1034
    for (i = 0;  i < globalTablePtr->numBuckets;  i++) {
sl@0
  1035
	for (globalPtr = globalTablePtr->buckets[i];
sl@0
  1036
	        globalPtr != NULL;  globalPtr = globalPtr->nextPtr) {
sl@0
  1037
	    count++;
sl@0
  1038
	    if (globalPtr->refCount < 1) {
sl@0
  1039
		bytes = Tcl_GetStringFromObj(globalPtr->objPtr, &length);
sl@0
  1040
		panic("TclVerifyGlobalLiteralTable: global literal \"%.*s\" had bad refCount %d",
sl@0
  1041
		        (length>60? 60 : length), bytes,
sl@0
  1042
		        globalPtr->refCount);
sl@0
  1043
	    }
sl@0
  1044
	    if (globalPtr->objPtr->bytes == NULL) {
sl@0
  1045
		panic("TclVerifyGlobalLiteralTable: literal has NULL string rep");
sl@0
  1046
	    }
sl@0
  1047
	}
sl@0
  1048
    }
sl@0
  1049
    if (count != globalTablePtr->numEntries) {
sl@0
  1050
	panic("TclVerifyGlobalLiteralTable: global literal table had %d entries, should be %d",
sl@0
  1051
	      count, globalTablePtr->numEntries);
sl@0
  1052
    }
sl@0
  1053
}
sl@0
  1054
#endif /*TCL_COMPILE_DEBUG*/