Attachment "fsspeed.diff" to
ticket [682500ffff]
added by
vincentdarley
2003-02-08 02:05:29.
Index: generic/tclIOUtil.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIOUtil.c,v
retrieving revision 1.71
diff -u -r1.71 tclIOUtil.c
--- generic/tclIOUtil.c 4 Feb 2003 17:06:50 -0000 1.71
+++ generic/tclIOUtil.c 7 Feb 2003 18:51:43 -0000
@@ -37,12 +37,14 @@
static void DupFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
Tcl_Obj *copyPtr));
static void FreeFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
+static void UpdateStringOfFsPath _ANSI_ARGS_((Tcl_Obj *objPtr));
static int SetFsPathFromAny _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr));
static Tcl_Obj* FSNormalizeAbsolutePath
_ANSI_ARGS_((Tcl_Interp* interp, Tcl_Obj *pathPtr));
static int TclNormalizeToUniquePath
- _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *pathPtr));
+ _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ int startAt));
static int SetFsPathFromAbsoluteNormalized
_ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *objPtr));
static int FindSplitPos _ANSI_ARGS_((char *path, char *separator));
@@ -61,7 +63,7 @@
"path", /* name */
FreeFsPathInternalRep, /* freeIntRepProc */
DupFsPathInternalRep, /* dupIntRepProc */
- NULL, /* updateStringProc */
+ UpdateStringOfFsPath, /* updateStringProc */
SetFsPathFromAny /* setFromAnyProc */
};
@@ -495,7 +497,8 @@
Tcl_Obj *cwdPtr; /* If null, path is absolute, else
* this points to the cwd object used
* for this path. We have a refCount
- * on the object. */
+ * on the object. */
+ int flags; /* Flags to describe interpretation */
ClientData nativePathPtr; /* Native representation of this path,
* which is filesystem dependent. */
int filesystemEpoch; /* Used to ensure the path representation
@@ -507,6 +510,8 @@
* entry to use for this path. */
} FsPath;
+#define TCLPATH_APPENDED 1
+#define TCLPATH_RELATIVE 2
/*
* Used to implement Tcl_FSGetCwd in a file-system independent way.
* This is protected by the cwdMutex below.
@@ -1040,7 +1045,7 @@
* other criteria for normalizing a path.
*/
Tcl_IncrRefCount(retVal);
- TclNormalizeToUniquePath(interp, retVal);
+ TclNormalizeToUniquePath(interp, retVal, 0);
/*
* Since we know it is a normalized path, we can
* actually convert this object into an FsPath for
@@ -1082,16 +1087,15 @@
* us a unique, case-dependent path).
*
* Results:
- * The result is returned in a Tcl_Obj with a refCount of 1,
- * which is therefore owned by the caller. It must be
- * freed (with Tcl_DecrRefCount) by the caller when no longer needed.
+ * The pathPtr is modified in place. The return value is
+ * the last byte offset which was recognised in the path
+ * string.
*
* Side effects:
* None (beyond the memory allocation for the result).
*
* Special note:
- * This is only used by the above function. Also if the
- * filesystem-specific normalizePathProcs can re-introduce
+ * If the filesystem-specific normalizePathProcs can re-introduce
* ../, ./ sequences into the path, then this function will
* not return the correct result. This may be possible with
* symbolic links on unix/macos.
@@ -1099,12 +1103,12 @@
*---------------------------------------------------------------------------
*/
static int
-TclNormalizeToUniquePath(interp, pathPtr)
+TclNormalizeToUniquePath(interp, pathPtr, startAt)
Tcl_Interp *interp;
Tcl_Obj *pathPtr;
+ int startAt;
{
FilesystemRecord *fsRecPtr;
- int retVal = 0;
/*
* Call each of the "normalise path" functions in succession. This is
@@ -1118,7 +1122,7 @@
if (fsRecPtr == &nativeFilesystemRecord) {
Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
if (proc != NULL) {
- retVal = (*proc)(interp, pathPtr, retVal);
+ startAt = (*proc)(interp, pathPtr, startAt);
}
break;
}
@@ -1132,7 +1136,7 @@
if (fsRecPtr != &nativeFilesystemRecord) {
Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
if (proc != NULL) {
- retVal = (*proc)(interp, pathPtr, retVal);
+ startAt = (*proc)(interp, pathPtr, startAt);
}
/*
* We could add an efficiency check like this:
@@ -1146,7 +1150,7 @@
}
FsReleaseIterator();
- return (retVal);
+ return (startAt);
}
/*
@@ -1899,9 +1903,7 @@
Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc;
if (proc != NULL) {
int cwdLen;
- Tcl_Obj *cwdDir;
char *cwdStr;
- char sep = 0;
Tcl_Obj* tmpResultPtr = Tcl_NewListObj(0, NULL);
/*
* We know the cwd is a normalised object which does
@@ -1915,9 +1917,7 @@
* either too much or too little below, leading to
* wrong answers returned by glob.
*/
- cwdDir = Tcl_DuplicateObj(cwd);
- Tcl_IncrRefCount(cwdDir);
- cwdStr = Tcl_GetStringFromObj(cwdDir, &cwdLen);
+ cwdStr = Tcl_GetStringFromObj(cwd, &cwdLen);
/*
* Should we perhaps use 'Tcl_FSPathSeparator'?
* But then what about the Windows special case?
@@ -1927,27 +1927,22 @@
switch (tclPlatform) {
case TCL_PLATFORM_UNIX:
if (cwdStr[cwdLen-1] != '/') {
- sep = '/';
+ cwdLen++;
}
break;
case TCL_PLATFORM_WINDOWS:
- if (cwdStr[cwdLen-1] != '/' && cwdStr[cwdLen-1] != '\\') {
- sep = '/';
+ if (cwdStr[cwdLen-1] != '/'
+ && cwdStr[cwdLen-1] != '\\') {
+ cwdLen++;
}
break;
case TCL_PLATFORM_MAC:
if (cwdStr[cwdLen-1] != ':') {
- sep = ':';
+ cwdLen++;
}
break;
}
- if (sep != 0) {
- Tcl_AppendToObj(cwdDir, &sep, 1);
- cwdLen++;
- /* Note: cwdStr may no longer be a valid pointer now */
- }
- ret = (*proc)(interp, tmpResultPtr, cwdDir, pattern, types);
- Tcl_DecrRefCount(cwdDir);
+ ret = (*proc)(interp, tmpResultPtr, cwd, pattern, types);
if (ret == TCL_OK) {
int resLength;
@@ -3766,10 +3761,14 @@
if (objPtr->typePtr == &tclFsPathType) {
FsPath *fsPathPtr = (FsPath*) objPtr->internalRep.otherValuePtr;
if (fsPathPtr->filesystemEpoch != theFilesystemEpoch) {
+ if (objPtr->bytes == NULL) {
+
+ }
FreeFsPathInternalRep(objPtr);
objPtr->typePtr = NULL;
return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
}
+ return TCL_OK;
if (fsPathPtr->cwdPtr == NULL) {
return TCL_OK;
} else {
@@ -3823,6 +3822,127 @@
/*
*---------------------------------------------------------------------------
*
+ * UpdateStringOfFsPath --
+ *
+ * Gives an object a valid string rep.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory may be allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+UpdateStringOfFsPath(objPtr)
+ register Tcl_Obj *objPtr; /* path obj with string rep to update. */
+{
+ register FsPath* fsPathPtr =
+ (FsPath*) objPtr->internalRep.otherValuePtr;
+ CONST char *cwdStr;
+ int cwdLen;
+ Tcl_Obj *copy;
+
+ if (fsPathPtr->flags == 0 || fsPathPtr->cwdPtr == NULL) {
+ panic("Called UpdateStringOfFsPath with invalid object");
+ }
+
+ copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
+ Tcl_IncrRefCount(copy);
+
+ cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
+ /*
+ * Should we perhaps use 'Tcl_FSPathSeparator'?
+ * But then what about the Windows special case?
+ * Perhaps we should just check if cwd is a root
+ * volume.
+ */
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ if (cwdStr[cwdLen-1] != '/') {
+ Tcl_AppendToObj(copy, "/", 1);
+ cwdLen++;
+ }
+ break;
+ case TCL_PLATFORM_WINDOWS:
+ /*
+ * We need the cwdLen > 2 because a volume
+ * relative path doesn't get a '/'. For
+ * example 'glob C:*cat*.exe' will return
+ * 'C:cat32.exe'
+ */
+ if (cwdLen > 2 && cwdStr[cwdLen-1] != '/'
+ && cwdStr[cwdLen-1] != '\\') {
+ Tcl_AppendToObj(copy, "/", 1);
+ cwdLen++;
+ }
+ break;
+ case TCL_PLATFORM_MAC:
+ if (cwdStr[cwdLen-1] != ':') {
+ Tcl_AppendToObj(copy, ":", 1);
+ cwdLen++;
+ }
+ break;
+ }
+
+ Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
+ objPtr->bytes = Tcl_GetStringFromObj(copy, &cwdLen);
+ objPtr->length = cwdLen;
+ copy->bytes = tclEmptyStringRep;
+ copy->length = 0;
+ Tcl_DecrRefCount(copy);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclNewFSPathObj --
+ *
+ * Creates a path object whose string representation is
+ * '[file join dirPtr addStrRep]', but does so in a way that
+ * allows for more efficient caching of normalized paths.
+ *
+ * Results:
+ * The new Tcl object.
+ *
+ * Side effects:
+ * Memory is allocated. 'dirPtr' gets an additional refCount.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
+{
+ FsPath *fsPathPtr;
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewObj();
+ fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
+
+ /* Setup the path */
+ fsPathPtr->translatedPathPtr = NULL;
+ fsPathPtr->normPathPtr = Tcl_NewStringObj(addStrRep, len);
+ Tcl_IncrRefCount(fsPathPtr->normPathPtr);
+ fsPathPtr->cwdPtr = dirPtr;
+ Tcl_IncrRefCount(dirPtr);
+ fsPathPtr->flags = TCLPATH_RELATIVE | TCLPATH_APPENDED;
+ fsPathPtr->nativePathPtr = NULL;
+ fsPathPtr->fsRecPtr = NULL;
+ fsPathPtr->filesystemEpoch = theFilesystemEpoch;
+
+ objPtr->internalRep.otherValuePtr = (VOID *) fsPathPtr;
+ objPtr->typePtr = &tclFsPathType;
+ objPtr->bytes = NULL;
+ objPtr->length = 0;
+ return objPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* SetFsPathFromAbsoluteNormalized --
*
* Like SetFsPathFromAny, but assumes the given object is an
@@ -3870,6 +3990,7 @@
/* It's a pure normalized absolute path */
fsPathPtr->translatedPathPtr = NULL;
fsPathPtr->normPathPtr = objPtr;
+ fsPathPtr->flags = 0;
fsPathPtr->cwdPtr = NULL;
fsPathPtr->nativePathPtr = NULL;
fsPathPtr->fsRecPtr = NULL;
@@ -4031,6 +4152,7 @@
fsPathPtr->translatedPathPtr = transPtr;
Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);
fsPathPtr->normPathPtr = NULL;
+ fsPathPtr->flags = 0;
fsPathPtr->cwdPtr = NULL;
fsPathPtr->nativePathPtr = NULL;
fsPathPtr->fsRecPtr = NULL;
@@ -4122,6 +4244,7 @@
fsPathPtr->translatedPathPtr = NULL;
/* Circular reference, by design */
fsPathPtr->normPathPtr = objPtr;
+ fsPathPtr->flags = 0;
fsPathPtr->cwdPtr = NULL;
fsPathPtr->nativePathPtr = clientData;
fsPathPtr->fsRecPtr = fsFromPtr;
@@ -4209,6 +4332,8 @@
copyFsPathPtr->cwdPtr = NULL;
}
+ copyFsPathPtr->flags = srcFsPathPtr->flags;
+
if (srcFsPathPtr->fsRecPtr != NULL
&& srcFsPathPtr->nativePathPtr != NULL) {
dupProc = srcFsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc;
@@ -4330,18 +4455,109 @@
Tcl_Interp *interp;
Tcl_Obj* pathObjPtr;
{
- register FsPath* srcFsPathPtr;
+ register FsPath* fsPathPtr;
if (Tcl_FSConvertToPathType(interp, pathObjPtr) != TCL_OK) {
return NULL;
}
- srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
- if (srcFsPathPtr->normPathPtr == NULL) {
+ fsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+
+ /* Ensure cwd hasn't changed */
+ if (fsPathPtr->flags != 0) {
+ Tcl_Obj *dir, *copy;
+ int dirLen;
+ int pathType;
+ CONST char *cwdStr;
+
+ pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr);
+ dir = Tcl_FSGetNormalizedPath(interp, fsPathPtr->cwdPtr);
+ if (dir == NULL) {
+ return NULL;
+ }
+ if (pathObjPtr->bytes == NULL) {
+ UpdateStringOfFsPath(pathObjPtr);
+ }
+ copy = Tcl_DuplicateObj(dir);
+ Tcl_IncrRefCount(copy);
+ Tcl_IncrRefCount(dir);
+ /* We now own a reference on both 'dir' and 'copy' */
+
+ cwdStr = Tcl_GetStringFromObj(copy,&dirLen);
+ /*
+ * Should we perhaps use 'Tcl_FSPathSeparator'?
+ * But then what about the Windows special case?
+ * Perhaps we should just check if cwd is a root
+ * volume.
+ */
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ if (cwdStr[dirLen-1] != '/') {
+ Tcl_AppendToObj(copy, "/", 1);
+ dirLen++;
+ }
+ break;
+ case TCL_PLATFORM_WINDOWS:
+ if (cwdStr[dirLen-1] != '/'
+ && cwdStr[dirLen-1] != '\\') {
+ Tcl_AppendToObj(copy, "/", 1);
+ dirLen++;
+ }
+ break;
+ case TCL_PLATFORM_MAC:
+ if (cwdStr[dirLen-1] != ':') {
+ Tcl_AppendToObj(copy, ":", 1);
+ dirLen++;
+ }
+ break;
+ }
+ Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
+ /*
+ * Normalize the combined string, but only starting after
+ * the end of the previously normalized 'dir'. This should
+ * be much faster!
+ */
+ TclNormalizeToUniquePath(interp, copy, dirLen);
+ /* Now we need to construct the new path object */
+
+ if (pathType == TCL_PATH_RELATIVE) {
+ register FsPath* origDirFsPathPtr;
+ Tcl_Obj *origDir = fsPathPtr->cwdPtr;
+ origDirFsPathPtr = (FsPath*) origDir->internalRep.otherValuePtr;
+
+ fsPathPtr->cwdPtr = origDirFsPathPtr->cwdPtr;
+ Tcl_IncrRefCount(fsPathPtr->cwdPtr);
+
+ Tcl_DecrRefCount(fsPathPtr->normPathPtr);
+ fsPathPtr->normPathPtr = copy;
+ /* That's our reference to copy used */
+ Tcl_DecrRefCount(dir);
+ Tcl_DecrRefCount(origDir);
+ } else {
+ Tcl_DecrRefCount(fsPathPtr->cwdPtr);
+ fsPathPtr->cwdPtr = NULL;
+ Tcl_DecrRefCount(fsPathPtr->normPathPtr);
+ fsPathPtr->normPathPtr = copy;
+ /* That's our reference to copy used */
+ Tcl_DecrRefCount(dir);
+ }
+ fsPathPtr->flags = 0;
+ }
+ if (fsPathPtr->cwdPtr != NULL) {
+ if (!FsCwdPointerEquals(fsPathPtr->cwdPtr)) {
+ FreeFsPathInternalRep(pathObjPtr);
+ pathObjPtr->typePtr = NULL;
+ if (Tcl_ConvertToType(interp, pathObjPtr, &tclFsPathType) != TCL_OK) {
+ return NULL;
+ }
+ fsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+ }
+ }
+ if (fsPathPtr->normPathPtr == NULL) {
int relative = 0;
/*
* Since normPathPtr is NULL, but this is a valid path
* object, we know that the translatedPathPtr cannot be NULL.
*/
- Tcl_Obj *absolutePath = srcFsPathPtr->translatedPathPtr;
+ Tcl_Obj *absolutePath = fsPathPtr->translatedPathPtr;
char *path = Tcl_GetString(absolutePath);
/*
@@ -4365,19 +4581,19 @@
relative = 1;
}
/* Already has refCount incremented */
- srcFsPathPtr->normPathPtr = FSNormalizeAbsolutePath(interp, absolutePath);
- if (!strcmp(Tcl_GetString(srcFsPathPtr->normPathPtr),
+ fsPathPtr->normPathPtr = FSNormalizeAbsolutePath(interp, absolutePath);
+ if (!strcmp(Tcl_GetString(fsPathPtr->normPathPtr),
Tcl_GetString(pathObjPtr))) {
/*
* The path was already normalized.
* Get rid of the duplicate.
*/
- Tcl_DecrRefCount(srcFsPathPtr->normPathPtr);
+ Tcl_DecrRefCount(fsPathPtr->normPathPtr);
/*
* We do *not* increment the refCount for
* this circular reference
*/
- srcFsPathPtr->normPathPtr = pathObjPtr;
+ fsPathPtr->normPathPtr = pathObjPtr;
}
if (relative) {
/* This was returned by Tcl_FSJoinToPath above */
@@ -4385,12 +4601,12 @@
/* Get a quick, temporary lock on the cwd while we copy it */
Tcl_MutexLock(&cwdMutex);
- srcFsPathPtr->cwdPtr = cwdPathPtr;
- Tcl_IncrRefCount(srcFsPathPtr->cwdPtr);
+ fsPathPtr->cwdPtr = cwdPathPtr;
+ Tcl_IncrRefCount(fsPathPtr->cwdPtr);
Tcl_MutexUnlock(&cwdMutex);
}
}
- return srcFsPathPtr->normPathPtr;
+ return fsPathPtr->normPathPtr;
}
/*
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.117
diff -u -r1.117 tclInt.h
--- generic/tclInt.h 4 Feb 2003 17:06:50 -0000 1.117
+++ generic/tclInt.h 7 Feb 2003 18:51:46 -0000
@@ -1717,6 +1717,8 @@
Tcl_StatBuf *buf));
EXTERN int TclpCheckStackSpace _ANSI_ARGS_((void));
EXTERN Tcl_Obj* TclpTempFileName _ANSI_ARGS_((void));
+EXTERN Tcl_Obj* TclNewFSPathObj _ANSI_ARGS_((Tcl_Obj *dirPtr,
+ CONST char *addStrRep, int len));
EXTERN int TclpDeleteFile _ANSI_ARGS_((CONST char *path));
EXTERN void TclpFinalizeCondition _ANSI_ARGS_((
Tcl_Condition *condPtr));
Index: unix/tclUnixFCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixFCmd.c,v
retrieving revision 1.26
diff -u -r1.26 tclUnixFCmd.c
--- unix/tclUnixFCmd.c 4 Feb 2003 17:06:52 -0000 1.26
+++ unix/tclUnixFCmd.c 7 Feb 2003 18:51:46 -0000
@@ -1652,7 +1652,6 @@
*
*---------------------------------------------------------------------------
*/
-
int
TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
Tcl_Interp *interp;
@@ -1667,10 +1666,24 @@
char normPath[MAXPATHLEN];
Tcl_DString ds;
CONST char *nativePath;
+ char *lastDir;
#endif
currentPathEndPosition = path + nextCheckpoint;
+#ifndef NO_REALPATH
+ /* For speed, try to get the entire path in one go */
+ lastDir = strrchr(currentPathEndPosition, '/');
+ if (lastDir != NULL) {
+ nativePath = Tcl_UtfToExternalDString(NULL, path, lastDir - path, &ds);
+ if (Realpath(nativePath, normPath) != NULL) {
+ nextCheckpoint = lastDir - path;
+ goto wholeStringOk;
+ }
+ }
+ /* Else do it the slow way */
+#endif
+
while (1) {
cur = *currentPathEndPosition;
if ((cur == '/') && (path != currentPathEndPosition)) {
@@ -1713,6 +1726,7 @@
nativePath = Tcl_UtfToExternalDString(NULL, path, nextCheckpoint, &ds);
if (Realpath(nativePath, normPath) != NULL) {
+ wholeStringOk:
/*
* Free up the native path and put in its place the
* converted, normalized path.
Index: unix/tclUnixFile.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixFile.c,v
retrieving revision 1.29
diff -u -r1.29 tclUnixFile.c
--- unix/tclUnixFile.c 9 Jan 2003 10:38:34 -0000 1.29
+++ unix/tclUnixFile.c 7 Feb 2003 18:51:46 -0000
@@ -353,7 +353,7 @@
}
if (typeOk) {
Tcl_ListObjAppendElement(interp, resultPtr,
- Tcl_NewStringObj(fname, Tcl_DStringLength(&dsOrig)));
+ TclNewFSPathObj(pathPtr, utf, Tcl_DStringLength(&utfDs)));
}
}
Tcl_DStringFree(&utfDs);
Index: win/tclWinFile.c
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tclWinFile.c,v
retrieving revision 1.42
diff -u -r1.42 tclWinFile.c
--- win/tclWinFile.c 7 Feb 2003 15:29:34 -0000 1.42
+++ win/tclWinFile.c 7 Feb 2003 18:51:47 -0000
@@ -860,11 +860,8 @@
*/
Tcl_DStringAppend(&dsOrig, name, -1);
- Tcl_DStringFree(&ds);
fullname = Tcl_DStringValue(&dsOrig);
- nativeName = Tcl_WinUtfToTChar(fullname,
- Tcl_DStringLength(&dsOrig), &ds);
if (checkDrive) {
isDrive = WinIsDrive(fullname, Tcl_DStringLength(&dsOrig));
@@ -873,7 +870,7 @@
}
if (NativeMatchType(isDrive, attr, nativeName, types)) {
Tcl_ListObjAppendElement(interp, resultPtr,
- Tcl_NewStringObj(fullname, Tcl_DStringLength(&dsOrig)));
+ TclNewFSPathObj(pathPtr, name, Tcl_DStringLength(&ds)));
}
/*
* Free ds here to ensure that nativeName is valid above.