[ruby/json] Reduce comparisons when parsing numbers

Before this commit, we would try to scan for a float, then if that
failed, scan for an integer.  But floats and integers have many bytes in
common, so we would end up scanning the same bytes multiple times.

This patch combines integer and float scanning machines so that we only
have to scan bytes once.  If the machine finds "float parts", then it
executes the "isFloat" transition in the machine, which sets a boolean
letting us know that the parser found a float.

If we didn't find a float, but we did match, then we know it's an int.

https://github.com/ruby/json/commit/0c0e0930cd
This commit is contained in:
Aaron Patterson 2024-11-06 17:12:07 -08:00 коммит произвёл Hiroshi SHIBATA
Родитель f1be046a1d
Коммит c991f75c19
2 изменённых файлов: 195 добавлений и 268 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -420,7 +420,6 @@ static const rb_data_type_t JSON_Parser_type;
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result);
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result);
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
@ -631,10 +630,6 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
if (np != NULL) {
fexec np;
}
np = JSON_parse_integer(json, fpc, pe, result);
if (np != NULL) {
fexec np;
}
fhold; fbreak;
}
@ -716,15 +711,8 @@ static inline VALUE fast_parse_integer(char *p, char *pe)
return LL2NUM(memo);
}
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
static char *JSON_decode_integer(JSON_Parser *json, char *p, VALUE *result)
{
int cs = EVIL;
%% write init;
json->memo = p;
%% write exec;
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
if (RB_LIKELY(len < MAX_FAST_INTEGER_SIZE)) {
*result = fast_parse_integer(json->memo, p);
@ -735,9 +723,6 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
*result = rb_cstr2inum(FBUFFER_PTR(&json->fbuffer), 10);
}
return p + 1;
} else {
return NULL;
}
}
%%{
@ -747,22 +732,28 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
write data;
action exit { fhold; fbreak; }
action isFloat { is_float = true; }
main := '-'? (
(('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?)
| (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+))
) (^[0-9Ee.\-]? @exit );
(('0' | [1-9][0-9]*)
((('.' [0-9]+ ([Ee] [+\-]?[0-9]+)?) |
([Ee] [+\-]?[0-9]+)) > isFloat)?
) (^[0-9Ee.\-]? @exit ));
}%%
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
int cs = EVIL;
bool is_float = false;
%% write init;
json->memo = p;
%% write exec;
if (cs >= JSON_float_first_final) {
if (!is_float) {
return JSON_decode_integer(json, p, result);
}
VALUE mod = Qnil;
ID method_id = 0;
if (json->decimal_class) {