From 46f1c30245f6b8692aa4b186a2b61ca93d4f2a04 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 22 Mar 2024 21:04:41 +0100 Subject: [PATCH] the overflow check could mistakenly pass in some cases (so basically expects div 10 to check it properly); optimizes both str2int, since we don't need to check it for most cases at all, thus definitely faster now (O(n)+O(1) vs. O(n)+O(n) and also has fewer branch mispredictions). --- generic/tclClockFmt.c | 48 +++++++++++++++++++++++++++++++------------ tests/clock.test | 29 +++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index 9dd8ecb..ffb5fbb 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -56,7 +56,7 @@ static void ClockFrmScnFinalize(ClientData clientData); *---------------------------------------------------------------------- */ -/* int overflows may happens here (expected case) */ +/* int & Tcl_WideInt overflows may happens here (expected case) */ #if defined(__GNUC__) || defined(__GNUG__) # pragma GCC optimize("no-trapv") #endif @@ -70,21 +70,32 @@ _str2int( int sign) { register int val = 0, prev = 0; + const char *eNO = e; + /* overflow impossible for 10 digits ("9..9"), so no needs to check before */ + if (e-p > 10) { + eNO = p+10; + } if (sign >= 0) { - while (p < e) { + while (p < eNO) { /* never overflows */ + val = val * 10 + (*p++ - '0'); + } + while (p < e) { /* check for overflow */ + prev = val; val = val * 10 + (*p++ - '0'); - if (val < prev) { + if (val / 10 < prev) { return TCL_ERROR; } - prev = val; } } else { - while (p < e) { + while (p < eNO) { /* never overflows */ val = val * 10 - (*p++ - '0'); - if (val > prev) { + } + while (p < e) { /* check for overflow */ + prev = val; + val = val * 10 - (*p++ - '0'); + if (val / 10 > prev) { return TCL_ERROR; } - prev = val; } } *out = val; @@ -100,21 +111,32 @@ _str2wideInt( int sign) { register Tcl_WideInt val = 0, prev = 0; + const char *eNO = e; + /* overflow impossible for 18 digits ("9..9"), so no needs to check before */ + if (e-p > 18) { + eNO = p+18; + } if (sign >= 0) { - while (p < e) { + while (p < eNO) { /* never overflows */ + val = val * 10 + (*p++ - '0'); + } + while (p < e) { /* check for overflow */ + prev = val; val = val * 10 + (*p++ - '0'); - if (val < prev) { + if (val / 10 < prev) { return TCL_ERROR; } - prev = val; } } else { - while (p < e) { + while (p < eNO) { /* never overflows */ val = val * 10 - (*p++ - '0'); - if (val > prev) { + } + while (p < e) { /* check for overflow */ + prev = val; + val = val * 10 - (*p++ - '0'); + if (val / 10 > prev) { return TCL_ERROR; } - prev = val; } } *out = val; diff --git a/tests/clock.test b/tests/clock.test index 513ae94..9f65ac7 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -18685,13 +18685,36 @@ test clock-6.8 {input of seconds} { } 9223372036854775807 test clock-6.9 {input of seconds - overflow} { - list [catch {clock scan -9223372036854775809 -format %s -gmt true} result] $result $::errorCode + list [catch {clock scan -9223372036854775809 -format %s -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] } {1 {requested date too large to represent} {CLOCK dateTooLarge}} - test clock-6.10 {input of seconds - overflow} { - list [catch {clock scan 9223372036854775808 -format %s -gmt true} result] $result $::errorCode + list [catch {clock scan 9223372036854775808 -format %s -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] } {1 {requested date too large to represent} {CLOCK dateTooLarge}} +foreach sign {{} -} { + test clock-6.10a {input of seconds - overflow, bug [1f40aa83c5]} { + list [catch {clock scan ${sign}27670116110564327423 -format %s -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] + } {1 {requested date too large to represent} {CLOCK dateTooLarge}} + test clock-6.10b {input of seconds - overflow, bug [1f40aa83c5]} { + list [catch {clock scan ${sign}27670116110564327424 -format %s -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] + } {1 {requested date too large to represent} {CLOCK dateTooLarge}} + test clock-6.10c {input of seconds - no overflow, bug [1f40aa83c5]} { + list [catch {clock scan ${sign}[string repeat 9 18] -format %s -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] + } [list 0 ${sign}[string repeat 9 18] {}] + test clock-6.10d {input of seconds - overflow, bug [1f40aa83c5]} { + list [catch {clock scan ${sign}[string repeat 9 19] -format %s -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] + } {1 {requested date too large to represent} {CLOCK dateTooLarge}} + # both fololowing freescan test don't generate overflow error, + # since it is a free scan, thus the token is simply not recognized further in yacc lexer, + # therefore we get parse error (can be surely changed latter): + test clock-6.10e {input of seconds - overflow (but since freescan parse error, but not boom), bug [1f40aa83c5]} -body { + list [catch {clock scan ${sign}27670116110564327423 -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] + } -match glob -result {1 {unable to convert date-time string "*": syntax error *} {TCL VALUE DATE PARSE}} + test clock-6.10f {input of seconds - overflow (but since freescan parse error, but not boom), bug [1f40aa83c5]} -body { + list [catch {clock scan ${sign}27670116110564327424 -gmt true} result opt] $result [if {[dict exists $opt -errorcode]} {dict get $opt -errorcode}] + } -match glob -result {1 {unable to convert date-time string "*": syntax error *} {TCL VALUE DATE PARSE}} +}; unset sign + test clock-6.11 {input of seconds - two values} { clock scan {1 2} -format {%s %s} -gmt true } 2