summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpooryorick <com.digitalsmarties@pooryorick.com>2018-05-05 17:04:08 (GMT)
committerpooryorick <com.digitalsmarties@pooryorick.com>2018-05-05 17:04:08 (GMT)
commite383cd7b266a40eadc6cb39fc9b38b8ebae91834 (patch)
treeb296d02849a41ef7aa58e8749d93e0042cf23142
parent096a011c439f5f857ba14ba0827fcead0572233e (diff)
parent3a3fd2954f11d13424d941346c5ede39c3bf175f (diff)
downloadtcl-e383cd7b266a40eadc6cb39fc9b38b8ebae91834.zip
tcl-e383cd7b266a40eadc6cb39fc9b38b8ebae91834.tar.gz
tcl-e383cd7b266a40eadc6cb39fc9b38b8ebae91834.tar.bz2
Avoid generating string representation when comparing the empty string.
-rw-r--r--generic/tclExecute.c56
-rw-r--r--generic/tclInt.h10
-rw-r--r--generic/tclStringObj.c45
3 files changed, 106 insertions, 5 deletions
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index af44323..728a847 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -5440,22 +5440,58 @@ TEBCresume(
}
} else {
/*
- * strcmp can't do a simple memcmp in order to handle the
- * special Tcl \xC0\x80 null encoding for utf-8.
+ * In order to handle the special Tcl \xC0\x80 null encoding
+ * for utf-8, strcmp can't do a simple memcmp.
*/
- s1 = TclGetStringFromObj(valuePtr, &s1len);
- s2 = TclGetStringFromObj(value2Ptr, &s2len);
+ {
+ int empty;
+ if ((empty = TclCheckEmptyString(valuePtr)) > 0) {
+ switch (TclCheckEmptyString(value2Ptr)) {
+ case -1:
+ s1 = "";
+ s1len = 0;
+ s2 = TclGetStringFromObj(value2Ptr, &s2len);
+ break;
+ case 0:
+ match = -1;
+ goto matchdone;
+ case 1:
+ match = 0;
+ goto matchdone;
+ }
+ } else if (TclCheckEmptyString(value2Ptr) > 0) {
+ switch (empty) {
+ case -1:
+ s2 = "";
+ s2len = 0;
+ s1 = TclGetStringFromObj(valuePtr, &s1len);
+ break;
+ case 0:
+ match = 1;
+ goto matchdone;
+ case 1:
+ match = 0;
+ goto matchdone;
+ }
+ } else {
+ s1 = TclGetStringFromObj(valuePtr, &s1len);
+ s2 = TclGetStringFromObj(value2Ptr, &s2len);
+ }
+ }
+
+
if (checkEq) {
memCmpFn = memcmp;
} else {
memCmpFn = (memCmpFn_t) TclpUtfNcmp2;
}
+
}
if (checkEq && (s1len != s2len)) {
match = 1;
- } else {
+ } else {
/*
* The comparison function should compare up to the minimum
* byte length only.
@@ -5468,6 +5504,8 @@ TEBCresume(
}
}
+ matchdone:
+
/*
* Make sure only -1,0,1 is returned
* TODO: consider peephole opt.
@@ -6142,6 +6180,14 @@ TEBCresume(
value2Ptr = OBJ_AT_TOS;
valuePtr = OBJ_UNDER_TOS;
+ /*
+ Try to determine, without triggering generation of a string
+ representation, whether one value is not a number.
+ */
+ if (TclCheckEmptyString(valuePtr) > 0 || TclCheckEmptyString(value2Ptr) > 0) {
+ goto stringCompare;
+ }
+
if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK
|| GetNumberFromObj(NULL, value2Ptr, &ptr2, &type2) != TCL_OK) {
/*
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 4db4576..4bdaf58 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2735,6 +2735,10 @@ MODULE_SCOPE long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS];
MODULE_SCOPE char * tclEmptyStringRep;
MODULE_SCOPE char tclEmptyString;
+enum CheckEmptyStringResult {
+ TCL_EMPTYSTRING_UNKNOWN = -1, TCL_EMPTYSTRING_NO, TCL_EMPTYSTRING_YES
+};
+
/*
*----------------------------------------------------------------
* Procedures shared among Tcl modules but not used by the outside world,
@@ -2875,6 +2879,7 @@ MODULE_SCOPE int TclCheckArrayTraces(Tcl_Interp *interp, Var *varPtr,
Var *arrayPtr, Tcl_Obj *name, int index);
MODULE_SCOPE int TclCheckBadOctal(Tcl_Interp *interp,
const char *value);
+MODULE_SCOPE int TclCheckEmptyString(Tcl_Obj *objPtr);
MODULE_SCOPE int TclChanCaughtErrorBypass(Tcl_Interp *interp,
Tcl_Channel chan);
MODULE_SCOPE Tcl_ObjCmdProc TclChannelNamesCmd;
@@ -4455,6 +4460,11 @@ MODULE_SCOPE void TclDbInitNewObj(Tcl_Obj *objPtr, const char *file,
#define TclIsPureByteArray(objPtr) \
(((objPtr)->typePtr==&tclByteArrayType) && ((objPtr)->bytes==NULL))
+#define TclIsPureDict(objPtr) \
+ (((objPtr)->bytes==NULL) && ((objPtr)->typePtr==&tclDictType))
+
+#define TclIsPureList(objPtr) \
+ (((objPtr)->bytes==NULL) && ((objPtr)->typePtr==&tclListType))
/*
*----------------------------------------------------------------
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index 1b35c56..a503392 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -434,6 +434,7 @@ Tcl_GetCharLength(
return length;
}
+
/*
* OK, need to work with the object as a string.
*/
@@ -464,6 +465,50 @@ Tcl_GetCharLength(
}
return numChars;
}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCheckEmptyString --
+ *
+ * Determine whether the string value of an object is or would be the
+ * empty string, without generating a string representation.
+ *
+ * Results:
+ * Returns 1 if empty, 0 if not, and -1 if unknown.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclCheckEmptyString (
+ Tcl_Obj *objPtr
+) {
+ int length = -1;
+
+ if (objPtr->bytes == tclEmptyStringRep) {
+ return TCL_EMPTYSTRING_YES;
+ }
+
+ if (TclIsPureList(objPtr)) {
+ Tcl_ListObjLength(NULL, objPtr, &length);
+ return length == 0;
+ }
+
+ if (TclIsPureDict(objPtr)) {
+ Tcl_DictObjSize(NULL, objPtr, &length);
+ return length == 0;
+ }
+
+ if (objPtr->bytes == NULL) {
+ return TCL_EMPTYSTRING_UNKNOWN;
+ }
+ return objPtr->length == 0;
+}
/*
*----------------------------------------------------------------------