diff options
| author | Paul Eggert | 2017-06-01 16:03:12 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-06-01 16:06:38 -0700 |
| commit | 178d0cb5f530e6d7eb36eb9987ff405c854ccdb3 (patch) | |
| tree | d5c8c63dc97ed4635b354bb16803cbfd1d953470 /src/editfns.c | |
| parent | 53247108411a1e9d1aa5352c231fa049f3f918aa (diff) | |
| download | emacs-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/editfns.c')
| -rw-r--r-- | src/editfns.c | 43 |
1 files changed, 31 insertions, 12 deletions
diff --git a/src/editfns.c b/src/editfns.c index 98187df5d97..1dbae8f5d4b 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -3851,6 +3851,23 @@ usage: (propertize STRING &rest PROPERTIES) */) | |||
| 3851 | return string; | 3851 | return string; |
| 3852 | } | 3852 | } |
| 3853 | 3853 | ||
| 3854 | /* Convert the prefix of STR from ASCII decimal digits to a number. | ||
| 3855 | Set *STR_END to the address of the first non-digit. Return the | ||
| 3856 | number, or PTRDIFF_MAX on overflow. Return 0 if there is no number. | ||
| 3857 | This is like strtol for ptrdiff_t and base 10 and C locale, | ||
| 3858 | except without negative numbers or errno. */ | ||
| 3859 | |||
| 3860 | static ptrdiff_t | ||
| 3861 | str2num (char *str, char **str_end) | ||
| 3862 | { | ||
| 3863 | ptrdiff_t n = 0; | ||
| 3864 | for (; c_isdigit (*str); str++) | ||
| 3865 | if (INT_MULTIPLY_WRAPV (n, 10, &n) || INT_ADD_WRAPV (n, *str - '0', &n)) | ||
| 3866 | n = PTRDIFF_MAX; | ||
| 3867 | *str_end = str; | ||
| 3868 | return n; | ||
| 3869 | } | ||
| 3870 | |||
| 3854 | DEFUN ("format", Fformat, Sformat, 1, MANY, 0, | 3871 | DEFUN ("format", Fformat, Sformat, 1, MANY, 0, |
| 3855 | doc: /* Format a string out of a format-string and arguments. | 3872 | doc: /* Format a string out of a format-string and arguments. |
| 3856 | The first argument is a format control string. | 3873 | The first argument is a format control string. |
| @@ -4057,17 +4074,16 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4057 | digits to print after the '.' for floats, or the max. | 4074 | digits to print after the '.' for floats, or the max. |
| 4058 | number of chars to print from a string. */ | 4075 | number of chars to print from a string. */ |
| 4059 | 4076 | ||
| 4060 | uintmax_t num; | 4077 | ptrdiff_t num; |
| 4061 | char *num_end; | 4078 | char *num_end; |
| 4062 | if (c_isdigit (*format)) | 4079 | if (c_isdigit (*format)) |
| 4063 | { | 4080 | { |
| 4064 | num = strtoumax (format, &num_end, 10); | 4081 | num = str2num (format, &num_end); |
| 4065 | if (*num_end == '$') | 4082 | if (*num_end == '$') |
| 4066 | { | 4083 | { |
| 4067 | if (num == 0) | 4084 | if (num == 0) |
| 4068 | error ("Invalid format field number 0"); | 4085 | error ("Invalid format field number 0"); |
| 4069 | n = min (num, PTRDIFF_MAX); | 4086 | n = num - 1; |
| 4070 | n--; | ||
| 4071 | format = num_end + 1; | 4087 | format = num_end + 1; |
| 4072 | } | 4088 | } |
| 4073 | } | 4089 | } |
| @@ -4095,15 +4111,15 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4095 | space_flag &= ! plus_flag; | 4111 | space_flag &= ! plus_flag; |
| 4096 | zero_flag &= ! minus_flag; | 4112 | zero_flag &= ! minus_flag; |
| 4097 | 4113 | ||
| 4098 | num = strtoumax (format, &num_end, 10); | 4114 | num = str2num (format, &num_end); |
| 4099 | if (max_bufsize <= num) | 4115 | if (max_bufsize <= num) |
| 4100 | string_overflow (); | 4116 | string_overflow (); |
| 4101 | ptrdiff_t field_width = num; | 4117 | ptrdiff_t field_width = num; |
| 4102 | 4118 | ||
| 4103 | bool precision_given = *num_end == '.'; | 4119 | bool precision_given = *num_end == '.'; |
| 4104 | uintmax_t precision = (precision_given | 4120 | ptrdiff_t precision = (precision_given |
| 4105 | ? strtoumax (num_end + 1, &num_end, 10) | 4121 | ? str2num (num_end + 1, &num_end) |
| 4106 | : UINTMAX_MAX); | 4122 | : PTRDIFF_MAX); |
| 4107 | format = num_end; | 4123 | format = num_end; |
| 4108 | 4124 | ||
| 4109 | if (format == end) | 4125 | if (format == end) |
| @@ -4176,7 +4192,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4176 | /* handle case (precision[n] >= 0) */ | 4192 | /* handle case (precision[n] >= 0) */ |
| 4177 | 4193 | ||
| 4178 | ptrdiff_t prec = -1; | 4194 | ptrdiff_t prec = -1; |
| 4179 | if (precision_given && precision <= TYPE_MAXIMUM (ptrdiff_t)) | 4195 | if (precision_given) |
| 4180 | prec = precision; | 4196 | prec = precision; |
| 4181 | 4197 | ||
| 4182 | /* lisp_string_width ignores a precision of 0, but GNU | 4198 | /* lisp_string_width ignores a precision of 0, but GNU |
| @@ -4424,8 +4440,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4424 | padding and excess precision. Deal with excess precision | 4440 | padding and excess precision. Deal with excess precision |
| 4425 | first. This happens only when the format specifies | 4441 | first. This happens only when the format specifies |
| 4426 | ridiculously large precision. */ | 4442 | ridiculously large precision. */ |
| 4427 | uintmax_t excess_precision = precision - prec; | 4443 | ptrdiff_t excess_precision |
| 4428 | uintmax_t leading_zeros = 0, trailing_zeros = 0; | 4444 | = precision_given ? precision - prec : 0; |
| 4445 | ptrdiff_t leading_zeros = 0, trailing_zeros = 0; | ||
| 4429 | if (excess_precision) | 4446 | if (excess_precision) |
| 4430 | { | 4447 | { |
| 4431 | if (float_conversion) | 4448 | if (float_conversion) |
| @@ -4451,7 +4468,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4451 | 4468 | ||
| 4452 | /* Compute the total bytes needed for this item, including | 4469 | /* Compute the total bytes needed for this item, including |
| 4453 | excess precision and padding. */ | 4470 | excess precision and padding. */ |
| 4454 | uintmax_t numwidth = sprintf_bytes + excess_precision; | 4471 | ptrdiff_t numwidth; |
| 4472 | if (INT_ADD_WRAPV (sprintf_bytes, excess_precision, &numwidth)) | ||
| 4473 | numwidth = PTRDIFF_MAX; | ||
| 4455 | ptrdiff_t padding | 4474 | ptrdiff_t padding |
| 4456 | = numwidth < field_width ? field_width - numwidth : 0; | 4475 | = numwidth < field_width ? field_width - numwidth : 0; |
| 4457 | if (max_bufsize - sprintf_bytes <= excess_precision | 4476 | if (max_bufsize - sprintf_bytes <= excess_precision |