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.
This commit is contained in:
parent
32b01c02b4
commit
d4868b2bee
1 changed files with 79 additions and 80 deletions
159
src/lread.c
159
src/lread.c
|
@ -44,6 +44,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
#include "blockinput.h"
|
||||
#include "pdumper.h"
|
||||
#include <c-ctype.h>
|
||||
#include <vla.h>
|
||||
|
||||
#ifdef MSDOS
|
||||
#include "msdos.h"
|
||||
|
@ -2640,89 +2641,83 @@ digit_to_number (int character, int base)
|
|||
return digit < base ? digit : -1;
|
||||
}
|
||||
|
||||
static char const invalid_radix_integer_format[] = "integer, radix %"pI"d";
|
||||
|
||||
/* Small, as read1 is recursive (Bug#31995). But big enough to hold
|
||||
the invalid_radix_integer string. */
|
||||
enum { stackbufsize = max (64,
|
||||
(sizeof invalid_radix_integer_format
|
||||
- sizeof "%"pI"d"
|
||||
+ INT_STRLEN_BOUND (EMACS_INT) + 1)) };
|
||||
|
||||
static void
|
||||
free_contents (void *p)
|
||||
invalid_radix_integer (EMACS_INT radix, char stackbuf[VLA_ELEMS (stackbufsize)])
|
||||
{
|
||||
void **ptr = (void **) p;
|
||||
xfree (*ptr);
|
||||
sprintf (stackbuf, invalid_radix_integer_format, radix);
|
||||
invalid_syntax (stackbuf);
|
||||
}
|
||||
|
||||
/* Read an integer in radix RADIX using READCHARFUN to read
|
||||
characters. RADIX must be in the interval [2..36]; if it isn't, a
|
||||
read error is signaled . Value is the integer read. Signals an
|
||||
error if encountering invalid read syntax or if RADIX is out of
|
||||
range. */
|
||||
characters. RADIX must be in the interval [2..36]. Use STACKBUF
|
||||
for temporary storage as needed. Value is the integer read.
|
||||
Signal an error if encountering invalid read syntax. */
|
||||
|
||||
static Lisp_Object
|
||||
read_integer (Lisp_Object readcharfun, EMACS_INT radix)
|
||||
read_integer (Lisp_Object readcharfun, int radix,
|
||||
char stackbuf[VLA_ELEMS (stackbufsize)])
|
||||
{
|
||||
/* Room for sign, leading 0, other digits, trailing NUL byte.
|
||||
Also, room for invalid syntax diagnostic. */
|
||||
size_t len = max (1 + 1 + UINTMAX_WIDTH + 1,
|
||||
sizeof "integer, radix " + INT_STRLEN_BOUND (EMACS_INT));
|
||||
char *buf = xmalloc (len);
|
||||
char *p = buf;
|
||||
char *read_buffer = stackbuf;
|
||||
ptrdiff_t read_buffer_size = stackbufsize;
|
||||
char *p = read_buffer;
|
||||
char *heapbuf = NULL;
|
||||
int valid = -1; /* 1 if valid, 0 if not, -1 if incomplete. */
|
||||
|
||||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
record_unwind_protect_ptr (free_contents, &buf);
|
||||
|
||||
if (radix < 2 || radix > 36)
|
||||
valid = 0;
|
||||
else
|
||||
int c = READCHAR;
|
||||
if (c == '-' || c == '+')
|
||||
{
|
||||
int c, digit;
|
||||
|
||||
p = buf;
|
||||
|
||||
*p++ = c;
|
||||
c = READCHAR;
|
||||
if (c == '-' || c == '+')
|
||||
{
|
||||
*p++ = c;
|
||||
c = READCHAR;
|
||||
}
|
||||
|
||||
if (c == '0')
|
||||
{
|
||||
*p++ = c;
|
||||
valid = 1;
|
||||
|
||||
/* Ignore redundant leading zeros, so the buffer doesn't
|
||||
fill up with them. */
|
||||
do
|
||||
c = READCHAR;
|
||||
while (c == '0');
|
||||
}
|
||||
|
||||
while ((digit = digit_to_number (c, radix)) >= -1)
|
||||
{
|
||||
if (digit == -1)
|
||||
valid = 0;
|
||||
if (valid < 0)
|
||||
valid = 1;
|
||||
/* Allow 1 extra byte for the \0. */
|
||||
if (p + 1 == buf + len)
|
||||
{
|
||||
ptrdiff_t where = p - buf;
|
||||
len *= 2;
|
||||
buf = xrealloc (buf, len);
|
||||
p = buf + where;
|
||||
}
|
||||
*p++ = c;
|
||||
c = READCHAR;
|
||||
}
|
||||
|
||||
UNREAD (c);
|
||||
}
|
||||
|
||||
if (c == '0')
|
||||
{
|
||||
*p++ = c;
|
||||
valid = 1;
|
||||
|
||||
/* Ignore redundant leading zeros, so the buffer doesn't
|
||||
fill up with them. */
|
||||
do
|
||||
c = READCHAR;
|
||||
while (c == '0');
|
||||
}
|
||||
|
||||
for (int digit; (digit = digit_to_number (c, radix)) >= -1; )
|
||||
{
|
||||
if (digit == -1)
|
||||
valid = 0;
|
||||
if (valid < 0)
|
||||
valid = 1;
|
||||
/* Allow 1 extra byte for the \0. */
|
||||
if (p + 1 == read_buffer + read_buffer_size)
|
||||
{
|
||||
ptrdiff_t offset = p - read_buffer;
|
||||
read_buffer = grow_read_buffer (read_buffer, offset,
|
||||
&heapbuf, &read_buffer_size,
|
||||
count);
|
||||
p = read_buffer + offset;
|
||||
}
|
||||
*p++ = c;
|
||||
c = READCHAR;
|
||||
}
|
||||
|
||||
UNREAD (c);
|
||||
|
||||
if (valid != 1)
|
||||
{
|
||||
sprintf (buf, "integer, radix %"pI"d", radix);
|
||||
invalid_syntax (buf);
|
||||
}
|
||||
invalid_radix_integer (radix, stackbuf);
|
||||
|
||||
*p = '\0';
|
||||
return unbind_to (count, string_to_number (buf, radix, 0));
|
||||
return unbind_to (count, string_to_number (read_buffer, radix, NULL));
|
||||
}
|
||||
|
||||
|
||||
|
@ -2738,7 +2733,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
|
|||
int c;
|
||||
bool uninterned_symbol = false;
|
||||
bool multibyte;
|
||||
char stackbuf[128]; /* Small, as read1 is recursive (Bug#31995). */
|
||||
char stackbuf[stackbufsize];
|
||||
current_thread->stack_top = stackbuf;
|
||||
|
||||
*pch = 0;
|
||||
|
@ -3108,30 +3103,34 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
|
|||
/* ## is the empty symbol. */
|
||||
if (c == '#')
|
||||
return Fintern (empty_unibyte_string, Qnil);
|
||||
/* Reader forms that can reuse previously read objects. */
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
EMACS_INT n = 0;
|
||||
Lisp_Object tem;
|
||||
EMACS_INT n = c - '0';
|
||||
bool overflow = false;
|
||||
|
||||
/* Read a non-negative integer. */
|
||||
while (c >= '0' && c <= '9')
|
||||
while ('0' <= (c = READCHAR) && c <= '9')
|
||||
{
|
||||
overflow |= INT_MULTIPLY_WRAPV (n, 10, &n);
|
||||
overflow |= INT_ADD_WRAPV (n, c - '0', &n);
|
||||
c = READCHAR;
|
||||
}
|
||||
|
||||
if (!overflow && n <= MOST_POSITIVE_FIXNUM)
|
||||
if (!overflow)
|
||||
{
|
||||
if (c == 'r' || c == 'R')
|
||||
return read_integer (readcharfun, n);
|
||||
|
||||
if (! NILP (Vread_circle))
|
||||
{
|
||||
if (! (2 <= n && n <= 36))
|
||||
invalid_radix_integer (n, stackbuf);
|
||||
return read_integer (readcharfun, n, stackbuf);
|
||||
}
|
||||
|
||||
if (n <= MOST_POSITIVE_FIXNUM && ! NILP (Vread_circle))
|
||||
{
|
||||
/* Reader forms that can reuse previously read objects. */
|
||||
|
||||
/* #n=object returns object, but associates it with
|
||||
n for #n#. */
|
||||
n for #n#. */
|
||||
if (c == '=')
|
||||
{
|
||||
/* Make a placeholder for #n# to use temporarily. */
|
||||
|
@ -3160,7 +3159,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
|
|||
hash_put (h, number, placeholder, hash);
|
||||
|
||||
/* Read the object itself. */
|
||||
tem = read0 (readcharfun);
|
||||
Lisp_Object tem = read0 (readcharfun);
|
||||
|
||||
/* If it can be recursive, remember it for
|
||||
future substitutions. */
|
||||
|
@ -3210,11 +3209,11 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
|
|||
/* Fall through to error message. */
|
||||
}
|
||||
else if (c == 'x' || c == 'X')
|
||||
return read_integer (readcharfun, 16);
|
||||
return read_integer (readcharfun, 16, stackbuf);
|
||||
else if (c == 'o' || c == 'O')
|
||||
return read_integer (readcharfun, 8);
|
||||
return read_integer (readcharfun, 8, stackbuf);
|
||||
else if (c == 'b' || c == 'B')
|
||||
return read_integer (readcharfun, 2);
|
||||
return read_integer (readcharfun, 2, stackbuf);
|
||||
|
||||
UNREAD (c);
|
||||
invalid_syntax ("#");
|
||||
|
|
Loading…
Add table
Reference in a new issue