aboutsummaryrefslogtreecommitdiffstats
path: root/src/lread.c
diff options
context:
space:
mode:
authorPaul Eggert2017-06-01 16:03:12 -0700
committerPaul Eggert2017-06-01 16:06:38 -0700
commit178d0cb5f530e6d7eb36eb9987ff405c854ccdb3 (patch)
treed5c8c63dc97ed4635b354bb16803cbfd1d953470 /src/lread.c
parent53247108411a1e9d1aa5352c231fa049f3f918aa (diff)
downloademacs-178d0cb5f530e6d7eb36eb9987ff405c854ccdb3.tar.gz
emacs-178d0cb5f530e6d7eb36eb9987ff405c854ccdb3.zip
Improve performance by avoiding strtoumax
This made (string-to-number "10") 20% faster on my old desktop, an AMD Phenom II X4 910e running Fedora 25 x86-64. * admin/merge-gnulib (GNULIB_MODULES): Remove strtoumax. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * lib/strtoul.c, lib/strtoull.c, lib/strtoumax.c, m4/strtoull.m4: * m4/strtoumax.m4: Remove. * src/editfns.c (str2num): New function. (styled_format): Use it instead of strtoumax. Use ptrdiff_t instead of uintmax_t. Check for integer overflow. * src/lread.c (LEAD_INT, DOT_CHAR, TRAIL_INT, E_EXP): Move to private scope and make them enums. (string_to_number): Compute integer value directly during first pass instead of revisiting it with strtoumax later.
Diffstat (limited to 'src/lread.c')
-rw-r--r--src/lread.c59
1 files changed, 25 insertions, 34 deletions
diff --git a/src/lread.c b/src/lread.c
index 368b86e8189..f8493982c67 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -3495,25 +3495,18 @@ substitute_in_interval (INTERVAL interval, Lisp_Object arg)
3495} 3495}
3496 3496
3497 3497
3498#define LEAD_INT 1 3498/* Convert STRING to a number, assuming base BASE. Return a fixnum if
3499#define DOT_CHAR 2 3499 STRING has integer syntax and fits in a fixnum, else return the
3500#define TRAIL_INT 4 3500 nearest float if STRING has either floating point or integer syntax
3501#define E_EXP 16 3501 and BASE is 10, else return nil. If IGNORE_TRAILING, consider just
3502 3502 the longest prefix of STRING that has valid floating point syntax.
3503 3503 Signal an overflow if BASE is not 10 and the number has integer
3504/* Convert STRING to a number, assuming base BASE. Return a fixnum if CP has 3504 syntax but does not fit. */
3505 integer syntax and fits in a fixnum, else return the nearest float if CP has
3506 either floating point or integer syntax and BASE is 10, else return nil. If
3507 IGNORE_TRAILING, consider just the longest prefix of CP that has
3508 valid floating point syntax. Signal an overflow if BASE is not 10 and the
3509 number has integer syntax but does not fit. */
3510 3505
3511Lisp_Object 3506Lisp_Object
3512string_to_number (char const *string, int base, bool ignore_trailing) 3507string_to_number (char const *string, int base, bool ignore_trailing)
3513{ 3508{
3514 int state;
3515 char const *cp = string; 3509 char const *cp = string;
3516 int leading_digit;
3517 bool float_syntax = 0; 3510 bool float_syntax = 0;
3518 double value = 0; 3511 double value = 0;
3519 3512
@@ -3525,15 +3518,23 @@ string_to_number (char const *string, int base, bool ignore_trailing)
3525 bool signedp = negative || *cp == '+'; 3518 bool signedp = negative || *cp == '+';
3526 cp += signedp; 3519 cp += signedp;
3527 3520
3528 state = 0; 3521 enum { INTOVERFLOW = 1, LEAD_INT = 2, DOT_CHAR = 4, TRAIL_INT = 8,
3529 3522 E_EXP = 16 };
3530 leading_digit = digit_to_number (*cp, base); 3523 int state = 0;
3524 int leading_digit = digit_to_number (*cp, base);
3525 uintmax_t n = leading_digit;
3531 if (leading_digit >= 0) 3526 if (leading_digit >= 0)
3532 { 3527 {
3533 state |= LEAD_INT; 3528 state |= LEAD_INT;
3534 do 3529 for (int digit; 0 <= (digit = digit_to_number (*++cp, base)); )
3535 ++cp; 3530 {
3536 while (digit_to_number (*cp, base) >= 0); 3531 if (INT_MULTIPLY_OVERFLOW (n, base))
3532 state |= INTOVERFLOW;
3533 n *= base;
3534 if (INT_ADD_OVERFLOW (n, digit))
3535 state |= INTOVERFLOW;
3536 n += digit;
3537 }
3537 } 3538 }
3538 if (*cp == '.') 3539 if (*cp == '.')
3539 { 3540 {
@@ -3583,32 +3584,22 @@ string_to_number (char const *string, int base, bool ignore_trailing)
3583 } 3584 }
3584 3585
3585 float_syntax = ((state & (DOT_CHAR|TRAIL_INT)) == (DOT_CHAR|TRAIL_INT) 3586 float_syntax = ((state & (DOT_CHAR|TRAIL_INT)) == (DOT_CHAR|TRAIL_INT)
3586 || state == (LEAD_INT|E_EXP)); 3587 || (state & ~INTOVERFLOW) == (LEAD_INT|E_EXP));
3587 } 3588 }
3588 3589
3589 /* Return nil if the number uses invalid syntax. If IGNORE_TRAILING, accept 3590 /* Return nil if the number uses invalid syntax. If IGNORE_TRAILING, accept
3590 any prefix that matches. Otherwise, the entire string must match. */ 3591 any prefix that matches. Otherwise, the entire string must match. */
3591 if (! (ignore_trailing 3592 if (! (ignore_trailing
3592 ? ((state & LEAD_INT) != 0 || float_syntax) 3593 ? ((state & LEAD_INT) != 0 || float_syntax)
3593 : (!*cp && ((state & ~DOT_CHAR) == LEAD_INT || float_syntax)))) 3594 : (!*cp && ((state & ~(INTOVERFLOW | DOT_CHAR)) == LEAD_INT
3595 || float_syntax))))
3594 return Qnil; 3596 return Qnil;
3595 3597
3596 /* If the number uses integer and not float syntax, and is in C-language 3598 /* If the number uses integer and not float syntax, and is in C-language
3597 range, use its value, preferably as a fixnum. */ 3599 range, use its value, preferably as a fixnum. */
3598 if (leading_digit >= 0 && ! float_syntax) 3600 if (leading_digit >= 0 && ! float_syntax)
3599 { 3601 {
3600 uintmax_t n; 3602 if (state & INTOVERFLOW)
3601
3602 /* Fast special case for single-digit integers. This also avoids a
3603 glitch when BASE is 16 and IGNORE_TRAILING, because in that
3604 case some versions of strtoumax accept numbers like "0x1" that Emacs
3605 does not allow. */
3606 if (digit_to_number (string[signedp + 1], base) < 0)
3607 return make_number (negative ? -leading_digit : leading_digit);
3608
3609 errno = 0;
3610 n = strtoumax (string + signedp, NULL, base);
3611 if (errno == ERANGE)
3612 { 3603 {
3613 /* Unfortunately there's no simple and accurate way to convert 3604 /* Unfortunately there's no simple and accurate way to convert
3614 non-base-10 numbers that are out of C-language range. */ 3605 non-base-10 numbers that are out of C-language range. */