Skip to content

Commit

Permalink
the overflow check could mistakenly pass in some cases (so basically …
Browse files Browse the repository at this point in the history
…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).
  • Loading branch information
sebres committed Mar 22, 2024
1 parent 24b3f79 commit cd39465
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 18 deletions.
52 changes: 37 additions & 15 deletions generic/tclClockFmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -69,22 +69,33 @@ _str2int(
const char *e,
int sign)
{
register int val = 0, prev = 0;
register int val = 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 */
int 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');
}
while (p < e) { /* check for overflow */
int prev = val;
val = val * 10 - (*p++ - '0');
if (val > prev) {
if (val / 10 > prev) {
return TCL_ERROR;
}
prev = val;
}
}
*out = val;
Expand All @@ -99,22 +110,33 @@ _str2wideInt(
const char *e,
int sign)
{
register Tcl_WideInt val = 0, prev = 0;
register Tcl_WideInt val = 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 */
Tcl_WideInt 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');
}
while (p < e) { /* check for overflow */
Tcl_WideInt prev = val;
val = val * 10 - (*p++ - '0');
if (val > prev) {
if (val / 10 > prev) {
return TCL_ERROR;
}
prev = val;
}
}
*out = val;
Expand Down
29 changes: 26 additions & 3 deletions tests/clock.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit cd39465

Please sign in to comment.