Index: generic/tclCmdMZ.c ================================================================== --- generic/tclCmdMZ.c +++ generic/tclCmdMZ.c @@ -256,11 +256,11 @@ objPtr = objv[1]; stringLength = Tcl_GetCharLength(objPtr); if (startIndex) { - TclGetIntForIndexM(NULL, startIndex, stringLength, &offset); + TclGetIntForIndexM(interp, startIndex, stringLength, &offset); Tcl_DecrRefCount(startIndex); if (offset < 0) { offset = 0; } } @@ -580,11 +580,11 @@ objv += idx; if (startIndex) { int stringLength = Tcl_GetCharLength(objv[1]); - TclGetIntForIndexM(NULL, startIndex, stringLength, &offset); + TclGetIntForIndexM(interp, startIndex, stringLength, &offset); Tcl_DecrRefCount(startIndex); if (offset < 0) { offset = 0; } } Index: generic/tclExecute.c ================================================================== --- generic/tclExecute.c +++ generic/tclExecute.c @@ -4759,11 +4759,11 @@ * Extract the desired list element. */ if ((TclListObjGetElements(interp, valuePtr, &objc, &objv) == TCL_OK) && (value2Ptr->typePtr != &tclListType) - && (TclGetIntForIndexM(NULL , value2Ptr, objc-1, + && (TclGetIntForIndexM(NULL, value2Ptr, objc-1, &index) == TCL_OK)) { TclDecrRefCount(value2Ptr); tosPtr--; pcAdjustment = 1; goto lindexFastPath; Index: generic/tclUtil.c ================================================================== --- generic/tclUtil.c +++ generic/tclUtil.c @@ -13,10 +13,11 @@ */ #include "tclInt.h" #include "tclParse.h" #include "tclStringTrim.h" +#include "tommath.h" #include /* * The absolute pathname of the executable in which this Tcl library is * running. @@ -105,13 +106,15 @@ */ static void ClearHash(Tcl_HashTable *tablePtr); static void FreeProcessGlobalValue(ClientData clientData); static void FreeThreadHash(ClientData clientData); -static int GetEndOffsetFromObj(Tcl_Obj *objPtr, int endValue, - int *indexPtr); +static int GetEndOffsetFromObj(Tcl_Obj *objPtr, + Tcl_WideInt endValue, Tcl_WideInt *indexPtr); static Tcl_HashTable * GetThreadHash(Tcl_ThreadDataKey *keyPtr); +static int GetWideForIndex(Tcl_Interp *interp, Tcl_Obj *objPtr, + Tcl_WideInt endValue, Tcl_WideInt *widePtr); static int SetEndOffsetFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static int FindElement(Tcl_Interp *interp, const char *string, int stringLength, const char *typeStr, const char *typeCode, const char **elementPtr, @@ -3652,10 +3655,208 @@ } /* *---------------------------------------------------------------------- * + * GetWideForIndex -- + * + * This function produces a wide integer value corresponding to the + * list index held in *objPtr. The parsing supports all values + * recognized as any size of integer, and the syntaxes end[-+]$integer + * and $integer[-+]$integer. The argument endValue is used to give + * the meaning of the literal index value "end". + * + * Results: + * When parsing of *objPtr successfully recognizes an index value, + * TCL_OK is returned, and the wide integer value corresponding to + * the recognized index value is written to *widePtr. When parsing + * fails, TCL_ERROR is returned and error information is written to + * interp, if non-NULL. + * + * Side effects: + * The type of *objPtr may change. + * + *---------------------------------------------------------------------- + */ + +static int +GetWideForIndex( + Tcl_Interp *interp, /* Interpreter to use for error reporting. If + * NULL, then no error message is left after + * errors. */ + Tcl_Obj *objPtr, /* Points to the value to be parsed */ + Tcl_WideInt endValue, /* The value to be stored at *widePtr if + * objPtr holds "end". + * NOTE: this value may be negative. */ + Tcl_WideInt *widePtr) /* Location filled in with a wide integer + * representing an index. */ +{ + ClientData cd; + const char *opPtr; + int numType, length, t1 = 0, t2 = 0; + int code = TclGetNumberFromObj(NULL, objPtr, &cd, &numType); + + if (code == TCL_OK) { + if (numType == TCL_NUMBER_WIDE) { + /* objPtr holds an integer in the signed wide range */ + *widePtr = (Tcl_WideInt)(*(Tcl_WideInt *)cd); + return TCL_OK; + } + if (numType == TCL_NUMBER_BIG) { + /* objPtr holds an integer outside the signed wide range */ + /* Truncate to the signed wide range. */ + if (mp_isneg((mp_int *)cd)) { + *widePtr = LLONG_MIN; + } else { + *widePtr = LLONG_MAX; + } + return TCL_OK; + } + /* Must be a double -> not a valid index */ + goto parseError; + } + + /* objPtr does not hold a number, check the end+/- format... */ + if (GetEndOffsetFromObj(objPtr, endValue, widePtr) == TCL_OK) { + return TCL_OK; + } + + /* If we reach here, the string rep of objPtr exists. */ + + /* + * The valid index syntax does not include any value that is + * a list of more than one element. This is necessary so that + * lists of index values can be reliably distinguished from any + * single index value. + */ + + /* + * Quick scan to see if multi-value list is even possible. + * This relies on TclGetString() returning a NUL-terminated string. + */ + if ((TclMaxListLength(TclGetString(objPtr), -1, NULL) > 1) + + /* If it's possible, do the full list parse. */ + && (TCL_OK == Tcl_ListObjLength(NULL, objPtr, &length)) + && (length > 1)) { + goto parseError; + } + + /* Passed the list screen, so parse for index arithmetic expression */ + if (TCL_OK == TclParseNumber(NULL, objPtr, NULL, NULL, -1, &opPtr, + TCL_PARSE_INTEGER_ONLY)) { + Tcl_WideInt w1=0, w2=0; + + /* value starts with valid integer... */ + + if ((*opPtr == '-') || (*opPtr == '+')) { + /* ... value continues with [-+] ... */ + + /* Save first integer as wide if possible */ + TclGetNumberFromObj(NULL, objPtr, &cd, &t1); + if (t1 == TCL_NUMBER_WIDE) { + w1 = (*(Tcl_WideInt *)cd); + } + + if (TCL_OK == TclParseNumber(NULL, objPtr, NULL, opPtr + 1, + -1, NULL, TCL_PARSE_INTEGER_ONLY)) { + /* ... value concludes with second valid integer */ + + /* Save second integer as wide if possible */ + TclGetNumberFromObj(NULL, objPtr, &cd, &t2); + if (t2 == TCL_NUMBER_WIDE) { + w2 = (*(Tcl_WideInt *)cd); + } + } + } + /* Clear invalid intreps left by TclParseNumber */ + TclFreeIntRep(objPtr); + + if (t1 && t2) { + /* We have both integer values */ + if ((t1 == TCL_NUMBER_WIDE) && (t2 == TCL_NUMBER_WIDE)) { + /* Both are wide, do wide-integer math */ + if (*opPtr == '-') { + if ((w2 == LLONG_MIN) && (interp != NULL)) { + goto extreme; + } + w2 = -w2; + } + + if ((w1 ^ w2) < 0) { + /* Different signs, sum cannot overflow */ + *widePtr = w1 + w2; + } else if (w1 >= 0) { + if (w1 < LLONG_MAX - w2) { + *widePtr = w1 + w2; + } else { + *widePtr = LLONG_MAX; + } + } else { + if (w1 > LLONG_MIN - w2) { + *widePtr = w1 + w2; + } else { + *widePtr = LLONG_MIN; + } + } + } else if (interp == NULL) { + /* + * We use an interp to do bignum index calculations. + * If we don't get one, call all indices with bignums errors, + * and rely on callers to handle it. + */ + return TCL_ERROR; + } else { + /* + * At least one is big, do bignum math. Little reason to + * value performance here. Re-use code. Parse has verified + * objPtr is an expresion. Compute it. + */ + + Tcl_Obj *sum; + + extreme: + Tcl_ExprObj(interp, objPtr, &sum); + TclGetNumberFromObj(NULL, sum, &cd, &numType); + + if (numType == TCL_NUMBER_WIDE) { + /* sum holds an integer in the signed wide range */ + *widePtr = (Tcl_WideInt)(*(Tcl_WideInt *)cd); + } else { + /* sum holds an integer outside the signed wide range */ + /* Truncate to the signed wide range. */ + if (mp_isneg((mp_int *)cd)) { + *widePtr = LLONG_MIN; + } else { + *widePtr = LLONG_MAX; + } + } + Tcl_DecrRefCount(sum); + } + return TCL_OK; + } + } + + /* Report a parse error. */ + parseError: + if (interp != NULL) { + char * bytes = TclGetString(objPtr); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad index \"%s\": must be integer?[+-]integer? or" + " end?[+-]integer?", bytes)); + if (!strncmp(bytes, "end-", 4)) { + bytes += 4; + } + TclCheckBadOctal(interp, bytes); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX", NULL); + } + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * * TclGetIntForIndex -- * * This function returns an integer corresponding to the list index held * in a Tcl object. The Tcl object's value is expected to be in the * format integer([+-]integer)? or the format end([+-]integer)?. @@ -3685,80 +3886,23 @@ int endValue, /* The value to be stored at "indexPtr" if * "objPtr" holds "end". */ int *indexPtr) /* Location filled in with an integer * representing an index. */ { - size_t length; - char *opPtr; - const char *bytes; - - if (TclGetIntFromObj(NULL, objPtr, indexPtr) == TCL_OK) { - return TCL_OK; - } - - if (GetEndOffsetFromObj(objPtr, endValue, indexPtr) == TCL_OK) { - return TCL_OK; - } - - bytes = TclGetString(objPtr); - length = objPtr->length; - - /* - * Leading whitespace is acceptable in an index. - */ - - while (length && TclIsSpaceProc(*bytes)) { - bytes++; - length--; - } - - if (TclParseNumber(NULL, NULL, NULL, bytes, length, (const char **)&opPtr, - TCL_PARSE_INTEGER_ONLY | TCL_PARSE_NO_WHITESPACE) == TCL_OK) { - int code, first, second; - char savedOp = *opPtr; - - if ((savedOp != '+') && (savedOp != '-')) { - goto parseError; - } - if (TclIsSpaceProc(opPtr[1])) { - goto parseError; - } - *opPtr = '\0'; - code = Tcl_GetInt(interp, bytes, &first); - *opPtr = savedOp; - if (code == TCL_ERROR) { - goto parseError; - } - if (TCL_ERROR == Tcl_GetInt(interp, opPtr+1, &second)) { - goto parseError; - } - if (savedOp == '+') { - *indexPtr = first + second; - } else { - *indexPtr = first - second; - } - return TCL_OK; - } - - /* - * Report a parse error. - */ - - parseError: - if (interp != NULL) { - bytes = TclGetString(objPtr); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad index \"%s\": must be integer?[+-]integer? or" - " end?[+-]integer?", bytes)); - if (!strncmp(bytes, "end-", 4)) { - bytes += 4; - } - TclCheckBadOctal(interp, bytes); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX", NULL); - } - - return TCL_ERROR; + Tcl_WideInt wide; + + if (GetWideForIndex(interp, objPtr, endValue, &wide) == TCL_ERROR) { + return TCL_ERROR; + } + if (wide < INT_MIN) { + *indexPtr = INT_MIN; + } else if (wide > INT_MAX) { + *indexPtr = INT_MAX; + } else { + *indexPtr = (int) wide; + } + return TCL_OK; } /* *---------------------------------------------------------------------- * @@ -3777,24 +3921,38 @@ */ static int GetEndOffsetFromObj( Tcl_Obj *objPtr, /* Pointer to the object to parse */ - int endValue, /* The value to be stored at "indexPtr" if + Tcl_WideInt endValue, /* The value to be stored at "indexPtr" if * "objPtr" holds "end". */ - int *indexPtr) /* Location filled in with an integer + Tcl_WideInt *widePtr) /* Location filled in with an integer * representing an index. */ { - if (SetEndOffsetFromAny(NULL, objPtr) != TCL_OK) { - return TCL_ERROR; - } - - /* TODO: Handle overflow cases sensibly */ - *indexPtr = endValue + (int)objPtr->internalRep.wideValue; - return TCL_OK; -} - + if (SetEndOffsetFromAny(NULL, objPtr) == TCL_OK) { + Tcl_WideInt offset = objPtr->internalRep.wideValue; + + if ((endValue ^ offset) < 0) { + /* Different signs, sum cannot overflow */ + *widePtr = endValue + offset; + } else if (endValue >= 0) { + if (endValue < LLONG_MAX - offset) { + *widePtr = endValue + offset; + } else { + *widePtr = LLONG_MAX; + } + } else { + if (endValue > LLONG_MIN - offset) { + *widePtr = endValue + offset; + } else { + *widePtr = LLONG_MIN; + } + } + return TCL_OK; + } + return TCL_ERROR; +} /* *---------------------------------------------------------------------- * * SetEndOffsetFromAny -- @@ -3959,47 +4117,52 @@ Tcl_Obj *objPtr, /* Index value to parse */ int before, /* Value to return for index before beginning */ int after, /* Value to return for index after end */ int *indexPtr) /* Where to write the encoded answer, not NULL */ { - int idx; + ClientData cd; + Tcl_WideInt wide; + int idx, numType, code = TclGetNumberFromObj(NULL, objPtr, &cd, &numType); - if (TCL_OK == TclGetIntFromObj(NULL, objPtr, &idx)) { - /* We parsed a value in the range INT_MIN...INT_MAX */ + if ((code == TCL_OK) && (numType == TCL_NUMBER_WIDE)) { + /* We parsed a value in the range LLONG_MIN...LLONG_MAX */ + wide = (*(Tcl_WideInt *)cd); integerEncode: - if (idx < TCL_INDEX_START) { + if (wide < TCL_INDEX_START) { /* All negative absolute indices are "before the beginning" */ idx = before; - } else if (idx == INT_MAX) { + } else if (wide >= INT_MAX) { /* This index value is always "after the end" */ idx = after; - } + } else { + idx = (int) wide; + } /* usual case, the absolute index value encodes itself */ - } else if (TCL_OK == GetEndOffsetFromObj(objPtr, 0, &idx)) { + } else if (TCL_OK == GetEndOffsetFromObj(objPtr, 0, &wide)) { /* * We parsed an end+offset index value. - * idx holds the offset value in the range INT_MIN...INT_MAX. + * wide holds the offset value in the range LLONG_MIN...LLONG_MAX. */ - if (idx > 0) { + if (wide > 0) { /* * All end+postive or end-negative expressions * always indicate "after the end". */ idx = after; - } else if (idx < INT_MIN - TCL_INDEX_END) { + } else if (wide < INT_MIN - TCL_INDEX_END) { /* These indices always indicate "before the beginning */ idx = before; } else { /* Encoded end-positive (or end+negative) are offset */ - idx += TCL_INDEX_END; + idx = (int)wide + TCL_INDEX_END; } /* TODO: Consider flag to suppress repeated end-offset parse. */ - } else if (TCL_OK == TclGetIntForIndexM(interp, objPtr, 0, &idx)) { + } else if (TCL_OK == GetWideForIndex(interp, objPtr, 0, &wide)) { /* * Only reach this case when the index value is a - * constant index arithmetic expression, and idx + * constant index arithmetic expression, and wide * holds the result. Treat it the same as if it were * parsed as an absolute integer value. */ goto integerEncode; } else { Index: tests/util.test ================================================================== --- tests/util.test +++ tests/util.test @@ -687,17 +687,17 @@ test util-9.31.1 {TclGetIntForIndex} -body { string index a 0d } -returnCodes error -match glob -result * test util-9.32 {TclGetIntForIndex} -body { string index a 0x1FFFFFFFF+0 -} -returnCodes error -match glob -result * +} -result {} test util-9.33 {TclGetIntForIndex} -body { string index a 100000000000+0 -} -returnCodes error -match glob -result * +} -result {} test util-9.33.1 {TclGetIntForIndex} -body { string index a 0d100000000000+0 -} -returnCodes error -match glob -result * +} -result {} test util-9.34 {TclGetIntForIndex} -body { string index a 1.0 } -returnCodes error -match glob -result * test util-9.35 {TclGetIntForIndex} -body { string index a 1e23 @@ -726,11 +726,17 @@ test util-9.43 {TclGetIntForIndex} -body { string index a 0+1.5e1 } -returnCodes error -match glob -result * test util-9.44 {TclGetIntForIndex} -body { string index a 0+1000000000000 -} -returnCodes error -match glob -result * +} -result {} +test util-9.45 {TclGetIntForIndex} { + string index abcd end+2305843009213693950 +} {} +test util-9.46 {TclGetIntForIndex} { + string index abcd end+4294967294 +} {} test util-10.1 {Tcl_PrintDouble - rounding} {ieeeFloatingPoint} { convertDouble 0x0000000000000000 } {0.0} test util-10.2 {Tcl_PrintDouble - rounding} {ieeeFloatingPoint} {