diff options
Diffstat (limited to 'generic/tclCmdMZ.c')
| -rw-r--r-- | generic/tclCmdMZ.c | 652 |
1 files changed, 625 insertions, 27 deletions
diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index dac82b8..6792378 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -17,6 +17,7 @@ */ #include "tclInt.h" +#include "tclCompile.h" #include "tclRegexp.h" #include "tclStringTrim.h" @@ -1221,8 +1222,8 @@ Tcl_SplitObjCmd( fullchar = ch; #if TCL_UTF_MAX <= 4 - if (!len) { - len += TclUtfToUniChar(stringPtr, &ch); + if ((ch >= 0xD800) && (len < 3)) { + len += TclUtfToUniChar(stringPtr + len, &ch); fullchar = (((fullchar & 0x3ff) << 10) | (ch & 0x3ff)) + 0x10000; } #endif @@ -1444,11 +1445,11 @@ StringIndexCmd( Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(&uch, 1)); } else { - char buf[4]; + char buf[4] = ""; length = Tcl_UniCharToUtf(ch, buf); - if (!length) { - length = Tcl_UniCharToUtf(-1, buf); + if ((ch >= 0xD800) && (length < 3)) { + length += Tcl_UniCharToUtf(-1, buf + length); } Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, length)); } @@ -1459,6 +1460,63 @@ StringIndexCmd( /* *---------------------------------------------------------------------- * + * StringInsertCmd -- + * + * This procedure is invoked to process the "string insert" Tcl command. + * See the user documentation for details on what it does. Note that this + * command only functions correctly on properly formed Tcl UTF strings. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +StringInsertCmd( + ClientData dummy, /* Not used */ + Tcl_Interp *interp, /* Current interpreter */ + int objc, /* Number of arguments */ + Tcl_Obj *const objv[]) /* Argument objects */ +{ + int length; /* String length */ + int index; /* Insert index */ + Tcl_Obj *outObj; /* Output object */ + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "string index insertString"); + return TCL_ERROR; + } + + length = Tcl_GetCharLength(objv[1]); + if (TclGetIntForIndexM(interp, objv[2], length, &index) != TCL_OK) { + return TCL_ERROR; + } + + if (index < 0) { + index = 0; + } + if (index > length) { + index = length; + } + + outObj = TclStringReplace(interp, objv[1], index, 0, objv[3], + TCL_STRING_IN_PLACE); + + if (outObj != NULL) { + Tcl_SetObjResult(interp, outObj); + return TCL_OK; + } + + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * * StringIsCmd -- * * This procedure is invoked to process the "string is" Tcl command. See @@ -1571,7 +1629,7 @@ StringIsCmd( case STR_IS_BOOL: case STR_IS_TRUE: case STR_IS_FALSE: - if ((objPtr->typePtr != &tclBooleanType) + if (!TclHasIntRep(objPtr, &tclBooleanType) && (TCL_OK != TclSetBooleanFromAny(NULL, objPtr))) { if (strict) { result = 0; @@ -1641,9 +1699,9 @@ StringIsCmd( chcomp = Tcl_UniCharIsDigit; break; case STR_IS_DOUBLE: { - if ((objPtr->typePtr == &tclDoubleType) || - (objPtr->typePtr == &tclIntType) || - (objPtr->typePtr == &tclBignumType)) { + if (TclHasIntRep(objPtr, &tclDoubleType) || + TclHasIntRep(objPtr, &tclIntType) || + TclHasIntRep(objPtr, &tclBignumType)) { break; } string1 = TclGetStringFromObj(objPtr, &length1); @@ -1672,8 +1730,8 @@ StringIsCmd( break; case STR_IS_INT: case STR_IS_ENTIER: - if ((objPtr->typePtr == &tclIntType) || - (objPtr->typePtr == &tclBignumType)) { + if (TclHasIntRep(objPtr, &tclIntType) || + TclHasIntRep(objPtr, &tclBignumType)) { break; } string1 = TclGetStringFromObj(objPtr, &length1); @@ -1854,8 +1912,8 @@ StringIsCmd( length2 = TclUtfToUniChar(string1, &ch); fullchar = ch; #if TCL_UTF_MAX <= 4 - if (!length2) { - length2 = TclUtfToUniChar(string1, &ch); + if ((ch >= 0xD800) && (length2 < 3)) { + length2 += TclUtfToUniChar(string1 + length2, &ch); fullchar = (((fullchar & 0x3ff) << 10) | (ch & 0x3ff)) + 0x10000; } #endif @@ -1935,7 +1993,7 @@ StringMapCmd( const char *string = TclGetStringFromObj(objv[1], &length2); if ((length2 > 1) && - strncmp(string, "-nocase", (size_t) length2) == 0) { + strncmp(string, "-nocase", length2) == 0) { nocase = 1; } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -1952,7 +2010,7 @@ StringMapCmd( */ if (!TclHasStringRep(objv[objc-2]) - && (objv[objc-2]->typePtr == &tclDictType)){ + && TclHasIntRep(objv[objc-2], &tclDictType)) { int i, done; Tcl_DictSearch search; @@ -2114,7 +2172,7 @@ StringMapCmd( (Tcl_UniCharToLower(*ustring1) == u2lc[index/2]))) && /* Restrict max compare length. */ (end-ustring1 >= length2) && ((length2 == 1) || - !strCmpFn(ustring2, ustring1, (unsigned) length2))) { + !strCmpFn(ustring2, ustring1, length2))) { if (p != ustring1) { /* * Put the skipped chars onto the result first. @@ -2203,7 +2261,7 @@ StringMatchCmd( const char *string = TclGetStringFromObj(objv[1], &length); if ((length > 1) && - strncmp(string, "-nocase", (size_t) length) == 0) { + strncmp(string, "-nocase", length) == 0) { nocase = TCL_MATCH_NOCASE; } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -2366,15 +2424,16 @@ StringRplcCmd( end = length - 1; if (TclGetIntForIndexM(interp, objv[2], end, &first) != TCL_OK || - TclGetIntForIndexM(interp, objv[3], end, &last) != TCL_OK){ + TclGetIntForIndexM(interp, objv[3], end, &last) != TCL_OK) { return TCL_ERROR; } /* - * The following test screens out most empty substrings as - * candidates for replacement. When they are detected, no - * replacement is done, and the result is the original string, + * The following test screens out most empty substrings as candidates for + * replacement. When they are detected, no replacement is done, and the + * result is the original string. */ + if ((last < 0) || /* Range ends before start of string */ (first > end) || /* Range begins after end of string */ (last < first)) { /* Range begins after it starts */ @@ -2384,6 +2443,7 @@ StringRplcCmd( * have (first <= end < 0 <= last) and an empty string is permitted * to be replaced. */ + Tcl_SetObjResult(interp, objv[1]); } else { Tcl_Obj *resultPtr; @@ -2605,10 +2665,10 @@ StringEqualCmd( for (i = 1; i < objc-2; i++) { string2 = TclGetStringFromObj(objv[i], &length); - if ((length > 1) && !strncmp(string2, "-nocase", (size_t)length)) { + if ((length > 1) && !strncmp(string2, "-nocase", length)) { nocase = 1; } else if ((length > 1) - && !strncmp(string2, "-length", (size_t)length)) { + && !strncmp(string2, "-length", length)) { if (i+1 >= objc-2) { goto str_cmp_args; } @@ -2703,10 +2763,10 @@ TclStringCmpOpts( for (i = 1; i < objc-2; i++) { string = TclGetStringFromObj(objv[i], &length); - if ((length > 1) && !strncmp(string, "-nocase", (size_t)length)) { + if ((length > 1) && !strncmp(string, "-nocase", length)) { *nocase = 1; } else if ((length > 1) - && !strncmp(string, "-length", (size_t)length)) { + && !strncmp(string, "-length", length)) { if (i+1 >= objc-2) { goto str_cmp_args; } @@ -3268,6 +3328,7 @@ TclInitStringCmd( {"equal", StringEqualCmd, TclCompileStringEqualCmd, NULL, NULL, 0}, {"first", StringFirstCmd, TclCompileStringFirstCmd, NULL, NULL, 0}, {"index", StringIndexCmd, TclCompileStringIndexCmd, NULL, NULL, 0}, + {"insert", StringInsertCmd, TclCompileStringInsertCmd, NULL, NULL, 0}, {"is", StringIsCmd, TclCompileStringIsCmd, NULL, NULL, 0}, {"last", StringLastCmd, TclCompileStringLastCmd, NULL, NULL, 0}, {"length", StringLenCmd, TclCompileStringLenCmd, NULL, NULL, 0}, @@ -3564,7 +3625,7 @@ TclNRSwitchObjCmd( Tcl_Obj **listv; blist = objv[0]; - if (TclListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK){ + if (TclListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) { return TCL_ERROR; } @@ -4030,7 +4091,7 @@ Tcl_TimeObjCmd( start = TclpGetWideClicks(); #endif while (i-- > 0) { - result = Tcl_EvalObjEx(interp, objPtr, 0); + result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); if (result != TCL_OK) { return result; } @@ -4070,6 +4131,543 @@ Tcl_TimeObjCmd( /* *---------------------------------------------------------------------- * + * Tcl_TimeRateObjCmd -- + * + * This object-based procedure is invoked to process the "timerate" Tcl + * command. + * + * This is similar to command "time", except the execution limited by + * given time (in milliseconds) instead of repetition count. + * + * Example: + * timerate {after 5} 1000; # equivalent to: time {after 5} [expr 1000/5] + * + * Results: + * A standard Tcl object result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_TimeRateObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + static double measureOverhead = 0; + /* global measure-overhead */ + double overhead = -1; /* given measure-overhead */ + register Tcl_Obj *objPtr; + register int result, i; + Tcl_Obj *calibrate = NULL, *direct = NULL; + Tcl_WideUInt count = 0; /* Holds repetition count */ + Tcl_WideInt maxms = WIDE_MIN; + /* Maximal running time (in milliseconds) */ + Tcl_WideUInt maxcnt = WIDE_MAX; + /* Maximal count of iterations. */ + Tcl_WideUInt threshold = 1; /* Current threshold for check time (faster + * repeat count without time check) */ + Tcl_WideUInt maxIterTm = 1; /* Max time of some iteration as max + * threshold, additionally avoiding divide to + * zero (i.e., never < 1) */ + unsigned short factor = 50; /* Factor (4..50) limiting threshold to avoid + * growth of execution time. */ + register Tcl_WideInt start, middle, stop; +#ifndef TCL_WIDE_CLICKS + Tcl_Time now; +#endif /* !TCL_WIDE_CLICKS */ + static const char *const options[] = { + "-direct", "-overhead", "-calibrate", "--", NULL + }; + enum options { + TMRT_EV_DIRECT, TMRT_OVERHEAD, TMRT_CALIBRATE, TMRT_LAST + }; + NRE_callback *rootPtr; + ByteCode *codePtr = NULL; + + for (i = 1; i < objc - 1; i++) { + int index; + + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", TCL_EXACT, + &index) != TCL_OK) { + break; + } + if (index == TMRT_LAST) { + i++; + break; + } + switch (index) { + case TMRT_EV_DIRECT: + direct = objv[i]; + break; + case TMRT_OVERHEAD: + if (++i >= objc - 1) { + goto usage; + } + if (Tcl_GetDoubleFromObj(interp, objv[i], &overhead) != TCL_OK) { + return TCL_ERROR; + } + break; + case TMRT_CALIBRATE: + calibrate = objv[i]; + break; + } + } + + if (i >= objc || i < objc - 3) { + usage: + Tcl_WrongNumArgs(interp, 1, objv, + "?-direct? ?-calibrate? ?-overhead double? " + "command ?time ?max-count??"); + return TCL_ERROR; + } + objPtr = objv[i++]; + if (i < objc) { /* max-time */ + result = Tcl_GetWideIntFromObj(interp, objv[i++], &maxms); + if (result != TCL_OK) { + return result; + } + if (i < objc) { /* max-count*/ + Tcl_WideInt v; + + result = Tcl_GetWideIntFromObj(interp, objv[i], &v); + if (result != TCL_OK) { + return result; + } + maxcnt = (v > 0) ? v : 0; + } + } + + /* + * If we are doing calibration. + */ + + if (calibrate) { + /* + * If no time specified for the calibration. + */ + + if (maxms == WIDE_MIN) { + Tcl_Obj *clobjv[6]; + Tcl_WideInt maxCalTime = 5000; + double lastMeasureOverhead = measureOverhead; + + clobjv[0] = objv[0]; + i = 1; + if (direct) { + clobjv[i++] = direct; + } + clobjv[i++] = objPtr; + + /* + * Reset last measurement overhead. + */ + + measureOverhead = (double) 0; + + /* + * Self-call with 100 milliseconds to warm-up, before entering the + * calibration cycle. + */ + + TclNewIntObj(clobjv[i], 100); + Tcl_IncrRefCount(clobjv[i]); + result = Tcl_TimeRateObjCmd(NULL, interp, i + 1, clobjv); + Tcl_DecrRefCount(clobjv[i]); + if (result != TCL_OK) { + return result; + } + + i--; + clobjv[i++] = calibrate; + clobjv[i++] = objPtr; + + /* + * Set last measurement overhead to max. + */ + + measureOverhead = (double) UWIDE_MAX; + + /* + * Run the calibration cycle until it is more precise. + */ + + maxms = -1000; + do { + lastMeasureOverhead = measureOverhead; + TclNewIntObj(clobjv[i], (int) maxms); + Tcl_IncrRefCount(clobjv[i]); + result = Tcl_TimeRateObjCmd(NULL, interp, i + 1, clobjv); + Tcl_DecrRefCount(clobjv[i]); + if (result != TCL_OK) { + return result; + } + maxCalTime += maxms; + + /* + * Increase maxms for more precise calibration. + */ + + maxms -= -maxms / 4; + + /* + * As long as new value more as 0.05% better + */ + } while ((measureOverhead >= lastMeasureOverhead + || measureOverhead / lastMeasureOverhead <= 0.9995) + && maxCalTime > 0); + + return result; + } + if (maxms == 0) { + /* + * Reset last measurement overhead + */ + + measureOverhead = 0; + Tcl_SetObjResult(interp, Tcl_NewLongObj(0)); + return TCL_OK; + } + + /* + * If time is negative, make current overhead more precise. + */ + + if (maxms > 0) { + /* + * Set last measurement overhead to max. + */ + + measureOverhead = (double) UWIDE_MAX; + } else { + maxms = -maxms; + } + } + + if (maxms == WIDE_MIN) { + maxms = 1000; + } + if (overhead == -1) { + overhead = measureOverhead; + } + + /* + * Ensure that resetting of result will not smudge the further + * measurement. + */ + + Tcl_ResetResult(interp); + + /* + * Compile object if needed. + */ + + if (!direct) { + if (TclInterpReady(interp) != TCL_OK) { + return TCL_ERROR; + } + codePtr = TclCompileObj(interp, objPtr, NULL, 0); + TclPreserveByteCode(codePtr); + } + + /* + * Get start and stop time. + */ + +#ifdef TCL_WIDE_CLICKS + start = middle = TclpGetWideClicks(); + + /* + * Time to stop execution (in wide clicks). + */ + + stop = start + (maxms * 1000 / TclpWideClickInMicrosec()); +#else + Tcl_GetTime(&now); + start = now.sec; + start *= 1000000; + start += now.usec; + middle = start; + + /* + * Time to stop execution (in microsecs). + */ + + stop = start + maxms * 1000; +#endif /* TCL_WIDE_CLICKS */ + + /* + * Start measurement. + */ + + if (maxcnt > 0) { + while (1) { + /* + * Evaluate a single iteration. + */ + + count++; + if (!direct) { /* precompiled */ + rootPtr = TOP_CB(interp); + result = TclNRExecuteByteCode(interp, codePtr); + result = TclNRRunCallbacks(interp, result, rootPtr); + } else { /* eval */ + result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); + } + if (result != TCL_OK) { + /* + * Allow break from measurement cycle (used for conditional + * stop). + */ + + if (result != TCL_BREAK) { + goto done; + } + + /* + * Force stop immediately. + */ + + threshold = 1; + maxcnt = 0; + result = TCL_OK; + } + + /* + * Don't check time up to threshold. + */ + + if (--threshold > 0) { + continue; + } + + /* + * Check stop time reached, estimate new threshold. + */ + +#ifdef TCL_WIDE_CLICKS + middle = TclpGetWideClicks(); +#else + Tcl_GetTime(&now); + middle = now.sec; + middle *= 1000000; + middle += now.usec; +#endif /* TCL_WIDE_CLICKS */ + + if (middle >= stop || count >= maxcnt) { + break; + } + + /* + * Don't calculate threshold by few iterations, because sometimes + * first iteration(s) can be too fast or slow (cached, delayed + * clean up, etc). + */ + + if (count < 10) { + threshold = 1; + continue; + } + + /* + * Average iteration time in microsecs. + */ + + threshold = (middle - start) / count; + if (threshold > maxIterTm) { + maxIterTm = threshold; + + /* + * Iterations seem to be longer. + */ + + if (threshold > maxIterTm * 2) { + factor *= 2; + if (factor > 50) { + factor = 50; + } + } else { + if (factor < 50) { + factor++; + } + } + } else if (factor > 4) { + /* + * Iterations seem to be shorter. + */ + + if (threshold < (maxIterTm / 2)) { + factor /= 2; + if (factor < 4) { + factor = 4; + } + } else { + factor--; + } + } + + /* + * As relation between remaining time and time since last check, + * maximal some % of time (by factor), so avoid growing of the + * execution time if iterations are not consistent, e.g. was + * continuously on time). + */ + + threshold = ((stop - middle) / maxIterTm) / factor + 1; + if (threshold > 100000) { /* fix for too large threshold */ + threshold = 100000; + } + + /* + * Consider max-count + */ + + if (threshold > maxcnt - count) { + threshold = maxcnt - count; + } + } + } + + { + Tcl_Obj *objarr[8], **objs = objarr; + Tcl_WideInt val; + int digits; + + middle -= start; /* execution time in microsecs */ + +#ifdef TCL_WIDE_CLICKS + /* + * convert execution time in wide clicks to microsecs. + */ + + middle *= TclpWideClickInMicrosec(); +#endif /* TCL_WIDE_CLICKS */ + + if (!count) { /* no iterations - avoid divide by zero */ + objs[0] = objs[2] = objs[4] = Tcl_NewWideIntObj(0); + goto retRes; + } + + /* + * If not calibrating... + */ + + if (!calibrate) { + /* + * Minimize influence of measurement overhead. + */ + + if (overhead > 0) { + /* + * Estimate the time of overhead (microsecs). + */ + + Tcl_WideUInt curOverhead = overhead * count; + + if (middle > (Tcl_WideInt) curOverhead) { + middle -= curOverhead; + } else { + middle = 0; + } + } + } else { + /* + * Calibration: obtaining new measurement overhead. + */ + + if (measureOverhead > ((double) middle) / count) { + measureOverhead = ((double) middle) / count; + } + objs[0] = Tcl_NewDoubleObj(measureOverhead); + TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#-overhead"); /* mics */ + objs += 2; + } + + val = middle / count; /* microsecs per iteration */ + if (val >= 1000000) { + objs[0] = Tcl_NewWideIntObj(val); + } else { + if (val < 10) { + digits = 6; + } else if (val < 100) { + digits = 4; + } else if (val < 1000) { + digits = 3; + } else if (val < 10000) { + digits = 2; + } else { + digits = 1; + } + objs[0] = Tcl_ObjPrintf("%.*f", digits, ((double) middle)/count); + } + + objs[2] = Tcl_NewWideIntObj(count); /* iterations */ + + /* + * Calculate speed as rate (count) per sec + */ + + if (!middle) { + middle++; /* Avoid divide by zero. */ + } + if (count < (WIDE_MAX / 1000000)) { + val = (count * 1000000) / middle; + if (val < 100000) { + if (val < 100) { + digits = 3; + } else if (val < 1000) { + digits = 2; + } else { + digits = 1; + } + objs[4] = Tcl_ObjPrintf("%.*f", + digits, ((double) (count * 1000000)) / middle); + } else { + objs[4] = Tcl_NewWideIntObj(val); + } + } else { + objs[4] = Tcl_NewWideIntObj((count / middle) * 1000000); + } + + retRes: + /* + * Estimated net execution time (in millisecs). + */ + + if (!calibrate) { + if (middle >= 1) { + objs[6] = Tcl_ObjPrintf("%.3f", (double)middle / 1000); + } else { + objs[6] = Tcl_NewWideIntObj(0); + } + TclNewLiteralStringObj(objs[7], "nett-ms"); + } + + /* + * Construct the result as a list because many programs have always + * parsed as such (extracting the first element, typically). + */ + + TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#"); /* mics/# */ + TclNewLiteralStringObj(objs[3], "#"); + TclNewLiteralStringObj(objs[5], "#/sec"); + Tcl_SetObjResult(interp, Tcl_NewListObj(8, objarr)); + } + + done: + if (codePtr != NULL) { + TclReleaseByteCode(codePtr); + } + return result; +} + +/* + *---------------------------------------------------------------------- + * * Tcl_TryObjCmd, TclNRTryObjCmd -- * * This procedure is invoked to process the "try" Tcl command. See the |
