diff options
Diffstat (limited to 'generic/tclListObj.c')
| -rw-r--r-- | generic/tclListObj.c | 861 | 
1 files changed, 473 insertions, 388 deletions
| diff --git a/generic/tclListObj.c b/generic/tclListObj.c index b5065ec..bd2dbc4 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -9,8 +9,6 @@   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclListObj.c,v 1.49.2.4 2010/05/19 22:04:48 ferrieux Exp $   */  #include "tclInt.h" @@ -19,7 +17,9 @@   * Prototypes for functions defined later in this file:   */ -static List *		NewListIntRep(int objc, Tcl_Obj *CONST objv[]); +static List *		AttemptNewList(Tcl_Interp *interp, int objc, +			    Tcl_Obj *const objv[]); +static List *		NewListIntRep(int objc, Tcl_Obj *const objv[], int p);  static void		DupListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);  static void		FreeListInternalRep(Tcl_Obj *listPtr);  static int		SetListFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); @@ -38,29 +38,33 @@ static void		UpdateStringOfList(Tcl_Obj *listPtr);   * storage to avoid an auxiliary stack.   */ -Tcl_ObjType tclListType = { +const Tcl_ObjType tclListType = {      "list",			/* name */      FreeListInternalRep,	/* freeIntRepProc */      DupListInternalRep,		/* dupIntRepProc */      UpdateStringOfList,		/* updateStringProc */      SetListFromAny		/* setFromAnyProc */  }; + +#ifndef TCL_MIN_ELEMENT_GROWTH +#define TCL_MIN_ELEMENT_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Obj *) +#endif  /*   *----------------------------------------------------------------------   *   * NewListIntRep --   * - *	If objc>0 and objv!=NULL, this function creates a list internal rep - *	with objc elements given in the array objv. If objc>0 and objv==NULL - *	it creates the list internal rep of a list with 0 elements, where - *	enough space has been preallocated to store objc elements. If objc<=0, - *	it returns NULL. + *	Creates a list internal rep with space for objc elements.  objc + *	must be > 0.  If objv!=NULL, initializes with the first objc values + *	in that array.  If objv==NULL, initalize list internal rep to have + *	0 elements, with space to add objc more.  Flag value "p" indicates + *	how to behave on failure.   *   * Results: - *	A new List struct is returned. If objc<=0 or if the allocation fails - *	for lack of memory, NULL is returned. The list returned has refCount - *	0. + *	A new List struct with refCount 0 is returned. If some failure + *	prevents this then if p=0, NULL is returned and otherwise the + *	routine panics.   *   * Side effects:   *	The ref counts of the elements in objv are incremented since the @@ -72,12 +76,13 @@ Tcl_ObjType tclListType = {  static List *  NewListIntRep(      int objc, -    Tcl_Obj *CONST objv[]) +    Tcl_Obj *const objv[], +    int p)  {      List *listRepPtr;      if (objc <= 0) { -	return NULL; +	Tcl_Panic("NewListIntRep: expects postive element count");      }      /* @@ -87,13 +92,20 @@ NewListIntRep(       * requires API changes to fix. See [Bug 219196] for a discussion.       */ -    if ((size_t)objc > INT_MAX/sizeof(Tcl_Obj *)) { +    if ((size_t)objc > LIST_MAX) { +	if (p) { +	    Tcl_Panic("max length of a Tcl list (%d elements) exceeded", +		    LIST_MAX); +	}  	return NULL;      } -    listRepPtr = (List *) -	    attemptckalloc(sizeof(List) + ((objc-1) * sizeof(Tcl_Obj *))); +    listRepPtr = attemptckalloc(LIST_SIZE(objc));      if (listRepPtr == NULL) { +	if (p) { +	    Tcl_Panic("list creation failed: unable to alloc %u bytes", +		    LIST_SIZE(objc)); +	}  	return NULL;      } @@ -120,6 +132,51 @@ NewListIntRep(  /*   *----------------------------------------------------------------------   * + * AttemptNewList -- + * + *	Creates a list internal rep with space for objc elements.  objc + *	must be > 0.  If objv!=NULL, initializes with the first objc values + *	in that array.  If objv==NULL, initalize list internal rep to have + *	0 elements, with space to add objc more.   + * + * Results: + *	A new List struct with refCount 0 is returned. If some failure + *	prevents this then NULL is returned, and an error message is left + *	in the interp result, unless interp is NULL. + * + * Side effects: + *	The ref counts of the elements in objv are incremented since the + *	resulting list now refers to them. + * + *---------------------------------------------------------------------- + */ + +static List * +AttemptNewList( +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    List *listRepPtr = NewListIntRep(objc, objv, 0); + +    if (interp != NULL && listRepPtr == NULL) { +	if (objc > LIST_MAX) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "max length of a Tcl list (%d elements) exceeded", +		    LIST_MAX)); +	} else { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "list creation failed: unable to alloc %u bytes", +		    LIST_SIZE(objc))); +	} +	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); +    } +    return listRepPtr; +} + +/* + *---------------------------------------------------------------------- + *   * Tcl_NewListObj --   *   *	This function is normally called when not debugging: i.e., when @@ -149,7 +206,7 @@ NewListIntRep(  Tcl_Obj *  Tcl_NewListObj(      int objc,			/* Count of objects referenced by objv. */ -    Tcl_Obj *CONST objv[])	/* An array of pointers to Tcl objects. */ +    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */  {      return Tcl_DbNewListObj(objc, objv, "unknown", 0);  } @@ -159,7 +216,7 @@ Tcl_NewListObj(  Tcl_Obj *  Tcl_NewListObj(      int objc,			/* Count of objects referenced by objv. */ -    Tcl_Obj *CONST objv[])	/* An array of pointers to Tcl objects. */ +    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */  {      List *listRepPtr;      Tcl_Obj *listPtr; @@ -174,21 +231,14 @@ Tcl_NewListObj(       * Create the internal rep.       */ -    listRepPtr = NewListIntRep(objc, objv); -    if (!listRepPtr) { -	Tcl_Panic("Not enough memory to allocate list"); -    } +    listRepPtr = NewListIntRep(objc, objv, 1);      /*       * Now create the object.       */ -    Tcl_InvalidateStringRep(listPtr); -    listPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -    listPtr->internalRep.twoPtrValue.ptr2 = NULL; -    listPtr->typePtr = &tclListType; -    listRepPtr->refCount++; - +    TclInvalidateStringRep(listPtr); +    ListSetIntRep(listPtr, listRepPtr);      return listPtr;  }  #endif /* if TCL_MEM_DEBUG */ @@ -227,8 +277,8 @@ Tcl_NewListObj(  Tcl_Obj *  Tcl_DbNewListObj(      int objc,			/* Count of objects referenced by objv. */ -    Tcl_Obj *CONST objv[],	/* An array of pointers to Tcl objects. */ -    CONST char *file,		/* The name of the source file calling this +    Tcl_Obj *const objv[],	/* An array of pointers to Tcl objects. */ +    const char *file,		/* The name of the source file calling this  				 * function; used for debugging. */      int line)			/* Line number in the source file; used for  				 * debugging. */ @@ -246,20 +296,14 @@ Tcl_DbNewListObj(       * Create the internal rep.       */ -    listRepPtr = NewListIntRep(objc, objv); -    if (!listRepPtr) { -	Tcl_Panic("Not enough memory to allocate list"); -    } +    listRepPtr = NewListIntRep(objc, objv, 1);      /*       * Now create the object.       */ -    Tcl_InvalidateStringRep(listPtr); -    listPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -    listPtr->internalRep.twoPtrValue.ptr2 = NULL; -    listPtr->typePtr = &tclListType; -    listRepPtr->refCount++; +    TclInvalidateStringRep(listPtr); +    ListSetIntRep(listPtr, listRepPtr);      return listPtr;  } @@ -269,8 +313,8 @@ Tcl_DbNewListObj(  Tcl_Obj *  Tcl_DbNewListObj(      int objc,			/* Count of objects referenced by objv. */ -    Tcl_Obj *CONST objv[],	/* An array of pointers to Tcl objects. */ -    CONST char *file,		/* The name of the source file calling this +    Tcl_Obj *const objv[],	/* An array of pointers to Tcl objects. */ +    const char *file,		/* The name of the source file calling this  				 * function; used for debugging. */      int line)			/* Line number in the source file; used for  				 * debugging. */ @@ -305,7 +349,7 @@ void  Tcl_SetListObj(      Tcl_Obj *objPtr,		/* Object whose internal rep to init. */      int objc,			/* Count of objects referenced by objv. */ -    Tcl_Obj *CONST objv[])	/* An array of pointers to Tcl objects. */ +    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */  {      List *listRepPtr; @@ -318,8 +362,7 @@ Tcl_SetListObj(       */      TclFreeIntRep(objPtr); -    objPtr->typePtr = NULL; -    Tcl_InvalidateStringRep(objPtr); +    TclInvalidateStringRep(objPtr);      /*       * Set the object's type to "list" and initialize the internal rep. @@ -328,14 +371,8 @@ Tcl_SetListObj(       */      if (objc > 0) { -	listRepPtr = NewListIntRep(objc, objv); -	if (!listRepPtr) { -	    Tcl_Panic("Cannot allocate enough memory for Tcl_SetListObj"); -	} -	objPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -	objPtr->internalRep.twoPtrValue.ptr2 = NULL; -	objPtr->typePtr = &tclListType; -	listRepPtr->refCount++; +	listRepPtr = NewListIntRep(objc, objv, 1); +	ListSetIntRep(objPtr, listRepPtr);      } else {  	objPtr->bytes = tclEmptyStringRep;  	objPtr->length = 0; @@ -426,30 +463,19 @@ Tcl_ListObjGetElements(      register List *listRepPtr;      if (listPtr->typePtr != &tclListType) { -	int result, length; +	int result; -	/* -	 * Don't get the string version of a dictionary; that transformation -	 * is not lossy, but is expensive. -	 */ - -	if (listPtr->typePtr == &tclDictType) { -	    (void) Tcl_DictObjSize(NULL, listPtr, &length); -	} else { -	    (void) TclGetStringFromObj(listPtr, &length); -	} -	if (!length) { +	if (listPtr->bytes == tclEmptyStringRep) {  	    *objcPtr = 0;  	    *objvPtr = NULL;  	    return TCL_OK;  	} -  	result = SetListFromAny(interp, listPtr);  	if (result != TCL_OK) {  	    return result;  	}      } -    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    listRepPtr = ListRepPtr(listPtr);      *objcPtr = listRepPtr->elemCount;      *objvPtr = &listRepPtr->elements;      return TCL_OK; @@ -460,16 +486,13 @@ Tcl_ListObjGetElements(   *   * Tcl_ListObjAppendList --   * - *	This function appends the objects in the list referenced by - *	elemListPtr to the list object referenced by listPtr. If listPtr is - *	not already a list object, an attempt will be made to convert it to - *	one. + *	This function appends the elements in the list value referenced by + *	elemListPtr to the list value referenced by listPtr.   *   * Results:   *	The return value is normally TCL_OK. If listPtr or elemListPtr do not - *	refer to list objects and they can not be converted to one, TCL_ERROR - *	is returned and an error message is left in the interpreter's result - *	if interp is not NULL. + *	refer to list values, TCL_ERROR is returned and an error message is + *	left in the interpreter's result if interp is not NULL.   *   * Side effects:   *	The reference counts of the elements in elemListPtr are incremented @@ -487,29 +510,27 @@ Tcl_ListObjAppendList(      register Tcl_Obj *listPtr,	/* List object to append elements to. */      Tcl_Obj *elemListPtr)	/* List obj with elements to append. */  { -    int listLen, objc, result; +    int objc;      Tcl_Obj **objv;      if (Tcl_IsShared(listPtr)) {  	Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendList");      } -    result = TclListObjLength(interp, listPtr, &listLen); -    if (result != TCL_OK) { -	return result; -    } +    /* +     * Pull the elements to append from elemListPtr. +     */ -    result = TclListObjGetElements(interp, elemListPtr, &objc, &objv); -    if (result != TCL_OK) { -	return result; +    if (TCL_OK != TclListObjGetElements(interp, elemListPtr, &objc, &objv)) { +	return TCL_ERROR;      }      /* -     * Insert objc new elements starting after the lists's last element. +     * Insert the new elements starting after the lists's last element.       * Delete zero existing elements.       */ -    return Tcl_ListObjReplace(interp, listPtr, listLen, 0, objc, objv); +    return Tcl_ListObjReplace(interp, listPtr, LIST_MAX, 0, objc, objv);  }  /* @@ -545,77 +566,129 @@ Tcl_ListObjAppendElement(      Tcl_Obj *listPtr,		/* List object to append objPtr to. */      Tcl_Obj *objPtr)		/* Object to append to listPtr's list. */  { -    register List *listRepPtr; -    register Tcl_Obj **elemPtrs; -    int numElems, numRequired, newMax, newSize, i; +    register List *listRepPtr, *newPtr = NULL; +    int numElems, numRequired, needGrow, isShared, attempt;      if (Tcl_IsShared(listPtr)) {  	Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendElement");      }      if (listPtr->typePtr != &tclListType) { -	int result, length; +	int result; -	(void) TclGetStringFromObj(listPtr, &length); -	if (!length) { +	if (listPtr->bytes == tclEmptyStringRep) {  	    Tcl_SetListObj(listPtr, 1, &objPtr);  	    return TCL_OK;  	} -  	result = SetListFromAny(interp, listPtr);  	if (result != TCL_OK) {  	    return result;  	}      } -    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    listRepPtr = ListRepPtr(listPtr);      numElems = listRepPtr->elemCount;      numRequired = numElems + 1 ; +    needGrow = (numRequired > listRepPtr->maxElemCount); +    isShared = (listRepPtr->refCount > 1); -    /* -     * If there is no room in the current array of element pointers, allocate -     * a new, larger array and copy the pointers to it. If the List struct is -     * shared, allocate a new one. -     */ +    if (numRequired > LIST_MAX) { +	if (interp != NULL) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "max length of a Tcl list (%d elements) exceeded", +		    LIST_MAX)); +	    Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); +	} +	return TCL_ERROR; +    } -    if (numRequired > listRepPtr->maxElemCount){ -	newMax = 2 * numRequired; -	newSize = sizeof(List) + ((newMax-1) * sizeof(Tcl_Obj *)); -    } else { -	newMax = listRepPtr->maxElemCount; -	newSize = 0; +    if (needGrow && !isShared) { +	/* +	 * Need to grow + unshared intrep => try to realloc +	 */ + +	attempt = 2 * numRequired; +	if (attempt <= LIST_MAX) { +	    newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); +	} +	if (newPtr == NULL) { +	    attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; +	    if (attempt > LIST_MAX) { +		attempt = LIST_MAX; +	    } +	    newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); +	} +	if (newPtr == NULL) { +	    attempt = numRequired; +	    newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); +	} +	if (newPtr) { +	    listRepPtr = newPtr; +	    listRepPtr->maxElemCount = attempt; +	    needGrow = 0; +	}      } +    if (isShared || needGrow) { +	Tcl_Obj **dst, **src = &listRepPtr->elements; -    if (listRepPtr->refCount > 1) { -	List *oldListRepPtr = listRepPtr; -	Tcl_Obj **oldElems; +	/* +	 * Either we have a shared intrep and we must copy to write, or we +	 * need to grow and realloc attempts failed.  Attempt intrep copy. +	 */ -	listRepPtr = NewListIntRep(newMax, NULL); -	if (!listRepPtr) { -	    Tcl_Panic("Not enough memory to allocate list"); +	attempt = 2 * numRequired; +	newPtr = AttemptNewList(NULL, attempt, NULL); +	if (newPtr == NULL) { +	    attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; +	    if (attempt > LIST_MAX) { +		attempt = LIST_MAX; +	    } +	    newPtr = AttemptNewList(NULL, attempt, NULL);  	} -	oldElems = &oldListRepPtr->elements; -	elemPtrs = &listRepPtr->elements; -	for (i=0; i<numElems; i++) { -	    elemPtrs[i] = oldElems[i]; -	    Tcl_IncrRefCount(elemPtrs[i]); +	if (newPtr == NULL) { +	    attempt = numRequired; +	    newPtr = AttemptNewList(interp, attempt, NULL);  	} -	listRepPtr->elemCount = numElems; -	listRepPtr->refCount++; -	oldListRepPtr->refCount--; -	listPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -    } else if (newSize) { -	listRepPtr = (List *) ckrealloc((char *)listRepPtr, (size_t)newSize); -	listRepPtr->maxElemCount = newMax; -	listPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; +	if (newPtr == NULL) { +	    /* +	     * All growth attempts failed; throw the error. +	     */ + +	    return TCL_ERROR; +	} + +	dst = &newPtr->elements; +	newPtr->refCount++; +	newPtr->canonicalFlag = listRepPtr->canonicalFlag; +	newPtr->elemCount = listRepPtr->elemCount; + +	if (isShared) { +	    /* +	     * The original intrep must remain undisturbed.  Copy into the new +	     * one and bump refcounts +	     */ +	    while (numElems--) { +		*dst = *src++; +		Tcl_IncrRefCount(*dst++); +	    } +	    listRepPtr->refCount--; +	} else { +	    /* +	     * Old intrep to be freed, re-use refCounts. +	     */ + +	    memcpy(dst, src, (size_t) numElems * sizeof(Tcl_Obj *)); +	    ckfree(listRepPtr); +	} +	listRepPtr = newPtr;      } +    listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr;      /*       * Add objPtr to the end of listPtr's array of element pointers. Increment       * the ref count for the (now shared) objPtr.       */ -    elemPtrs = &listRepPtr->elements; -    elemPtrs[numElems] = objPtr; +    *(&listRepPtr->elements + listRepPtr->elemCount) = objPtr;      Tcl_IncrRefCount(objPtr);      listRepPtr->elemCount++; @@ -624,7 +697,7 @@ Tcl_ListObjAppendElement(       * representation has changed.       */ -    Tcl_InvalidateStringRep(listPtr); +    TclInvalidateStringRep(listPtr);      return TCL_OK;  } @@ -664,21 +737,19 @@ Tcl_ListObjIndex(      register List *listRepPtr;      if (listPtr->typePtr != &tclListType) { -	int result, length; +	int result; -	(void) TclGetStringFromObj(listPtr, &length); -	if (!length) { +	if (listPtr->bytes == tclEmptyStringRep) {  	    *objPtrPtr = NULL;  	    return TCL_OK;  	} -  	result = SetListFromAny(interp, listPtr);  	if (result != TCL_OK) {  	    return result;  	}      } -    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    listRepPtr = ListRepPtr(listPtr);      if ((index < 0) || (index >= listRepPtr->elemCount)) {  	*objPtrPtr = NULL;      } else { @@ -719,21 +790,19 @@ Tcl_ListObjLength(      register List *listRepPtr;      if (listPtr->typePtr != &tclListType) { -	int result, length; +	int result; -	(void) TclGetStringFromObj(listPtr, &length); -	if (!length) { +	if (listPtr->bytes == tclEmptyStringRep) {  	    *intPtr = 0;  	    return TCL_OK;  	} -  	result = SetListFromAny(interp, listPtr);  	if (result != TCL_OK) {  	    return result;  	}      } -    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    listRepPtr = ListRepPtr(listPtr);      *intPtr = listRepPtr->elemCount;      return TCL_OK;  } @@ -783,7 +852,7 @@ Tcl_ListObjReplace(      int first,			/* Index of first element to replace. */      int count,			/* Number of elements to replace. */      int objc,			/* Number of objects to insert. */ -    Tcl_Obj *CONST objv[])	/* An array of objc pointers to Tcl objects to +    Tcl_Obj *const objv[])	/* An array of objc pointers to Tcl objects to  				 * insert. */  {      List *listRepPtr; @@ -794,15 +863,11 @@ Tcl_ListObjReplace(  	Tcl_Panic("%s called with shared object", "Tcl_ListObjReplace");      }      if (listPtr->typePtr != &tclListType) { -	int length; - -	(void) TclGetStringFromObj(listPtr, &length); -	if (!length) { -	    if (objc) { -		Tcl_SetListObj(listPtr, objc, NULL); -	    } else { +	if (listPtr->bytes == tclEmptyStringRep) { +	    if (!objc) {  		return TCL_OK;  	    } +	    Tcl_SetListObj(listPtr, objc, NULL);  	} else {  	    int result = SetListFromAny(interp, listPtr); @@ -820,7 +885,7 @@ Tcl_ListObjReplace(       * Resist any temptation to optimize this case.       */ -    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    listRepPtr = ListRepPtr(listPtr);      elemPtrs = &listRepPtr->elements;      numElems = listRepPtr->elemCount; @@ -835,14 +900,19 @@ Tcl_ListObjReplace(      } else if (numElems < first+count || first+count < 0) {  	/*  	 * The 'first+count < 0' condition here guards agains integer -	 * overflow in determining 'first+count' +	 * overflow in determining 'first+count'.  	 */ +  	count = numElems - first;      }      isShared = (listRepPtr->refCount > 1);      numRequired = numElems - count + objc; +    for (i = 0;  i < objc;  i++) { +	Tcl_IncrRefCount(objv[i]); +    } +      if ((numRequired <= listRepPtr->maxElemCount) && !isShared) {  	int shift; @@ -886,12 +956,31 @@ Tcl_ListObjReplace(  	    newMax = listRepPtr->maxElemCount;  	} -	listRepPtr = NewListIntRep(newMax, NULL); -	if (!listRepPtr) { -	    Tcl_Panic("Not enough memory to allocate list"); +	listRepPtr = AttemptNewList(NULL, newMax, NULL); +	if (listRepPtr == NULL) { +	    unsigned int limit = LIST_MAX - numRequired; +	    unsigned int extra = numRequired - numElems +		    + TCL_MIN_ELEMENT_GROWTH; +	    int growth = (int) ((extra > limit) ? limit : extra); + +	    listRepPtr = AttemptNewList(NULL, numRequired + growth, NULL); +	    if (listRepPtr == NULL) { +		listRepPtr = AttemptNewList(interp, numRequired, NULL); +		if (listRepPtr == NULL) { +		    for (i = 0;  i < objc;  i++) { +			/* See bug 3598580 */ +#if TCL_MAJOR_VERSION > 8 +			Tcl_DecrRefCount(objv[i]); +#else +			objv[i]->refCount--; +#endif +		    } +		    return TCL_ERROR; +		} +	    }  	} -	listPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; +	listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr;  	listRepPtr->refCount++;  	elemPtrs = &listRepPtr->elements; @@ -945,19 +1034,16 @@ Tcl_ListObjReplace(  			(size_t) numAfterLast * sizeof(Tcl_Obj *));  	    } -	    ckfree((char *) oldListRepPtr); +	    ckfree(oldListRepPtr);  	}      }      /* -     * Insert the new elements into elemPtrs before "first". We don't do a -     * memcpy here because we must increment the reference counts for the -     * added elements, so we must explicitly loop anyway. +     * Insert the new elements into elemPtrs before "first".       */      for (i=0,j=first ; i<objc ; i++,j++) {  	elemPtrs[j] = objv[i]; -	Tcl_IncrRefCount(objv[i]);      }      /* @@ -971,7 +1057,7 @@ Tcl_ListObjReplace(       * reflects the list's internal representation.       */ -    Tcl_InvalidateStringRep(listPtr); +    TclInvalidateStringRep(listPtr);      return TCL_OK;  } @@ -1008,8 +1094,6 @@ TclLindexList(  {      int index;			/* Index into the list. */ -    Tcl_Obj **indices;		/* Array of list indices. */ -    int indexCount;		/* Size of the array of list indices. */      Tcl_Obj *indexListCopy;      /* @@ -1049,8 +1133,19 @@ TclLindexList(  	return TclLindexFlat(interp, listPtr, 1, &argPtr);      } -    TclListObjGetElements(NULL, indexListCopy, &indexCount, &indices); -    listPtr = TclLindexFlat(interp, listPtr, indexCount, indices); +    if (indexListCopy->typePtr == &tclListType) { +	List *listRepPtr = ListRepPtr(indexListCopy); + +	listPtr = TclLindexFlat(interp, listPtr, listRepPtr->elemCount, +		&listRepPtr->elements); +    } else { +	int indexCount = -1;	/* Size of the array of list indices. */ +	Tcl_Obj **indices = NULL; +				/* Array of list indices. */ + +	Tcl_ListObjGetElements(NULL, indexListCopy, &indexCount, &indices); +	listPtr = TclLindexFlat(interp, listPtr, indexCount, indices); +    }      Tcl_DecrRefCount(indexListCopy);      return listPtr;  } @@ -1094,8 +1189,8 @@ TclLindexFlat(      Tcl_IncrRefCount(listPtr);      for (i=0 ; i<indexCount && listPtr ; i++) { -	int index, listLen; -	Tcl_Obj **elemPtrs, *sublistCopy; +	int index, listLen = 0; +	Tcl_Obj **elemPtrs = NULL, *sublistCopy;  	/*  	 * Here we make a private copy of the current sublist, so we avoid any @@ -1180,8 +1275,8 @@ TclLsetList(      Tcl_Obj *indexArgPtr,	/* Index or index-list arg to 'lset'. */      Tcl_Obj *valuePtr)		/* Value arg to 'lset'. */  { -    int indexCount;		/* Number of indices in the index list. */ -    Tcl_Obj **indices;		/* Vector of indices in the index list. */ +    int indexCount = 0;		/* Number of indices in the index list. */ +    Tcl_Obj **indices = NULL;	/* Vector of indices in the index list. */      Tcl_Obj *retValuePtr;	/* Pointer to the list to be returned. */      int index;			/* Current index in the list - discarded. */      Tcl_Obj *indexListCopy; @@ -1232,8 +1327,8 @@ TclLsetList(   *   * Results:   *	Returns the new value of the list variable, or NULL if an error - *	occurred. The returned object includes one reference count for - *	the pointer returned. + *	occurred. The returned object includes one reference count for the + *	pointer returned.   *   * Side effects:   *	On entry, the reference count of the variable value does not reflect @@ -1275,12 +1370,12 @@ TclLsetFlat(  				/* Index args. */      Tcl_Obj *valuePtr)		/* Value arg to 'lset'. */  { -    int index, result; +    int index, result, len;      Tcl_Obj *subListPtr, *retValuePtr, *chainPtr;      /* -     * If there are no indices, simply return the new value. -     * (Without indices, [lset] is a synonym for [set]. +     * If there are no indices, simply return the new value.  (Without +     * indices, [lset] is a synonym for [set].       */      if (indexCount == 0) { @@ -1289,14 +1384,14 @@ TclLsetFlat(      }      /* -     * If the list is shared, make a copy we can modify (copy-on-write). -     * We use Tcl_DuplicateObj() instead of TclListObjCopy() for a few -     * reasons: 1) we have not yet confirmed listPtr is actually a list; -     * 2) We make a verbatim copy of any existing string rep, and when -     * we combine that with the delayed invalidation of string reps of -     * modified Tcl_Obj's implemented below, the outcome is that any -     * error condition that causes this routine to return NULL, will -     * leave the string rep of listPtr and all elements to be unchanged. +     * If the list is shared, make a copy we can modify (copy-on-write).  We +     * use Tcl_DuplicateObj() instead of TclListObjCopy() for a few reasons: +     * 1) we have not yet confirmed listPtr is actually a list; 2) We make a +     * verbatim copy of any existing string rep, and when we combine that with +     * the delayed invalidation of string reps of modified Tcl_Obj's +     * implemented below, the outcome is that any error condition that causes +     * this routine to return NULL, will leave the string rep of listPtr and +     * all elements to be unchanged.       */      subListPtr = Tcl_IsShared(listPtr) ? Tcl_DuplicateObj(listPtr) : listPtr; @@ -1308,21 +1403,25 @@ TclLsetFlat(      retValuePtr = subListPtr;      chainPtr = NULL; +    result = TCL_OK;      /* -     * Loop through all the index arguments, and for each one dive -     * into the appropriate sublist. +     * Loop through all the index arguments, and for each one dive into the +     * appropriate sublist.       */      do {  	int elemCount;  	Tcl_Obj *parentList, **elemPtrs; -	/* Check for the possible error conditions... */ -	result = TCL_ERROR; +	/* +	 * Check for the possible error conditions... +	 */ +  	if (TclListObjGetElements(interp, subListPtr, &elemCount, &elemPtrs)  		!= TCL_OK) {  	    /* ...the sublist we're indexing into isn't a list at all. */ +	    result = TCL_ERROR;  	    break;  	} @@ -1330,33 +1429,42 @@ TclLsetFlat(  	 * WARNING: the macro TclGetIntForIndexM is not safe for  	 * post-increments, avoid '*indexArray++' here.  	 */ -	 +  	if (TclGetIntForIndexM(interp, *indexArray, elemCount - 1, &index)  		!= TCL_OK)  {  	    /* ...the index we're trying to use isn't an index at all. */ +	    result = TCL_ERROR;  	    indexArray++;  	    break;  	}  	indexArray++; -	if (index < 0 || index >= elemCount) { +	if (index < 0 || index > elemCount) {  	    /* ...the index points outside the sublist. */ -	    Tcl_SetObjResult(interp, -		    Tcl_NewStringObj("list index out of range", -1)); +	    if (interp != NULL) { +		Tcl_SetObjResult(interp, +			Tcl_NewStringObj("list index out of range", -1)); +		Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", +			"BADINDEX", NULL); +	    } +	    result = TCL_ERROR;  	    break;  	}  	/* -	 * No error conditions.  As long as we're not yet on the last -	 * index, determine the next sublist for the next pass through -	 * the loop, and take steps to make sure it is an unshared copy, -	 * as we intend to modify it. +	 * No error conditions.  As long as we're not yet on the last index, +	 * determine the next sublist for the next pass through the loop, and +	 * take steps to make sure it is an unshared copy, as we intend to +	 * modify it.  	 */ -	result = TCL_OK;  	if (--indexCount) {  	    parentList = subListPtr; -	    subListPtr = elemPtrs[index]; +	    if (index == elemCount) { +		subListPtr = Tcl_NewObj(); +	    } else { +		subListPtr = elemPtrs[index]; +	    }  	    if (Tcl_IsShared(subListPtr)) {  		subListPtr = Tcl_DuplicateObj(subListPtr);  	    } @@ -1366,74 +1474,92 @@ TclLsetFlat(  	     * we know to be unshared.  This call will also deal with the  	     * situation where parentList shares its intrep with other  	     * Tcl_Obj's.  Dealing with the shared intrep case can cause -	     * subListPtr to become shared again, so detect that case and -	     * make and store another copy. +	     * subListPtr to become shared again, so detect that case and make +	     * and store another copy.  	     */ -	    TclListObjSetElement(NULL, parentList, index, subListPtr); +	    if (index == elemCount) { +		Tcl_ListObjAppendElement(NULL, parentList, subListPtr); +	    } else { +		TclListObjSetElement(NULL, parentList, index, subListPtr); +	    }  	    if (Tcl_IsShared(subListPtr)) {  		subListPtr = Tcl_DuplicateObj(subListPtr);  		TclListObjSetElement(NULL, parentList, index, subListPtr);  	    }  	    /* -	     * The TclListObjSetElement() calls do not spoil the string -	     * rep of parentList, and that's fine for now, since all we've -	     * done so far is replace a list element with an unshared copy. -	     * The list value remains the same, so the string rep. is still -	     * valid, and unchanged, which is good because if this whole -	     * routine returns NULL, we'd like to leave no change to the -	     * value of the lset variable.  Later on, when we set valuePtr -	     * in its proper place, then all containing lists will have -	     * their values changed, and will need their string reps spoiled. -	     * We maintain a list of all those Tcl_Obj's (via a little intrep -	     * surgery) so we can spoil them at that time. +	     * The TclListObjSetElement() calls do not spoil the string rep of +	     * parentList, and that's fine for now, since all we've done so +	     * far is replace a list element with an unshared copy.  The list +	     * value remains the same, so the string rep. is still valid, and +	     * unchanged, which is good because if this whole routine returns +	     * NULL, we'd like to leave no change to the value of the lset +	     * variable.  Later on, when we set valuePtr in its proper place, +	     * then all containing lists will have their values changed, and +	     * will need their string reps spoiled.  We maintain a list of all +	     * those Tcl_Obj's (via a little intrep surgery) so we can spoil +	     * them at that time.  	     */ -	    parentList->internalRep.twoPtrValue.ptr2 = (void *) chainPtr; +	    parentList->internalRep.twoPtrValue.ptr2 = chainPtr;  	    chainPtr = parentList;  	}      } while (indexCount > 0);      /* -     * Either we've detected and error condition, and exited the loop -     * with result == TCL_ERROR, or we've successfully reached the last -     * index, and we're ready to store valuePtr.  In either case, we -     * need to clean up our string spoiling list of Tcl_Obj's. +     * Either we've detected and error condition, and exited the loop with +     * result == TCL_ERROR, or we've successfully reached the last index, and +     * we're ready to store valuePtr.  In either case, we need to clean up our +     * string spoiling list of Tcl_Obj's.       */      while (chainPtr) {  	Tcl_Obj *objPtr = chainPtr;  	if (result == TCL_OK) { -  	    /* -	     * We're going to store valuePtr, so spoil string reps -	     * of all containing lists. +	     * We're going to store valuePtr, so spoil string reps of all +	     * containing lists.  	     */ -	    Tcl_InvalidateStringRep(objPtr); +	    TclInvalidateStringRep(objPtr);  	} -	/* Clear away our intrep surgery mess */ -	chainPtr = (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr2; +	/* +	 * Clear away our intrep surgery mess. +	 */ + +	chainPtr = objPtr->internalRep.twoPtrValue.ptr2;  	objPtr->internalRep.twoPtrValue.ptr2 = NULL;      }      if (result != TCL_OK) { -	/*  -	 * Error return; message is already in interp. Clean up -	 * any excess memory.  +	/* +	 * Error return; message is already in interp. Clean up any excess +	 * memory.  	 */ +  	if (retValuePtr != listPtr) {  	    Tcl_DecrRefCount(retValuePtr);  	}  	return NULL;      } -    /* Store valuePtr in proper sublist and return */ -    TclListObjSetElement(NULL, subListPtr, index, valuePtr); -    Tcl_InvalidateStringRep(subListPtr); +    /* +     * Store valuePtr in proper sublist and return. The -1 is to avoid a +     * compiler warning (not a problem because we checked that we have a +     * proper list - or something convertible to one - above). +     */ + +    len = -1; +    TclListObjLength(NULL, subListPtr, &len); +    if (index == len) { +	Tcl_ListObjAppendElement(NULL, subListPtr, valuePtr); +    } else { +	TclListObjSetElement(NULL, subListPtr, index, valuePtr); +    } +    TclInvalidateStringRep(subListPtr);      Tcl_IncrRefCount(retValuePtr);      return retValuePtr;  } @@ -1490,12 +1616,15 @@ TclListObjSetElement(  	Tcl_Panic("%s called with shared object", "TclListObjSetElement");      }      if (listPtr->typePtr != &tclListType) { -	int length, result; - -	(void) TclGetStringFromObj(listPtr, &length); -	if (!length) { -	    Tcl_SetObjResult(interp, -		    Tcl_NewStringObj("list index out of range", -1)); +	int result; + +	if (listPtr->bytes == tclEmptyStringRep) { +	    if (interp != NULL) { +		Tcl_SetObjResult(interp, +			Tcl_NewStringObj("list index out of range", -1)); +		Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", +			"BADINDEX", NULL); +	    }  	    return TCL_ERROR;  	}  	result = SetListFromAny(interp, listPtr); @@ -1504,9 +1633,8 @@ TclListObjSetElement(  	}      } -    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    listRepPtr = ListRepPtr(listPtr);      elemCount = listRepPtr->elemCount; -    elemPtrs = &listRepPtr->elements;      /*       * Ensure that the index is in bounds. @@ -1516,6 +1644,8 @@ TclListObjSetElement(  	if (interp != NULL) {  	    Tcl_SetObjResult(interp,  		    Tcl_NewStringObj("list index out of range", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", "BADINDEX", +		    NULL);  	}  	return TCL_ERROR;      } @@ -1525,25 +1655,30 @@ TclListObjSetElement(       */      if (listRepPtr->refCount > 1) { -	List *oldListRepPtr = listRepPtr; -	Tcl_Obj **oldElemPtrs = elemPtrs; -	int i; +	Tcl_Obj **dst, **src = &listRepPtr->elements; +	List *newPtr = AttemptNewList(NULL, listRepPtr->maxElemCount, NULL); -	listRepPtr = NewListIntRep(listRepPtr->maxElemCount, NULL); -	if (listRepPtr == NULL) { -	    Tcl_Panic("Not enough memory to allocate list"); +	if (newPtr == NULL) { +	    newPtr = AttemptNewList(interp, elemCount, NULL); +	    if (newPtr == NULL) { +		return TCL_ERROR; +	    }  	} -	listRepPtr->canonicalFlag = oldListRepPtr->canonicalFlag; -	elemPtrs = &listRepPtr->elements; -	for (i=0; i < elemCount; i++) { -	    elemPtrs[i] = oldElemPtrs[i]; -	    Tcl_IncrRefCount(elemPtrs[i]); +	newPtr->refCount++; +	newPtr->elemCount = elemCount; +	newPtr->canonicalFlag = listRepPtr->canonicalFlag; + +	dst = &newPtr->elements; +	while (elemCount--) { +	    *dst = *src++; +	    Tcl_IncrRefCount(*dst++);  	} -	listRepPtr->refCount++; -	listRepPtr->elemCount = elemCount; -	listPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -	oldListRepPtr->refCount--; + +	listRepPtr->refCount--; + +	listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr = newPtr;      } +    elemPtrs = &listRepPtr->elements;      /*       * Add a reference to the new list element. @@ -1589,22 +1724,19 @@ static void  FreeListInternalRep(      Tcl_Obj *listPtr)		/* List object with internal rep to free. */  { -    register List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; -    register Tcl_Obj **elemPtrs = &listRepPtr->elements; -    register Tcl_Obj *objPtr; -    int numElems = listRepPtr->elemCount; -    int i; +    List *listRepPtr = ListRepPtr(listPtr);      if (--listRepPtr->refCount <= 0) { +	Tcl_Obj **elemPtrs = &listRepPtr->elements; +	int i, numElems = listRepPtr->elemCount; +  	for (i = 0;  i < numElems;  i++) { -	    objPtr = elemPtrs[i]; -	    Tcl_DecrRefCount(objPtr); +	    Tcl_DecrRefCount(elemPtrs[i]);  	} -	ckfree((char *) listRepPtr); +	ckfree(listRepPtr);      } -    listPtr->internalRep.twoPtrValue.ptr1 = NULL; -    listPtr->internalRep.twoPtrValue.ptr2 = NULL; +    listPtr->typePtr = NULL;  }  /* @@ -1629,12 +1761,9 @@ DupListInternalRep(      Tcl_Obj *srcPtr,		/* Object with internal rep to copy. */      Tcl_Obj *copyPtr)		/* Object with internal rep to set. */  { -    List *listRepPtr = (List *) srcPtr->internalRep.twoPtrValue.ptr1; +    List *listRepPtr = ListRepPtr(srcPtr); -    listRepPtr->refCount++; -    copyPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -    copyPtr->internalRep.twoPtrValue.ptr2 = NULL; -    copyPtr->typePtr = &tclListType; +    ListSetIntRep(copyPtr, listRepPtr);  }  /* @@ -1661,14 +1790,8 @@ SetListFromAny(      Tcl_Interp *interp,		/* Used for error reporting if not NULL. */      Tcl_Obj *objPtr)		/* The object to convert. */  { -    char *string, *s; -    const char *elemStart, *nextElem; -    int lenRemain, length, estCount, elemSize, hasBrace, i, j, result; -    const char *limit;		/* Points just after string's last byte. */ -    register const char *p; -    register Tcl_Obj **elemPtrs; -    register Tcl_Obj *elemPtr;      List *listRepPtr; +    Tcl_Obj **elemPtrs;      /*       * Dictionaries are a special case; they have a string representation such @@ -1693,11 +1816,8 @@ SetListFromAny(  	 */  	Tcl_DictObjSize(NULL, objPtr, &size); -	listRepPtr = NewListIntRep(size > 0 ? 2*size : 1, NULL); +	listRepPtr = AttemptNewList(interp, size > 0 ? 2*size : 1, NULL);  	if (!listRepPtr) { -	    Tcl_SetResult(interp, -		    "insufficient memory to allocate list working space", -		    TCL_STATIC);  	    return TCL_ERROR;  	}  	listRepPtr->elemCount = 2 * size; @@ -1708,113 +1828,75 @@ SetListFromAny(  	elemPtrs = &listRepPtr->elements;  	Tcl_DictObjFirst(NULL, objPtr, &search, &keyPtr, &valuePtr, &done); -	i = 0;  	while (!done) { -	    elemPtrs[i++] = keyPtr; -	    elemPtrs[i++] = valuePtr; +	    *elemPtrs++ = keyPtr; +	    *elemPtrs++ = valuePtr;  	    Tcl_IncrRefCount(keyPtr);  	    Tcl_IncrRefCount(valuePtr);  	    Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);  	} +    } else { +	int estCount, length; +	const char *limit, *nextElem = TclGetStringFromObj(objPtr, &length);  	/* -	 * Swap the representations. +	 * Allocate enough space to hold a (Tcl_Obj *) for each +	 * (possible) list element.  	 */ -	goto commitRepresentation; -    } - -    /* -     * Get the string representation. Make it up-to-date if necessary. -     */ - -    string = TclGetStringFromObj(objPtr, &length); - -    /* -     * Parse the string into separate string objects, and create a List -     * structure that points to the element string objects. We use a modified -     * version of Tcl_SplitList's implementation to avoid one malloc and a -     * string copy for each list element. First, estimate the number of -     * elements by counting the number of space characters in the list. -     */ - -    limit = string + length; -    estCount = 1; -    for (p = string;  p < limit;  p++) { -	if (isspace(UCHAR(*p))) { /* INTL: ISO space. */ -	    estCount++; +	estCount = TclMaxListLength(nextElem, length, &limit); +	estCount += (estCount == 0);	/* Smallest list struct holds 1 +					 * element. */ +	listRepPtr = AttemptNewList(interp, estCount, NULL); +	if (listRepPtr == NULL) { +	    return TCL_ERROR;  	} -    } +	elemPtrs = &listRepPtr->elements; -    /* -     * Allocate a new List structure with enough room for "estCount" elements. -     * Each element is a pointer to a Tcl_Obj with the appropriate string rep. -     * The initial "estCount" elements are set using the corresponding "argv" -     * strings. -     */ +	/* +	 * Each iteration, parse and store a list element. +	 */ -    listRepPtr = NewListIntRep(estCount, NULL); -    if (!listRepPtr) { -	Tcl_SetObjResult(interp, Tcl_NewStringObj( -		"Not enough memory to allocate the list internal rep", -1)); -	return TCL_ERROR; -    } -    elemPtrs = &listRepPtr->elements; +	while (nextElem < limit) { +	    const char *elemStart; +	    int elemSize, literal; -    for (p=string, lenRemain=length, i=0; -	    lenRemain > 0; -	    p=nextElem, lenRemain=limit-nextElem, i++) { -	result = TclFindElement(interp, p, lenRemain, &elemStart, &nextElem, -		&elemSize, &hasBrace); -	if (result != TCL_OK) { -	    for (j = 0;  j < i;  j++) { -		elemPtr = elemPtrs[j]; -		Tcl_DecrRefCount(elemPtr); +	    if (TCL_OK != TclFindElement(interp, nextElem, limit - nextElem, +		    &elemStart, &nextElem, &elemSize, &literal)) { +		while (--elemPtrs >= &listRepPtr->elements) { +		    Tcl_DecrRefCount(*elemPtrs); +		} +		ckfree((char *) listRepPtr); +		return TCL_ERROR; +	    } +	    if (elemStart == limit) { +		break;  	    } -	    ckfree((char *) listRepPtr); -	    return result; -	} -	if (elemStart >= limit) { -	    break; -	} -	if (i > estCount) { -	    Tcl_Panic("SetListFromAny: bad size estimate for list"); -	} -	/* -	 * Allocate a Tcl object for the element and initialize it from the -	 * "elemSize" bytes starting at "elemStart". -	 */ +	    /* TODO: replace panic with error on alloc failure? */ +	    if (literal) { +		TclNewStringObj(*elemPtrs, elemStart, elemSize); +	    } else { +		TclNewObj(*elemPtrs); +		(*elemPtrs)->bytes = ckalloc((unsigned) elemSize + 1); +		(*elemPtrs)->length = TclCopyAndCollapse(elemSize, elemStart, +			(*elemPtrs)->bytes); +	    } -	s = ckalloc((unsigned) elemSize + 1); -	if (hasBrace) { -	    memcpy(s, elemStart, (size_t) elemSize); -	    s[elemSize] = 0; -	} else { -	    elemSize = TclCopyAndCollapse(elemSize, elemStart, s); +	    Tcl_IncrRefCount(*elemPtrs++);/* Since list now holds ref to it. */  	} -	TclNewObj(elemPtr); -	elemPtr->bytes = s; -	elemPtr->length = elemSize; -	elemPtrs[i] = elemPtr; -	Tcl_IncrRefCount(elemPtr);	/* Since list now holds ref to it. */ + 	listRepPtr->elemCount = elemPtrs - &listRepPtr->elements;      } -    listRepPtr->elemCount = i; -      /*       * Free the old internalRep before setting the new one. We do this as late       * as possible to allow the conversion code, in particular       * Tcl_GetStringFromObj, to use that old internalRep.       */ -  commitRepresentation: -    listRepPtr->refCount++;      TclFreeIntRep(objPtr); -    objPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; -    objPtr->internalRep.twoPtrValue.ptr2 = NULL; -    objPtr->typePtr = &tclListType; +    ListSetIntRep(objPtr, listRepPtr);      return TCL_OK;  } @@ -1844,19 +1926,32 @@ UpdateStringOfList(      Tcl_Obj *listPtr)		/* List object with string rep to update. */  {  #   define LOCAL_SIZE 20 -    int localFlags[LOCAL_SIZE], *flagPtr; -    List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1; +    int localFlags[LOCAL_SIZE], *flagPtr = NULL; +    List *listRepPtr = ListRepPtr(listPtr);      int numElems = listRepPtr->elemCount; -    register int i; -    char *elem, *dst; -    int length; +    int i, length, bytesNeeded = 0; +    const char *elem; +    char *dst;      Tcl_Obj **elemPtrs;      /* -     * Convert each element of the list to string form and then convert it to -     * proper list element form, adding it to the result buffer. +     * Mark the list as being canonical; although it will now have a string +     * rep, it is one we derived through proper "canonical" quoting and so +     * it's known to be free from nasties relating to [concat] and [eval].       */ +    listRepPtr->canonicalFlag = 1; + +    /* +     * Handle empty list case first, so rest of the routine is simpler. +     */ + +    if (numElems == 0) { +	listPtr->bytes = tclEmptyStringRep; +	listPtr->length = 0; +	return; +    } +      /*       * Pass 1: estimate space, gather flags.       */ @@ -1864,54 +1959,44 @@ UpdateStringOfList(      if (numElems <= LOCAL_SIZE) {  	flagPtr = localFlags;      } else { -	flagPtr = (int *) ckalloc((unsigned) numElems * sizeof(int)); +	/* +	 * We know numElems <= LIST_MAX, so this is safe. +	 */ + +	flagPtr = ckalloc(numElems * sizeof(int));      } -    listPtr->length = 1;      elemPtrs = &listRepPtr->elements;      for (i = 0; i < numElems; i++) { +	flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0);  	elem = TclGetStringFromObj(elemPtrs[i], &length); -	listPtr->length += Tcl_ScanCountedElement(elem, length, flagPtr+i)+1; - -	/* -	 * Check for continued sanity. [Bug 1267380] -	 */ - -	if (listPtr->length < 1) { -	    Tcl_Panic("string representation size exceeds sane bounds"); +	bytesNeeded += TclScanElement(elem, length, flagPtr+i); +	if (bytesNeeded < 0) { +	    Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);  	}      } +    if (bytesNeeded > INT_MAX - numElems + 1) { +	Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); +    } +    bytesNeeded += numElems;      /*       * Pass 2: copy into string rep buffer.       */ -    listPtr->bytes = ckalloc((unsigned) listPtr->length); +    listPtr->length = bytesNeeded - 1; +    listPtr->bytes = ckalloc(bytesNeeded);      dst = listPtr->bytes;      for (i = 0; i < numElems; i++) { +	flagPtr[i] |= (i ? TCL_DONT_QUOTE_HASH : 0);  	elem = TclGetStringFromObj(elemPtrs[i], &length); -	dst += Tcl_ConvertCountedElement(elem, length, dst, -		flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH)); -	*dst = ' '; -	dst++; +	dst += TclConvertElement(elem, length, dst, flagPtr[i]); +	*dst++ = ' ';      } +    listPtr->bytes[listPtr->length] = '\0'; +      if (flagPtr != localFlags) { -	ckfree((char *) flagPtr); +	ckfree(flagPtr);      } -    if (dst == listPtr->bytes) { -	*dst = 0; -    } else { -	dst--; -	*dst = 0; -    } -    listPtr->length = dst - listPtr->bytes; - -    /* -     * Mark the list as being canonical; although it has a string rep, it is -     * one we derived through proper "canonical" quoting and so it's known to -     * be free from nasties relating to [concat] and [eval]. -     */ - -    listRepPtr->canonicalFlag = 1;  }  /* | 
