Subject: cleaner and faster 'string trim'

From: dkf <[email-masked]>
Date: Thu, 3 Oct 2013 13:32:06 +0000

---
 generic/tclAssembly.c   |  3 ++-
 generic/tclCompCmdsSZ.c | 24 ++----------------------
 generic/tclCompile.c    |  5 +++++
 generic/tclCompile.h    |  7 ++++---
 generic/tclExecute.c    | 34 ++++++++++++++++++++++++++++------
 5 files changed, 41 insertions(+), 32 deletions(-)

diff --git a/generic/tclAssembly.c b/generic/tclAssembly.c
index 659f483..44cddba 100644
--- a/generic/tclAssembly.c
+++ b/generic/tclAssembly.c
@@ -462,6 +462,7 @@ static const TalInstDesc TalInstructionTable[] = {
     {"strneq",		ASSEM_1BYTE,	INST_STR_NEQ,		2,	1},
     {"strrange",	ASSEM_1BYTE,	INST_STR_RANGE,		3,	1},
     {"strrfind",	ASSEM_1BYTE,	INST_STR_FIND_LAST,	2,	1},
+    {"strtrim",		ASSEM_1BYTE,	INST_STRTRIM,		2,	1},
     {"strtrimLeft",	ASSEM_1BYTE,	INST_STRTRIM_LEFT,	2,	1},
     {"strtrimRight",	ASSEM_1BYTE,	INST_STRTRIM_RIGHT,	2,	1},
     {"sub",		ASSEM_1BYTE,	INST_SUB,		2,	1},
@@ -505,7 +506,7 @@ static const unsigned char NonThrowingByteCodes[] = {
     INST_NS_CURRENT,						/* 151 */
     INST_INFO_LEVEL_NUM,					/* 152 */
     INST_RESOLVE_COMMAND,					/* 154 */
-    INST_STRTRIM_LEFT, INST_STRTRIM_RIGHT			/* 166,167 */
+    INST_STRTRIM, INST_STRTRIM_LEFT, INST_STRTRIM_RIGHT		/* 166-168 */
 };
 
 /*
diff --git a/generic/tclCompCmdsSZ.c b/generic/tclCompCmdsSZ.c
index 0177b2d..12f6167 100644
--- a/generic/tclCompCmdsSZ.c
+++ b/generic/tclCompCmdsSZ.c
@@ -739,7 +739,6 @@ TclCompileStringTrimCmd(
 {
     DefineLineInformation;	/* TIP #280 */
     Tcl_Token *tokenPtr;
-    Tcl_Obj *objPtr;
 
     if (parsePtr->numWords != 2 && parsePtr->numWords != 3) {
 	return TCL_ERROR;
@@ -749,30 +748,11 @@ TclCompileStringTrimCmd(
     CompileWord(envPtr, tokenPtr,			interp, 1);
     if (parsePtr->numWords == 3) {
 	tokenPtr = TokenAfter(tokenPtr);
-	TclNewObj(objPtr);
-	if (TclWordKnownAtCompileTime(tokenPtr, objPtr)) {
-	    int len;
-	    const char *p = Tcl_GetStringFromObj(objPtr, &len);
-
-	    PushLiteral(envPtr, p, len);
-	    OP(			STRTRIM_LEFT);
-	    PushLiteral(envPtr, p, len);
-	    OP(			STRTRIM_RIGHT);
-	} else {
-	    CompileWord(envPtr, tokenPtr,		interp, 2);
-	    OP4(		REVERSE, 2);
-	    OP4(		OVER, 1);
-	    OP(			STRTRIM_LEFT);
-	    OP4(		REVERSE, 2);
-	    OP(			STRTRIM_RIGHT);
-	}
-	TclDecrRefCount(objPtr);
+	CompileWord(envPtr, tokenPtr,			interp, 2);
     } else {
 	PushLiteral(envPtr, DEFAULT_TRIM_SET, strlen(DEFAULT_TRIM_SET));
-	OP(			STRTRIM_LEFT);
-	PushLiteral(envPtr, DEFAULT_TRIM_SET, strlen(DEFAULT_TRIM_SET));
-	OP(			STRTRIM_RIGHT);
     }
+    OP(			STRTRIM);
     return TCL_OK;
 }
 
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index cdedbda..7e72d84 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -545,6 +545,11 @@ InstructionDesc const tclInstructionTable[] = {
 	/* Drops an element from the auxiliary stack, popping stack elements
 	 * until the matching stack depth is reached. */
 
+    {"strtrim",		 1,	-1,	  0,	{OPERAND_NONE}},
+	/* [string trim] core: removes the characters (designated by the value
+	 * at the top of the stack) from both ends of the string and pushes
+	 * the resulting string.
+	 * Stack: ... string charset => ... trimmedString */
     {"strtrimLeft",	 1,	-1,	  0,	{OPERAND_NONE}},
 	/* [string trimleft] core: removes the characters (designated by the
 	 * value at the top of the stack) from the left of the string and
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index 08eb393..fa8d773 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -769,11 +769,12 @@ typedef struct ByteCode {
 #define INST_EXPAND_DROP		165
 
 /* For compilation of [string trim] and related */
-#define INST_STRTRIM_LEFT		166
-#define INST_STRTRIM_RIGHT		167
+#define INST_STRTRIM			166
+#define INST_STRTRIM_LEFT		167
+#define INST_STRTRIM_RIGHT		168
 
 /* The last opcode */
-#define LAST_INST_OPCODE		167
+#define LAST_INST_OPCODE		168
 
 /*
  * Table describing the Tcl bytecode instructions: their name (for displaying
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index 83f68fd..8470389 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -5258,19 +5258,41 @@ TEBCresume(
 
     {
 	const char *string1, *string2;
+	int trim1, trim2;
 
+    case INST_STRTRIM:
+	valuePtr = OBJ_UNDER_TOS;	/* String */
+	value2Ptr = OBJ_AT_TOS;		/* TrimSet */
+	string2 = TclGetStringFromObj(value2Ptr, &length2);
+	string1 = TclGetStringFromObj(valuePtr, &length);
+	trim1 = TclTrimLeft(string1, length, string2, length2);
+	if (trim1 < length) {
+	    trim2 = TclTrimRight(string1, length, string2, length2);
+	} else {
+	    trim2 = 0;
+	}
+	if (trim1 == 0 && trim2 == 0) {
+	    TRACE_WITH_OBJ(("\"%.30s\" \"%.30s\" => ", valuePtr, value2Ptr),
+		    valuePtr);
+	    NEXT_INST_F(1, 1, 0);
+	} else {
+	    objResultPtr = Tcl_NewStringObj(string1+trim1, length-trim1-trim2);
+	    TRACE_WITH_OBJ(("\"%.30s\" \"%.30s\" => ", valuePtr, value2Ptr),
+		    objResultPtr);
+	    NEXT_INST_F(1, 2, 1);
+	}
     case INST_STRTRIM_LEFT:
 	valuePtr = OBJ_UNDER_TOS;	/* String */
 	value2Ptr = OBJ_AT_TOS;		/* TrimSet */
 	string2 = TclGetStringFromObj(value2Ptr, &length2);
 	string1 = TclGetStringFromObj(valuePtr, &length);
-	match = TclTrimLeft(string1, length, string2, length2);
-	if (match == 0) {
+	trim1 = TclTrimLeft(string1, length, string2, length2);
+	if (trim1 == 0) {
 	    TRACE_WITH_OBJ(("\"%.30s\" \"%.30s\" => ", valuePtr, value2Ptr),
 		    valuePtr);
 	    NEXT_INST_F(1, 1, 0);
 	} else {
-	    objResultPtr = Tcl_NewStringObj(string1+match, length-match);
+	    objResultPtr = Tcl_NewStringObj(string1+trim1, length-trim1);
 	    TRACE_WITH_OBJ(("\"%.30s\" \"%.30s\" => ", valuePtr, value2Ptr),
 		    objResultPtr);
 	    NEXT_INST_F(1, 2, 1);
@@ -5280,11 +5302,11 @@ TEBCresume(
 	value2Ptr = OBJ_AT_TOS;		/* TrimSet */
 	string2 = TclGetStringFromObj(value2Ptr, &length2);
 	string1 = TclGetStringFromObj(valuePtr, &length);
-	match = TclTrimRight(string1, length, string2, length2);
-	if (match == 0) {
+	trim2 = TclTrimRight(string1, length, string2, length2);
+	if (trim2 == 0) {
 	    NEXT_INST_F(1, 1, 0);
 	} else {
-	    objResultPtr = Tcl_NewStringObj(string1, length-match);
+	    objResultPtr = Tcl_NewStringObj(string1, length-trim2);
 	    NEXT_INST_F(1, 2, 1);
 	}
     }
-- 
cgit v0.12