summaryrefslogtreecommitdiffstats
path: root/generic/tclListObj.c
diff options
context:
space:
mode:
authorMiguel Sofer <miguel.sofer@gmail.com>2005-04-02 02:08:22 (GMT)
committerMiguel Sofer <miguel.sofer@gmail.com>2005-04-02 02:08:22 (GMT)
commit95b50e96cfeca13080aa95e5a4cd378cbea25955 (patch)
tree60e127a56dc4b46c2944f5cd3e2270be9489cdca /generic/tclListObj.c
parentfbb5749d9fa84503a3480ab6e24a9f0436772110 (diff)
downloadtcl-95b50e96cfeca13080aa95e5a4cd378cbea25955.zip
tcl-95b50e96cfeca13080aa95e5a4cd378cbea25955.tar.gz
tcl-95b50e96cfeca13080aa95e5a4cd378cbea25955.tar.bz2
Changed the internal representation of lists to (a) reduce the malloc/free
calls at list creation (from 2 to 1), (b) reduce the cost of handling empty lists (we now never create a list internal rep for them), (c) allow refcounting of the list internal rep. The latter permits insuring that the pointers returned by Tcl_ListObjGetElements remain valid even if the object shimmers away from its original list type. This is [Patch 1158008]
Diffstat (limited to 'generic/tclListObj.c')
-rw-r--r--generic/tclListObj.c871
1 files changed, 396 insertions, 475 deletions
diff --git a/generic/tclListObj.c b/generic/tclListObj.c
index 162101c..ddcb062 100644
--- a/generic/tclListObj.c
+++ b/generic/tclListObj.c
@@ -11,7 +11,7 @@
* 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.20 2004/11/11 01:17:51 das Exp $
+ * RCS: @(#) $Id: tclListObj.c,v 1.21 2005/04/02 02:08:59 msofer Exp $
*/
#include "tclInt.h"
@@ -20,6 +20,8 @@
* Prototypes for procedures defined later in this file:
*/
+static List* NewListIntRep _ANSI_ARGS_((int objc,
+ Tcl_Obj *CONST objv[]));
static void DupListInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
Tcl_Obj *copyPtr));
static void FreeListInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
@@ -46,8 +48,78 @@ Tcl_ObjType tclListType = {
FreeListInternalRep, /* freeIntRepProc */
DupListInternalRep, /* dupIntRepProc */
UpdateStringOfList, /* updateStringProc */
- SetListFromAny /* setFromAnyProc */
+ NULL /* setFromAnyProc */
};
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewListIntRep --
+ *
+ * If objc>0 and objv!=NULL, this procedure 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.
+ *
+ * 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.
+ *
+ * Side effects:
+ * The ref counts of the elements in objv are incremented since the
+ * resulting list now refers to them.
+ *
+ *----------------------------------------------------------------------
+ */
+
+List*
+NewListIntRep(objc, objv)
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ Tcl_Obj **elemPtrs;
+ List *listRepPtr;
+ int i;
+
+ if (objc <= 0) {
+ return NULL;
+ }
+
+ /* First check to see if we'd overflow and try to allocate an
+ * object larger than our memory allocator allows. Note that this
+ * is actually a fairly small value when you're on a serious
+ * 64-bit machine, but that requires API changes to fix.
+ */
+
+ if (objc > INT_MAX/sizeof(Tcl_Obj *)) {
+ return NULL;
+ }
+
+ listRepPtr = (List *) attemptckalloc(sizeof(List) +
+ ((objc-1) * sizeof(Tcl_Obj *)));
+ if (listRepPtr == NULL) {
+ return NULL;
+ }
+
+ listRepPtr->refCount = 0;
+ listRepPtr->maxElemCount = objc;
+
+ if (objv) {
+ listRepPtr->elemCount = objc;
+ elemPtrs = &listRepPtr->elements;
+ for (i = 0; i < objc; i++) {
+ elemPtrs[i] = objv[i];
+ Tcl_IncrRefCount(elemPtrs[i]);
+ }
+ } else {
+ listRepPtr->elemCount = 0;
+ }
+ return listRepPtr;
+}
/*
*----------------------------------------------------------------------
@@ -93,32 +165,34 @@ Tcl_NewListObj(objc, objv)
int objc; /* Count of objects referenced by objv. */
Tcl_Obj *CONST objv[]; /* An array of pointers to Tcl objects. */
{
- register Tcl_Obj *listPtr;
- register Tcl_Obj **elemPtrs;
- register List *listRepPtr;
- int i;
+ List *listRepPtr;
+ Tcl_Obj *listPtr;
TclNewObj(listPtr);
- if (objc > 0) {
- Tcl_InvalidateStringRep(listPtr);
+ if (objc <= 0) {
+ return listPtr;
+ }
+
+ /*
+ * Create the internal rep.
+ */
- elemPtrs = (Tcl_Obj **)
- ckalloc((unsigned) (objc * sizeof(Tcl_Obj *)));
- for (i = 0; i < objc; i++) {
- elemPtrs[i] = objv[i];
- Tcl_IncrRefCount(elemPtrs[i]);
- }
+ listRepPtr = NewListIntRep(objc, objv);
+ if (!listRepPtr) {
+ Tcl_Panic("Not enough memory to create the list\n");
+ }
- listRepPtr = (List *) ckalloc(sizeof(List));
- listRepPtr->maxElemCount = objc;
- listRepPtr->elemCount = objc;
- listRepPtr->elements = elemPtrs;
+ /*
+ * Now create the object.
+ */
+
+ Tcl_InvalidateStringRep(listPtr);
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ listPtr->typePtr = &tclListType;
+ listRepPtr->refCount++;
- listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
- listPtr->internalRep.twoPtrValue.ptr2 = NULL;
- listPtr->typePtr = &tclListType;
- }
return listPtr;
}
#endif /* if TCL_MEM_DEBUG */
@@ -163,32 +237,34 @@ Tcl_DbNewListObj(objc, objv, file, line)
int line; /* Line number in the source file; used
* for debugging. */
{
- register Tcl_Obj *listPtr;
- register Tcl_Obj **elemPtrs;
- register List *listRepPtr;
- int i;
+ Tcl_Obj *listPtr;
+ List *listRepPtr;
TclDbNewObj(listPtr, file, line);
- if (objc > 0) {
- Tcl_InvalidateStringRep(listPtr);
+ if (objc <= 0) {
+ return listPtr;
+ }
+
+ /*
+ * Create the internal rep.
+ */
- elemPtrs = (Tcl_Obj **)
- ckalloc((unsigned) (objc * sizeof(Tcl_Obj *)));
- for (i = 0; i < objc; i++) {
- elemPtrs[i] = objv[i];
- Tcl_IncrRefCount(elemPtrs[i]);
- }
+ listRepPtr = NewListIntRep(objc, objv);
+ if (!listRepPtr) {
+ Tcl_Panic("Not enough memory to create the list\n");
+ }
- listRepPtr = (List *) ckalloc(sizeof(List));
- listRepPtr->maxElemCount = objc;
- listRepPtr->elemCount = objc;
- listRepPtr->elements = elemPtrs;
+ /*
+ * Now create the object.
+ */
+
+ Tcl_InvalidateStringRep(listPtr);
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ listPtr->typePtr = &tclListType;
+ listRepPtr->refCount++;
- listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
- listPtr->internalRep.twoPtrValue.ptr2 = NULL;
- listPtr->typePtr = &tclListType;
- }
return listPtr;
}
@@ -210,120 +286,6 @@ Tcl_DbNewListObj(objc, objv, file, line)
/*
*----------------------------------------------------------------------
*
- * TclNewListObjDirect, TclDbNewListObjDirect --
- *
- * Version of Tcl_NewListOb/Tcl_DbNewListObj that does not copy
- * the array of Tcl_Objs. It still scans it though to update the
- * reference counts.
- *
- * Results:
- * A new list object is returned that is initialized from the object
- * pointers in objv. If objc is less than or equal to zero, an empty
- * object is returned (and "ownership" of the array of objects is
- * not transferred.) The new object's string representation is left
- * NULL. The resulting new list object has ref count 0.
- *
- * Side effects:
- * The ref counts of the elements in objv are incremented since the
- * resulting list now refers to them.
- *
- *----------------------------------------------------------------------
- */
-
-#ifdef TCL_MEM_DEBUG
-#undef TclNewListObjDirect
-Tcl_Obj *
-TclNewListObjDirect(objc, objv)
- int objc; /* Count of objects referenced by objv. */
- Tcl_Obj **objv; /* An array of pointers to Tcl objects. */
-{
- return TclDbNewListObjDirect(objc, objv, "unknown", 0);
-}
-#else /* !TCL_MEM_DEBUG */
-Tcl_Obj *
-TclNewListObjDirect(objc, objv)
- int objc; /* Count of objects referenced by objv. */
- Tcl_Obj **objv; /* An array of pointers to Tcl objects. */
-{
- register Tcl_Obj *listPtr;
-
- TclNewObj(listPtr);
-
- if (objc > 0) {
- register List *listRepPtr;
- int i;
-
- Tcl_InvalidateStringRep(listPtr);
-
- for (i=0 ; i<objc ; i++) {
- Tcl_IncrRefCount(objv[i]);
- }
-
- listRepPtr = (List *) ckalloc(sizeof(List));
- listRepPtr->maxElemCount = objc;
- listRepPtr->elemCount = objc;
- listRepPtr->elements = objv;
-
- listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
- listPtr->internalRep.twoPtrValue.ptr2 = NULL;
- listPtr->typePtr = &tclListType;
- }
- return listPtr;
-}
-#endif /* TCL_MEM_DEBUG */
-
-#ifdef TCL_MEM_DEBUG
-Tcl_Obj *
-TclDbNewListObjDirect(objc, objv, file, line)
- int objc; /* Count of objects referenced by objv. */
- Tcl_Obj **objv; /* An array of pointers to Tcl objects. */
- CONST char *file; /* The name of the source file calling this
- * procedure; used for debugging. */
- int line; /* Line number in the source file; used
- * for debugging. */
-{
- register Tcl_Obj *listPtr;
-
- TclDbNewObj(listPtr, file, line);
-
- if (objc > 0) {
- register List *listRepPtr;
- int i;
-
- Tcl_InvalidateStringRep(listPtr);
-
- for (i=0 ; i<objc ; i++) {
- Tcl_IncrRefCount(objv[i]);
- }
-
- listRepPtr = (List *) ckalloc(sizeof(List));
- listRepPtr->maxElemCount = objc;
- listRepPtr->elemCount = objc;
- listRepPtr->elements = objv;
-
- listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
- listPtr->internalRep.twoPtrValue.ptr2 = NULL;
- listPtr->typePtr = &tclListType;
- }
- return listPtr;
-}
-#else /* !TCL_MEM_DEBUG */
-Tcl_Obj *
-TclDbNewListObjDirect(objc, objv, file, line)
- int objc; /* Count of objects referenced by objv. */
- Tcl_Obj **objv; /* An array of pointers to Tcl objects. */
- CONST char *file; /* The name of the source file calling this
- * procedure; used for debugging. */
- int line; /* Line number in the source file; used
- * for debugging. */
-{
- return TclNewListObjDirect(objc, objv);
-}
-#endif /* TCL_MEM_DEBUG */
-
-/*
- *----------------------------------------------------------------------
- *
* Tcl_SetListObj --
*
* Modify an object to be a list containing each of the objc elements
@@ -349,9 +311,7 @@ Tcl_SetListObj(objPtr, objc, objv)
int objc; /* Count of objects referenced by objv. */
Tcl_Obj *CONST objv[]; /* An array of pointers to Tcl objects. */
{
- register Tcl_Obj **elemPtrs;
- register List *listRepPtr;
- int i;
+ List *listRepPtr;
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("Tcl_SetListObj called with shared object");
@@ -372,21 +332,14 @@ Tcl_SetListObj(objPtr, objc, objv)
*/
if (objc > 0) {
- elemPtrs = (Tcl_Obj **)
- ckalloc((unsigned) (objc * sizeof(Tcl_Obj *)));
- for (i = 0; i < objc; i++) {
- elemPtrs[i] = objv[i];
- Tcl_IncrRefCount(elemPtrs[i]);
+ listRepPtr = NewListIntRep(objc, objv);
+ if (!listRepPtr) {
+ Tcl_Panic("Cannot allocate enough memory for Tcl_SetListObj");
}
-
- listRepPtr = (List *) ckalloc(sizeof(List));
- listRepPtr->maxElemCount = objc;
- listRepPtr->elemCount = objc;
- listRepPtr->elements = elemPtrs;
-
objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
objPtr->internalRep.twoPtrValue.ptr2 = NULL;
objPtr->typePtr = &tclListType;
+ listRepPtr->refCount++;
} else {
objPtr->bytes = tclEmptyStringRep;
objPtr->length = 0;
@@ -436,14 +389,23 @@ Tcl_ListObjGetElements(interp, listPtr, objcPtr, objvPtr)
register List *listRepPtr;
if (listPtr->typePtr != &tclListType) {
- int result = SetListFromAny(interp, listPtr);
+ int result, length;
+
+ (void) Tcl_GetStringFromObj(listPtr, &length);
+ if (!length) {
+ *objcPtr = 0;
+ *objvPtr = NULL;
+ return TCL_OK;
+ }
+
+ result = SetListFromAny(interp, listPtr);
if (result != TCL_OK) {
return result;
}
}
listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
*objcPtr = listRepPtr->elemCount;
- *objvPtr = listRepPtr->elements;
+ *objvPtr = &listRepPtr->elements;
return TCL_OK;
}
@@ -479,21 +441,17 @@ Tcl_ListObjAppendList(interp, listPtr, elemListPtr)
register Tcl_Obj *listPtr; /* List object to append elements to. */
Tcl_Obj *elemListPtr; /* List obj with elements to append. */
{
- register List *listRepPtr;
int listLen, objc, result;
Tcl_Obj **objv;
if (Tcl_IsShared(listPtr)) {
Tcl_Panic("Tcl_ListObjAppendList called with shared object");
}
- if (listPtr->typePtr != &tclListType) {
- result = SetListFromAny(interp, listPtr);
- if (result != TCL_OK) {
- return result;
- }
+
+ result = Tcl_ListObjLength(interp, listPtr, &listLen);
+ if (result != TCL_OK) {
+ return result;
}
- listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
- listLen = listRepPtr->elemCount;
result = Tcl_ListObjGetElements(interp, elemListPtr, &objc, &objv);
if (result != TCL_OK) {
@@ -543,40 +501,66 @@ Tcl_ListObjAppendElement(interp, listPtr, objPtr)
{
register List *listRepPtr;
register Tcl_Obj **elemPtrs;
- int numElems, numRequired;
+ int numElems, numRequired, newMax, newSize, i;
if (Tcl_IsShared(listPtr)) {
Tcl_Panic("Tcl_ListObjAppendElement called with shared object");
}
if (listPtr->typePtr != &tclListType) {
- int result = SetListFromAny(interp, listPtr);
+ int result, length;
+
+ (void) Tcl_GetStringFromObj(listPtr, &length);
+ if (!length) {
+ Tcl_SetListObj(listPtr, 1, &objPtr);
+ return TCL_OK;
+ }
+
+ result = SetListFromAny(interp, listPtr);
if (result != TCL_OK) {
return result;
}
}
listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
- elemPtrs = listRepPtr->elements;
numElems = listRepPtr->elemCount;
numRequired = numElems + 1 ;
/*
* If there is no room in the current array of element pointers,
- * allocate a new, larger array and copy the pointers to it.
+ * allocate a new, larger array and copy the pointers to it. If the
+ * List struct is shared, allocate a new one.
*/
- if (numRequired > listRepPtr->maxElemCount) {
- int newMax = (2 * numRequired);
- Tcl_Obj **newElemPtrs = (Tcl_Obj **)
- ckalloc((unsigned) (newMax * sizeof(Tcl_Obj *)));
-
- memcpy((VOID *) newElemPtrs, (VOID *) elemPtrs,
- (size_t) (numElems * sizeof(Tcl_Obj *)));
+ if (numRequired > listRepPtr->maxElemCount){
+ newMax = (2 * numRequired);
+ newSize = sizeof(List)+((newMax-1)*sizeof(Tcl_Obj*));
+ } else {
+ newMax = listRepPtr->maxElemCount;
+ newSize = 0;
+ }
+ if (listRepPtr->refCount > 1) {
+ List *oldListRepPtr = listRepPtr;
+ Tcl_Obj **oldElems;
+
+ listRepPtr = NewListIntRep(newMax, NULL);
+ if (!listRepPtr) {
+ Tcl_Panic("Not enough memory to allocate list");
+ }
+ oldElems = &oldListRepPtr->elements;
+ elemPtrs = &listRepPtr->elements;
+ for (i=0; i<numElems; i++) {
+ elemPtrs[i] = oldElems[i];
+ Tcl_IncrRefCount(elemPtrs[i]);
+ }
+ listRepPtr->elemCount = numElems;
+ listRepPtr->refCount++;
+ oldListRepPtr->refCount--;
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ } else if (newSize) {
+ listRepPtr = (List *) ckrealloc((char *)listRepPtr, newSize);
listRepPtr->maxElemCount = newMax;
- listRepPtr->elements = newElemPtrs;
- ckfree((char *) elemPtrs);
- elemPtrs = newElemPtrs;
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
}
/*
@@ -584,6 +568,7 @@ Tcl_ListObjAppendElement(interp, listPtr, objPtr)
* pointers. Increment the ref count for the (now shared) objPtr.
*/
+ elemPtrs = &listRepPtr->elements;
elemPtrs[numElems] = objPtr;
Tcl_IncrRefCount(objPtr);
listRepPtr->elemCount++;
@@ -633,7 +618,14 @@ Tcl_ListObjIndex(interp, listPtr, index, objPtrPtr)
register List *listRepPtr;
if (listPtr->typePtr != &tclListType) {
- int result = SetListFromAny(interp, listPtr);
+ int result, length;
+
+ (void) Tcl_GetStringFromObj(listPtr, &length);
+ if (!length) {
+ return 0;
+ }
+
+ result = SetListFromAny(interp, listPtr);
if (result != TCL_OK) {
return result;
}
@@ -643,7 +635,7 @@ Tcl_ListObjIndex(interp, listPtr, index, objPtrPtr)
if ((index < 0) || (index >= listRepPtr->elemCount)) {
*objPtrPtr = NULL;
} else {
- *objPtrPtr = listRepPtr->elements[index];
+ *objPtrPtr = (&listRepPtr->elements)[index];
}
return TCL_OK;
@@ -680,7 +672,15 @@ Tcl_ListObjLength(interp, listPtr, intPtr)
register List *listRepPtr;
if (listPtr->typePtr != &tclListType) {
- int result = SetListFromAny(interp, listPtr);
+ int result, length;
+
+ (void) Tcl_GetStringFromObj(listPtr, &length);
+ if (!length) {
+ *intPtr = 0;
+ return TCL_OK;
+ }
+
+ result = SetListFromAny(interp, listPtr);
if (result != TCL_OK) {
return result;
}
@@ -740,22 +740,35 @@ Tcl_ListObjReplace(interp, listPtr, first, count, objc, objv)
* to insert. */
{
List *listRepPtr;
- register Tcl_Obj **elemPtrs, **newPtrs;
+ register Tcl_Obj **elemPtrs;
Tcl_Obj *victimPtr;
int numElems, numRequired, numAfterLast;
int start, shift, newMax, i, j, result;
-
+ int isShared;
+
if (Tcl_IsShared(listPtr)) {
Tcl_Panic("Tcl_ListObjReplace called with shared object");
}
if (listPtr->typePtr != &tclListType) {
- result = SetListFromAny(interp, listPtr);
- if (result != TCL_OK) {
- return result;
+ int length;
+
+ (void) Tcl_GetStringFromObj(listPtr, &length);
+ if (!length) {
+ if (objc) {
+ Tcl_SetListObj(listPtr, objc, NULL);
+ } else {
+ return TCL_OK;
+ }
+ } else {
+ result = SetListFromAny(interp, listPtr);
+ if (result != TCL_OK) {
+ return result;
+ }
}
}
+
listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
- elemPtrs = listRepPtr->elements;
+ elemPtrs = &listRepPtr->elements;
numElems = listRepPtr->elemCount;
if (first < 0) {
@@ -766,16 +779,21 @@ Tcl_ListObjReplace(interp, listPtr, first, count, objc, objv)
}
if (count < 0) {
count = 0;
+ } else if (numElems < first+count) {
+ count = numElems - first;
}
+ isShared = (listRepPtr->refCount > 1);
numRequired = (numElems - count + objc);
- if (numRequired <= listRepPtr->maxElemCount) {
+
+ if ((numRequired <= listRepPtr->maxElemCount)
+ && !isShared) {
/*
- * Enough room in the current array. First "delete" count
+ * Can use the current List struct. First "delete" count
* elements starting at first.
*/
- for (i = 0, j = first; i < count; i++, j++) {
+ for (j = first; j < first + count; j++) {
victimPtr = elemPtrs[j];
TclDecrRefCount(victimPtr);
}
@@ -795,79 +813,100 @@ Tcl_ListObjReplace(interp, listPtr, first, count, objc, objv)
memmove((VOID*) dst, (VOID*) src,
(size_t) (numAfterLast * sizeof(Tcl_Obj*)));
}
-
- /*
- * 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]);
- }
-
- /*
- * Update the count of elements.
- */
-
- listRepPtr->elemCount = numRequired;
} else {
/*
- * Not enough room in the current array. Allocate a larger array and
- * insert elements into it.
+ * Cannot use the current List struct - it is shared, too small,
+ * or both. Allocate a new struct and insert elements into it.
*/
- newMax = (2 * numRequired);
- newPtrs = (Tcl_Obj **)
- ckalloc((unsigned) (newMax * sizeof(Tcl_Obj *)));
-
- /*
- * Copy over the elements before "first".
- */
+ List *oldListRepPtr = listRepPtr;
+ Tcl_Obj **oldPtrs = elemPtrs;
- if (first > 0) {
- memcpy((VOID *) newPtrs, (VOID *) elemPtrs,
- (size_t) (first * sizeof(Tcl_Obj *)));
+ if (numRequired > listRepPtr->maxElemCount){
+ newMax = (2 * numRequired);
+ } else {
+ newMax = listRepPtr->maxElemCount;
+ }
+
+ listRepPtr = NewListIntRep(newMax, NULL);
+ if (!listRepPtr) {
+ Tcl_Panic("Not enough memory to allocate list");
}
- /*
- * "Delete" count elements starting at first.
- */
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ listRepPtr->refCount++;
+
+ elemPtrs = &listRepPtr->elements;
+
+ if (isShared) {
+ /*
+ * The old struct will remain in place; need new refCounts for the
+ * new List struct references. Copy over only the surviving elements.
+ */
- for (i = 0, j = first; i < count; i++, j++) {
- victimPtr = elemPtrs[j];
- TclDecrRefCount(victimPtr);
- }
+ for (i=0; i < first; i++) {
+ elemPtrs[i] = oldPtrs[i];
+ Tcl_IncrRefCount(elemPtrs[i]);
+ }
+ for (i= first + count, j = first + objc;
+ j < numRequired; i++, j++) {
+ elemPtrs[j] = oldPtrs[i];
+ Tcl_IncrRefCount(elemPtrs[j]);
+ }
- /*
- * Copy the elements after the last one removed, shifted to
- * their new locations.
- */
+ oldListRepPtr->refCount--;
+ } else {
+ /*
+ * The old struct will be removed; use its inherited refCounts.
+ */
- start = (first + count);
- numAfterLast = (numElems - start);
- if (numAfterLast > 0) {
- memcpy((VOID *) &(newPtrs[first + objc]),
- (VOID *) &(elemPtrs[start]),
- (size_t) (numAfterLast * sizeof(Tcl_Obj *)));
- }
+ if (first > 0) {
+ memcpy((VOID *) elemPtrs, (VOID *) oldPtrs,
+ (size_t) (first * sizeof(Tcl_Obj *)));
+ }
- /*
- * Insert the new elements before "first" and update the
- * count of elements.
- */
+ /*
+ * "Delete" count elements starting at first.
+ */
+
+ for (j = first; j < first + count; j++) {
+ victimPtr = oldPtrs[j];
+ TclDecrRefCount(victimPtr);
+ }
+
+ /*
+ * Copy the elements after the last one removed, shifted to
+ * their new locations.
+ */
+
+ start = (first + count);
+ numAfterLast = (numElems - start);
+ if (numAfterLast > 0) {
+ memcpy((VOID *) &(elemPtrs[first + objc]),
+ (VOID *) &(oldPtrs[start]),
+ (size_t) (numAfterLast * sizeof(Tcl_Obj *)));
+ }
- for (i = 0, j = first; i < objc; i++, j++) {
- newPtrs[j] = objv[i];
- Tcl_IncrRefCount(objv[i]);
+ ckfree((char *) oldListRepPtr);
}
-
- listRepPtr->elemCount = numRequired;
- listRepPtr->maxElemCount = newMax;
- listRepPtr->elements = newPtrs;
- ckfree((char *) elemPtrs);
}
/*
+ * 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]);
+ }
+
+ /*
+ * Update the count of elements.
+ */
+
+ listRepPtr->elemCount = numRequired;
+
+ /*
* Invalidate and free any old string representation since it no longer
* reflects the list's internal representation.
*/
@@ -932,18 +971,11 @@ TclLsetList(interp, listPtr, indexArgPtr, valuePtr)
{
int indexCount; /* Number of indices in the index list */
Tcl_Obj** indices; /* Vector of indices in the index list*/
- int duplicated; /* Flag == 1 if the obj has been
- * duplicated, 0 otherwise */
Tcl_Obj* retValuePtr; /* Pointer to the list to be returned */
int index; /* Current index in the list - discarded */
- int result; /* Status return from library calls */
- Tcl_Obj* subListPtr; /* Pointer to the current sublist */
- int elemCount; /* Count of elements in the current sublist */
- Tcl_Obj** elemPtrs; /* Pointers to elements of current sublist */
- Tcl_Obj* chainPtr; /* Pointer to the enclosing sublist
- * of the current sublist */
int i;
-
+ List *indexListRepPtr;
+
/*
* Determine whether the index arg designates a list or a single
* index. We have to be careful about the order of the checks to
@@ -971,165 +1003,39 @@ TclLsetList(interp, listPtr, indexArgPtr, valuePtr)
/*
* At this point, we know that argPtr designates a well formed list,
* and the 'else if' above has parsed it into indexCount and indices.
- * If there are no indices, simply return 'valuePtr', counting the
- * returned pointer as a reference.
- */
-
- if (indexCount == 0) {
- Tcl_IncrRefCount(valuePtr);
- return valuePtr;
- }
-
- /*
- * Duplicate the list arg if necessary.
+ * Increase the reference count of the internal rep of indexArgPtr,
+ * in order to insure the validity of pointers even if indexArgPtr
+ * shimmers to another type.
*/
- if (Tcl_IsShared(listPtr)) {
- duplicated = 1;
- listPtr = Tcl_DuplicateObj(listPtr);
- Tcl_IncrRefCount(listPtr);
+ if (indexCount) {
+ indexListRepPtr = (List *) indexArgPtr->internalRep.twoPtrValue.ptr1;
+ indexListRepPtr->refCount++;
} else {
- duplicated = 0;
+ indexListRepPtr = NULL; /* avoid compiler warning*/
}
+
/*
- * It would be tempting simply to go off to TclLsetFlat to finish the
- * processing. Alas, it is also incorrect! The problem is that
- * 'indexArgPtr' may designate a sublist of 'listPtr' whose value
- * is to be manipulated. The fact that 'listPtr' is itself unshared
- * does not guarantee that no sublist is. Therefore, it's necessary
- * to replicate all the work here, expanding the index list on each
- * trip through the loop.
+ * Let TclLsetFlat handle the actual lset'ting.
*/
- /*
- * Anchor the linked list of Tcl_Obj's whose string reps must be
- * invalidated if the operation succeeds.
- */
-
- retValuePtr = listPtr;
- chainPtr = NULL;
+ retValuePtr = TclLsetFlat(interp, listPtr, indexCount, indices, valuePtr);
/*
- * Handle each index arg by diving into the appropriate sublist
+ * If we are the only users of indexListRepPtr, we free it before
+ * returning.
*/
-
- for (i=0 ; ; i++) {
- /*
- * Take the sublist apart.
- */
-
- result = Tcl_ListObjGetElements(interp, listPtr, &elemCount, &elemPtrs);
- if (result != TCL_OK) {
- break;
- }
- listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
-
- /*
- * Reconstitute the index array
- */
-
- result = Tcl_ListObjGetElements(interp, indexArgPtr, &indexCount,
- &indices);
- if (result != TCL_OK) {
- /*
- * Shouldn't be able to get here, because we already
- * parsed the thing successfully once.
- */
- break;
- }
-
- /*
- * Determine the index of the requested element.
- */
-
- result = TclGetIntForIndex(interp, indices[i], elemCount-1, &index);
- if (result != TCL_OK) {
- break;
- }
-
- /*
- * Check that the index is in range.
- */
-
- if (index<0 || index>=elemCount) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("list index out of range", -1));
- result = TCL_ERROR;
- break;
- }
-
- /*
- * Break the loop after extracting the innermost sublist
- */
-
- if (i >= indexCount-1) {
- result = TCL_OK;
- break;
- }
-
- /*
- * Extract the appropriate sublist, and make sure that it is unshared.
- */
-
- subListPtr = elemPtrs[index];
- if (Tcl_IsShared(subListPtr)) {
- subListPtr = Tcl_DuplicateObj(subListPtr);
- result = TclListObjSetElement(interp, listPtr, index, subListPtr);
- if (result != TCL_OK) {
- /*
- * We actually shouldn't be able to get here, because
- * we've already checked everything that TclListObjSetElement
- * checks. If we were to get here, it would result in leaking
- * subListPtr.
- */
- break;
+
+ if (indexCount) {
+ if (--indexListRepPtr->refCount <= 0) {
+ for (i=0; i<indexCount; i++) {
+ Tcl_DecrRefCount(indices[i]);
}
+ ckfree((char *) indexListRepPtr);
}
-
- /*
- * Chain the current sublist onto the linked list of Tcl_Obj's
- * whose string reps must be spoilt.
- */
-
- chainPtr = listPtr;
- listPtr = subListPtr;
}
-
- /*
- * Store the new element into the correct slot in the innermost sublist.
- */
-
- if (result == TCL_OK) {
- result = TclListObjSetElement(interp, listPtr, index, valuePtr);
- }
-
- if (result == TCL_OK) {
- listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
-
- /* Spoil all the string reps */
-
- while (listPtr != NULL) {
- subListPtr = (Tcl_Obj *) listPtr->internalRep.twoPtrValue.ptr2;
- Tcl_InvalidateStringRep(listPtr);
- listPtr->internalRep.twoPtrValue.ptr2 = NULL;
- listPtr = subListPtr;
- }
-
- /* Return the new list if everything worked. */
-
- if (!duplicated) {
- Tcl_IncrRefCount(retValuePtr);
- }
- return retValuePtr;
- }
-
- /* Clean up the one dangling reference otherwise */
-
- if (duplicated) {
- Tcl_DecrRefCount(retValuePtr);
- }
- return NULL;
+ return retValuePtr;
}
/*
@@ -1200,7 +1106,7 @@ TclLsetFlat(interp, listPtr, indexCount, indexArray, valuePtr)
* the current sublist. */
int result; /* Status return from library calls */
int i;
-
+
/*
* If there are no indices, then simply return the new value,
* counting the returned pointer as a reference
@@ -1244,6 +1150,12 @@ TclLsetFlat(interp, listPtr, indexCount, indexArray, valuePtr)
if (result != TCL_OK) {
break;
}
+ if (elemCount == 0) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("list index out of range", -1));
+ result = TCL_ERROR;
+ break;
+ }
listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
/*
@@ -1358,10 +1270,10 @@ TclLsetFlat(interp, listPtr, indexCount, indexArray, valuePtr)
* Side effects:
*
* Tcl_Panic if listPtr designates a shared object. Otherwise,
- * attempts to convert it to a list. Decrements the ref count of
- * the object at the specified index within the list, replaces with
- * the object designated by valuePtr, and increments the ref count
- * of the replacement object.
+ * attempts to convert it to a list with a non-shared internal rep.
+ * Decrements the ref count of the object at the specified index within
+ * the list, replaces with the object designated by valuePtr, and
+ * increments the ref count of the replacement object.
*
* It is the caller's responsibility to invalidate the string
* representation of the object.
@@ -1384,23 +1296,33 @@ TclListObjSetElement(interp, listPtr, index, valuePtr)
* being modified */
Tcl_Obj** elemPtrs; /* Pointers to elements of the list */
int elemCount; /* Number of elements in the list */
-
+ int i;
+
/* Ensure that the listPtr parameter designates an unshared list */
if (Tcl_IsShared(listPtr)) {
Tcl_Panic("Tcl_ListObjSetElement called with shared object");
}
if (listPtr->typePtr != &tclListType) {
+ int length;
+
+ (void) Tcl_GetStringFromObj(listPtr, &length);
+ if (!length) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("list index out of range", -1));
+ return TCL_ERROR;
+ }
result = SetListFromAny(interp, listPtr);
if (result != TCL_OK) {
return result;
}
}
+
listRepPtr = (List*) listPtr->internalRep.twoPtrValue.ptr1;
- elemPtrs = listRepPtr->elements;
elemCount = listRepPtr->elemCount;
+ elemPtrs = &listRepPtr->elements;
- /* Ensure that the index is in bounds */
+ /* Ensure that the index is in bounds. */
if (index<0 || index>=elemCount) {
if (interp != NULL) {
@@ -1410,6 +1332,26 @@ TclListObjSetElement(interp, listPtr, index, valuePtr)
}
}
+ /*
+ * If the internal rep is shared, replace it with an unshared copy.
+ */
+
+ if (listRepPtr->refCount > 1) {
+ List *oldListRepPtr = listRepPtr;
+ Tcl_Obj **oldElemPtrs = elemPtrs;
+
+ listRepPtr = NewListIntRep(listRepPtr->maxElemCount, NULL);
+ elemPtrs = &listRepPtr->elements;
+ for (i=0; i < elemCount; i++) {
+ elemPtrs[i] = oldElemPtrs[i];
+ Tcl_IncrRefCount(elemPtrs[i]);
+ }
+ listRepPtr->refCount++;
+ listRepPtr->elemCount = elemCount;
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ oldListRepPtr->refCount--;
+ }
+
/* Add a reference to the new list element */
Tcl_IncrRefCount(valuePtr);
@@ -1449,18 +1391,19 @@ FreeListInternalRep(listPtr)
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 **elemPtrs = &listRepPtr->elements;
register Tcl_Obj *objPtr;
int numElems = listRepPtr->elemCount;
int i;
- for (i = 0; i < numElems; i++) {
- objPtr = elemPtrs[i];
- Tcl_DecrRefCount(objPtr);
+ if (--listRepPtr->refCount <= 0) {
+ for (i = 0; i < numElems; i++) {
+ objPtr = elemPtrs[i];
+ Tcl_DecrRefCount(objPtr);
+ }
+ ckfree((char *) listRepPtr);
}
- ckfree((char *) elemPtrs);
- ckfree((char *) listRepPtr);
-
+
listPtr->internalRep.twoPtrValue.ptr1 = NULL;
listPtr->internalRep.twoPtrValue.ptr2 = NULL;
}
@@ -1470,19 +1413,14 @@ FreeListInternalRep(listPtr)
*
* DupListInternalRep --
*
- * Initialize the internal representation of a list Tcl_Obj to a
- * copy of the internal representation of an existing list object.
+ * Initialize the internal representation of a list Tcl_Obj to share
+ * the internal representation of an existing list object.
*
* Results:
* None.
*
* Side effects:
- * "srcPtr"s list internal rep pointer should not be NULL and we assume
- * it is not NULL. We set "copyPtr"s internal rep to a pointer to a
- * newly allocated List structure that, in turn, points to "srcPtr"s
- * element objects. Those element objects are not actually copied but
- * are shared between "srcPtr" and "copyPtr". The ref count of each
- * element object is incremented.
+ * The reference count of the List internal rep is incremented.
*
*----------------------------------------------------------------------
*/
@@ -1492,33 +1430,10 @@ DupListInternalRep(srcPtr, copyPtr)
Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
Tcl_Obj *copyPtr; /* Object with internal rep to set. */
{
- List *srcListRepPtr = (List *) srcPtr->internalRep.twoPtrValue.ptr1;
- int numElems = srcListRepPtr->elemCount;
- int maxElems = srcListRepPtr->maxElemCount;
- register Tcl_Obj **srcElemPtrs = srcListRepPtr->elements;
- register Tcl_Obj **copyElemPtrs;
- register List *copyListRepPtr;
- int i;
-
- /*
- * Allocate a new List structure that points to "srcPtr"s element
- * objects. Increment the ref counts for those (now shared) element
- * objects.
- */
-
- copyElemPtrs = (Tcl_Obj **)
- ckalloc((unsigned) maxElems * sizeof(Tcl_Obj *));
- for (i = 0; i < numElems; i++) {
- copyElemPtrs[i] = srcElemPtrs[i];
- Tcl_IncrRefCount(copyElemPtrs[i]);
- }
+ List *listRepPtr = (List *) srcPtr->internalRep.twoPtrValue.ptr1;
- copyListRepPtr = (List *) ckalloc(sizeof(List));
- copyListRepPtr->maxElemCount = maxElems;
- copyListRepPtr->elemCount = numElems;
- copyListRepPtr->elements = copyElemPtrs;
-
- copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) copyListRepPtr;
+ listRepPtr->refCount++;
+ copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
copyPtr->typePtr = &tclListType;
}
@@ -1587,8 +1502,14 @@ SetListFromAny(interp, objPtr)
* corresponding "argv" strings.
*/
- elemPtrs = (Tcl_Obj **)
- ckalloc((unsigned) (estCount * sizeof(Tcl_Obj *)));
+ 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;
+
for (p = string, lenRemain = length, i = 0;
lenRemain > 0;
p = nextElem, lenRemain = (limit - nextElem), i++) {
@@ -1599,7 +1520,7 @@ SetListFromAny(interp, objPtr)
elemPtr = elemPtrs[j];
Tcl_DecrRefCount(elemPtr);
}
- ckfree((char *) elemPtrs);
+ ckfree((char *) listRepPtr);
return result;
}
if (elemStart >= limit) {
@@ -1629,10 +1550,7 @@ SetListFromAny(interp, objPtr)
Tcl_IncrRefCount(elemPtr); /* since list now holds ref to it */
}
- listRepPtr = (List *) ckalloc(sizeof(List));
- listRepPtr->maxElemCount = estCount;
listRepPtr->elemCount = i;
- listRepPtr->elements = elemPtrs;
/*
* Free the old internalRep before setting the new one. We do this as
@@ -1640,6 +1558,7 @@ SetListFromAny(interp, objPtr)
* Tcl_GetStringFromObj, to use that old internalRep.
*/
+ listRepPtr->refCount++;
TclFreeIntRep(objPtr);
objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
objPtr->internalRep.twoPtrValue.ptr2 = NULL;
@@ -1679,7 +1598,8 @@ UpdateStringOfList(listPtr)
register int i;
char *elem, *dst;
int length;
-
+ 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.
@@ -1695,8 +1615,9 @@ UpdateStringOfList(listPtr)
flagPtr = (int *) ckalloc((unsigned) numElems*sizeof(int));
}
listPtr->length = 1;
+ elemPtrs = &listRepPtr->elements;
for (i = 0; i < numElems; i++) {
- elem = Tcl_GetStringFromObj(listRepPtr->elements[i], &length);
+ elem = Tcl_GetStringFromObj(elemPtrs[i], &length);
listPtr->length += Tcl_ScanCountedElement(elem, length,
&flagPtr[i]) + 1;
}
@@ -1708,7 +1629,7 @@ UpdateStringOfList(listPtr)
listPtr->bytes = ckalloc((unsigned) listPtr->length);
dst = listPtr->bytes;
for (i = 0; i < numElems; i++) {
- elem = Tcl_GetStringFromObj(listRepPtr->elements[i], &length);
+ elem = Tcl_GetStringFromObj(elemPtrs[i], &length);
dst += Tcl_ConvertCountedElement(elem, length, dst,
flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH));
*dst = ' ';