diff options
| author | Paul Eggert | 2019-05-15 10:26:54 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-05-15 10:27:55 -0700 |
| commit | d4868b2bee88c89e704b4228a34e29dfc4a9f2a5 (patch) | |
| tree | 716b52af67c7f615c39dc3634454feb3e6dcc400 | |
| parent | 32b01c02b48963a242fcc984c3599f59e59c1b2c (diff) | |
| download | emacs-d4868b2bee88c89e704b4228a34e29dfc4a9f2a5.tar.gz emacs-d4868b2bee88c89e704b4228a34e29dfc4a9f2a5.zip | |
Tune reading of radix integers
This improves the performance of (read "%xFF") by about 25%
on my platform.
* src/lread.c: Include <vla.h>, so that we can better document
buffer sizes of arguments.
(invalid_radix_integer_format, stackbufsize): New constants.
(free_contents): Remove. All uses removed.
(invalid_radix_integer): New function.
(read_integer): New arg STACKBUF. Assume radix is in range.
All uses changed. Use STACKBUF to avoid calling malloc in the
usual case. Use grow_read_buffer to simplify.
(read1): Tune. Improve quality of diagnostic when
MOST_POSITIVE_FIXNUM < radix <= EMACS_INT_MAX.
| -rw-r--r-- | src/lread.c | 147 |
1 files changed, 73 insertions, 74 deletions
diff --git a/src/lread.c b/src/lread.c index c37719e0d24..5fa90cad3f3 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -44,6 +44,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 44 | #include "blockinput.h" | 44 | #include "blockinput.h" |
| 45 | #include "pdumper.h" | 45 | #include "pdumper.h" |
| 46 | #include <c-ctype.h> | 46 | #include <c-ctype.h> |
| 47 | #include <vla.h> | ||
| 47 | 48 | ||
| 48 | #ifdef MSDOS | 49 | #ifdef MSDOS |
| 49 | #include "msdos.h" | 50 | #include "msdos.h" |
| @@ -2640,89 +2641,83 @@ digit_to_number (int character, int base) | |||
| 2640 | return digit < base ? digit : -1; | 2641 | return digit < base ? digit : -1; |
| 2641 | } | 2642 | } |
| 2642 | 2643 | ||
| 2644 | static char const invalid_radix_integer_format[] = "integer, radix %"pI"d"; | ||
| 2645 | |||
| 2646 | /* Small, as read1 is recursive (Bug#31995). But big enough to hold | ||
| 2647 | the invalid_radix_integer string. */ | ||
| 2648 | enum { stackbufsize = max (64, | ||
| 2649 | (sizeof invalid_radix_integer_format | ||
| 2650 | - sizeof "%"pI"d" | ||
| 2651 | + INT_STRLEN_BOUND (EMACS_INT) + 1)) }; | ||
| 2652 | |||
| 2643 | static void | 2653 | static void |
| 2644 | free_contents (void *p) | 2654 | invalid_radix_integer (EMACS_INT radix, char stackbuf[VLA_ELEMS (stackbufsize)]) |
| 2645 | { | 2655 | { |
| 2646 | void **ptr = (void **) p; | 2656 | sprintf (stackbuf, invalid_radix_integer_format, radix); |
| 2647 | xfree (*ptr); | 2657 | invalid_syntax (stackbuf); |
| 2648 | } | 2658 | } |
| 2649 | 2659 | ||
| 2650 | /* Read an integer in radix RADIX using READCHARFUN to read | 2660 | /* Read an integer in radix RADIX using READCHARFUN to read |
| 2651 | characters. RADIX must be in the interval [2..36]; if it isn't, a | 2661 | characters. RADIX must be in the interval [2..36]. Use STACKBUF |
| 2652 | read error is signaled . Value is the integer read. Signals an | 2662 | for temporary storage as needed. Value is the integer read. |
| 2653 | error if encountering invalid read syntax or if RADIX is out of | 2663 | Signal an error if encountering invalid read syntax. */ |
| 2654 | range. */ | ||
| 2655 | 2664 | ||
| 2656 | static Lisp_Object | 2665 | static Lisp_Object |
| 2657 | read_integer (Lisp_Object readcharfun, EMACS_INT radix) | 2666 | read_integer (Lisp_Object readcharfun, int radix, |
| 2667 | char stackbuf[VLA_ELEMS (stackbufsize)]) | ||
| 2658 | { | 2668 | { |
| 2659 | /* Room for sign, leading 0, other digits, trailing NUL byte. | 2669 | char *read_buffer = stackbuf; |
| 2660 | Also, room for invalid syntax diagnostic. */ | 2670 | ptrdiff_t read_buffer_size = stackbufsize; |
| 2661 | size_t len = max (1 + 1 + UINTMAX_WIDTH + 1, | 2671 | char *p = read_buffer; |
| 2662 | sizeof "integer, radix " + INT_STRLEN_BOUND (EMACS_INT)); | 2672 | char *heapbuf = NULL; |
| 2663 | char *buf = xmalloc (len); | ||
| 2664 | char *p = buf; | ||
| 2665 | int valid = -1; /* 1 if valid, 0 if not, -1 if incomplete. */ | 2673 | int valid = -1; /* 1 if valid, 0 if not, -1 if incomplete. */ |
| 2666 | |||
| 2667 | ptrdiff_t count = SPECPDL_INDEX (); | 2674 | ptrdiff_t count = SPECPDL_INDEX (); |
| 2668 | record_unwind_protect_ptr (free_contents, &buf); | ||
| 2669 | 2675 | ||
| 2670 | if (radix < 2 || radix > 36) | 2676 | int c = READCHAR; |
| 2671 | valid = 0; | 2677 | if (c == '-' || c == '+') |
| 2672 | else | ||
| 2673 | { | 2678 | { |
| 2674 | int c, digit; | 2679 | *p++ = c; |
| 2675 | |||
| 2676 | p = buf; | ||
| 2677 | |||
| 2678 | c = READCHAR; | 2680 | c = READCHAR; |
| 2679 | if (c == '-' || c == '+') | 2681 | } |
| 2680 | { | ||
| 2681 | *p++ = c; | ||
| 2682 | c = READCHAR; | ||
| 2683 | } | ||
| 2684 | 2682 | ||
| 2685 | if (c == '0') | 2683 | if (c == '0') |
| 2686 | { | 2684 | { |
| 2687 | *p++ = c; | 2685 | *p++ = c; |
| 2688 | valid = 1; | 2686 | valid = 1; |
| 2689 | 2687 | ||
| 2690 | /* Ignore redundant leading zeros, so the buffer doesn't | 2688 | /* Ignore redundant leading zeros, so the buffer doesn't |
| 2691 | fill up with them. */ | 2689 | fill up with them. */ |
| 2692 | do | 2690 | do |
| 2693 | c = READCHAR; | 2691 | c = READCHAR; |
| 2694 | while (c == '0'); | 2692 | while (c == '0'); |
| 2695 | } | 2693 | } |
| 2696 | 2694 | ||
| 2697 | while ((digit = digit_to_number (c, radix)) >= -1) | 2695 | for (int digit; (digit = digit_to_number (c, radix)) >= -1; ) |
| 2696 | { | ||
| 2697 | if (digit == -1) | ||
| 2698 | valid = 0; | ||
| 2699 | if (valid < 0) | ||
| 2700 | valid = 1; | ||
| 2701 | /* Allow 1 extra byte for the \0. */ | ||
| 2702 | if (p + 1 == read_buffer + read_buffer_size) | ||
| 2698 | { | 2703 | { |
| 2699 | if (digit == -1) | 2704 | ptrdiff_t offset = p - read_buffer; |
| 2700 | valid = 0; | 2705 | read_buffer = grow_read_buffer (read_buffer, offset, |
| 2701 | if (valid < 0) | 2706 | &heapbuf, &read_buffer_size, |
| 2702 | valid = 1; | 2707 | count); |
| 2703 | /* Allow 1 extra byte for the \0. */ | 2708 | p = read_buffer + offset; |
| 2704 | if (p + 1 == buf + len) | ||
| 2705 | { | ||
| 2706 | ptrdiff_t where = p - buf; | ||
| 2707 | len *= 2; | ||
| 2708 | buf = xrealloc (buf, len); | ||
| 2709 | p = buf + where; | ||
| 2710 | } | ||
| 2711 | *p++ = c; | ||
| 2712 | c = READCHAR; | ||
| 2713 | } | 2709 | } |
| 2714 | 2710 | *p++ = c; | |
| 2715 | UNREAD (c); | 2711 | c = READCHAR; |
| 2716 | } | 2712 | } |
| 2717 | 2713 | ||
| 2714 | UNREAD (c); | ||
| 2715 | |||
| 2718 | if (valid != 1) | 2716 | if (valid != 1) |
| 2719 | { | 2717 | invalid_radix_integer (radix, stackbuf); |
| 2720 | sprintf (buf, "integer, radix %"pI"d", radix); | ||
| 2721 | invalid_syntax (buf); | ||
| 2722 | } | ||
| 2723 | 2718 | ||
| 2724 | *p = '\0'; | 2719 | *p = '\0'; |
| 2725 | return unbind_to (count, string_to_number (buf, radix, 0)); | 2720 | return unbind_to (count, string_to_number (read_buffer, radix, NULL)); |
| 2726 | } | 2721 | } |
| 2727 | 2722 | ||
| 2728 | 2723 | ||
| @@ -2738,7 +2733,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) | |||
| 2738 | int c; | 2733 | int c; |
| 2739 | bool uninterned_symbol = false; | 2734 | bool uninterned_symbol = false; |
| 2740 | bool multibyte; | 2735 | bool multibyte; |
| 2741 | char stackbuf[128]; /* Small, as read1 is recursive (Bug#31995). */ | 2736 | char stackbuf[stackbufsize]; |
| 2742 | current_thread->stack_top = stackbuf; | 2737 | current_thread->stack_top = stackbuf; |
| 2743 | 2738 | ||
| 2744 | *pch = 0; | 2739 | *pch = 0; |
| @@ -3108,30 +3103,34 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) | |||
| 3108 | /* ## is the empty symbol. */ | 3103 | /* ## is the empty symbol. */ |
| 3109 | if (c == '#') | 3104 | if (c == '#') |
| 3110 | return Fintern (empty_unibyte_string, Qnil); | 3105 | return Fintern (empty_unibyte_string, Qnil); |
| 3111 | /* Reader forms that can reuse previously read objects. */ | 3106 | |
| 3112 | if (c >= '0' && c <= '9') | 3107 | if (c >= '0' && c <= '9') |
| 3113 | { | 3108 | { |
| 3114 | EMACS_INT n = 0; | 3109 | EMACS_INT n = c - '0'; |
| 3115 | Lisp_Object tem; | ||
| 3116 | bool overflow = false; | 3110 | bool overflow = false; |
| 3117 | 3111 | ||
| 3118 | /* Read a non-negative integer. */ | 3112 | /* Read a non-negative integer. */ |
| 3119 | while (c >= '0' && c <= '9') | 3113 | while ('0' <= (c = READCHAR) && c <= '9') |
| 3120 | { | 3114 | { |
| 3121 | overflow |= INT_MULTIPLY_WRAPV (n, 10, &n); | 3115 | overflow |= INT_MULTIPLY_WRAPV (n, 10, &n); |
| 3122 | overflow |= INT_ADD_WRAPV (n, c - '0', &n); | 3116 | overflow |= INT_ADD_WRAPV (n, c - '0', &n); |
| 3123 | c = READCHAR; | ||
| 3124 | } | 3117 | } |
| 3125 | 3118 | ||
| 3126 | if (!overflow && n <= MOST_POSITIVE_FIXNUM) | 3119 | if (!overflow) |
| 3127 | { | 3120 | { |
| 3128 | if (c == 'r' || c == 'R') | 3121 | if (c == 'r' || c == 'R') |
| 3129 | return read_integer (readcharfun, n); | 3122 | { |
| 3123 | if (! (2 <= n && n <= 36)) | ||
| 3124 | invalid_radix_integer (n, stackbuf); | ||
| 3125 | return read_integer (readcharfun, n, stackbuf); | ||
| 3126 | } | ||
| 3130 | 3127 | ||
| 3131 | if (! NILP (Vread_circle)) | 3128 | if (n <= MOST_POSITIVE_FIXNUM && ! NILP (Vread_circle)) |
| 3132 | { | 3129 | { |
| 3130 | /* Reader forms that can reuse previously read objects. */ | ||
| 3131 | |||
| 3133 | /* #n=object returns object, but associates it with | 3132 | /* #n=object returns object, but associates it with |
| 3134 | n for #n#. */ | 3133 | n for #n#. */ |
| 3135 | if (c == '=') | 3134 | if (c == '=') |
| 3136 | { | 3135 | { |
| 3137 | /* Make a placeholder for #n# to use temporarily. */ | 3136 | /* Make a placeholder for #n# to use temporarily. */ |
| @@ -3160,7 +3159,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) | |||
| 3160 | hash_put (h, number, placeholder, hash); | 3159 | hash_put (h, number, placeholder, hash); |
| 3161 | 3160 | ||
| 3162 | /* Read the object itself. */ | 3161 | /* Read the object itself. */ |
| 3163 | tem = read0 (readcharfun); | 3162 | Lisp_Object tem = read0 (readcharfun); |
| 3164 | 3163 | ||
| 3165 | /* If it can be recursive, remember it for | 3164 | /* If it can be recursive, remember it for |
| 3166 | future substitutions. */ | 3165 | future substitutions. */ |
| @@ -3210,11 +3209,11 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) | |||
| 3210 | /* Fall through to error message. */ | 3209 | /* Fall through to error message. */ |
| 3211 | } | 3210 | } |
| 3212 | else if (c == 'x' || c == 'X') | 3211 | else if (c == 'x' || c == 'X') |
| 3213 | return read_integer (readcharfun, 16); | 3212 | return read_integer (readcharfun, 16, stackbuf); |
| 3214 | else if (c == 'o' || c == 'O') | 3213 | else if (c == 'o' || c == 'O') |
| 3215 | return read_integer (readcharfun, 8); | 3214 | return read_integer (readcharfun, 8, stackbuf); |
| 3216 | else if (c == 'b' || c == 'B') | 3215 | else if (c == 'b' || c == 'B') |
| 3217 | return read_integer (readcharfun, 2); | 3216 | return read_integer (readcharfun, 2, stackbuf); |
| 3218 | 3217 | ||
| 3219 | UNREAD (c); | 3218 | UNREAD (c); |
| 3220 | invalid_syntax ("#"); | 3219 | invalid_syntax ("#"); |