From de532c94fa09b0520a3bba6eaa50d3f9f57b8000 Mon Sep 17 00:00:00 2001 From: dkf Date: Sun, 20 Jul 2008 17:55:37 +0000 Subject: Fix [Bug 2008248] and make dict->list->dict round trip efficient to boot. --- ChangeLog | 7 +++++++ generic/tclDictObj.c | 11 +--------- generic/tclListObj.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7966665..ebe7ee7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2008-07-20 Donal K. Fellows + * generic/tclDictObj.c (SetDictFromAny): Make the list->dict + transformation a bit more efficient; modern dicts are ordered and so + we can round-trip through lists without needing the string rep at all. + * generic/tclListObj.c (SetListFromAny): Make the dict->list + transformation not lossy of internal representations and hence more + efficient. [Bug 2008248] (ajpasadyn) but using a more efficient patch. + * tests/fileName.test: Revise to reduce the obscurity of tests. In particular, all tests should now produce informative messages on failure and the quantity of [catch]-based obscurity is now greatly diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index 2694cf8..eb56e4a 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclDictObj.c,v 1.65 2008/07/19 22:50:42 nijtmans Exp $ + * RCS: @(#) $Id: tclDictObj.c,v 1.66 2008/07/20 17:55:50 dkf Exp $ */ #include "tclInt.h" @@ -593,15 +593,6 @@ SetDictFromAny( } /* - * If the list is shared its string rep must not be lost so it still - * is the same list. - */ - - if (Tcl_IsShared(objPtr)) { - (void) TclGetString(objPtr); - } - - /* * Build the hash of key/value pairs. */ diff --git a/generic/tclListObj.c b/generic/tclListObj.c index d04fb45..6cf903b 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -10,7 +10,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.50 2008/04/27 22:21:30 dkf Exp $ + * RCS: @(#) $Id: tclListObj.c,v 1.51 2008/07/20 17:55:51 dkf Exp $ */ #include "tclInt.h" @@ -1658,6 +1658,58 @@ SetListFromAny( List *listRepPtr; /* + * Dictionaries are a special case; they have a string representation such + * that *all* valid dictionaries are valid lists. Hence we can convert + * more directly. + */ + + if (objPtr->typePtr == &tclDictType) { + Tcl_Obj *keyPtr, *valuePtr; + Tcl_DictSearch search; + int done, size; + + /* + * Create the new list representation. Note that we do not need to do + * anything with the string representation as the transformation (and + * the reverse back to a dictionary) are both order-preserving. Also + * note that since we know we've got a valid dictionary (by + * representation) we also know that fetching the size of the + * dictionary or iterating over it will not fail. + */ + + Tcl_DictObjSize(NULL, objPtr, &size); + listRepPtr = NewListIntRep(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; + + /* + * Populate the list representation. + */ + + elemPtrs = &listRepPtr->elements; + Tcl_DictObjFirst(NULL, objPtr, &search, &keyPtr, &valuePtr, &done); + i = 0; + while (!done) { + elemPtrs[i++] = keyPtr; + elemPtrs[i++] = valuePtr; + Tcl_IncrRefCount(keyPtr); + Tcl_IncrRefCount(valuePtr); + Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done); + } + + /* + * Swap the representations. + */ + + goto commitRepresentation; + } + + /* * Get the string representation. Make it up-to-date if necessary. */ @@ -1742,9 +1794,10 @@ SetListFromAny( * Tcl_GetStringFromObj, to use that old internalRep. */ + commitRepresentation: listRepPtr->refCount++; TclFreeIntRep(objPtr); - objPtr->internalRep.twoPtrValue.ptr1 = (void *) listRepPtr; + objPtr->internalRep.twoPtrValue.ptr1 = listRepPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &tclListType; return TCL_OK; -- cgit v0.12