summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2024-10-18 12:27:17 +0200
committerHiroshi SHIBATA <[email protected]>2024-10-26 18:44:15 +0900
commite0f8732023c50ee72ed132ebebc9e858643ba6b0 (patch)
treea64b117b3eee8c726783228cd071838607b46d3a
parent8e7e63822123568e677ad34cf5350d970eca4a17 (diff)
Reduce allocations in `parse` and `load` argument handling
Avoid needless hash allocations and such that degrade performance significantly on micro-benchmarks.
-rw-r--r--ext/json/lib/json/common.rb32
-rw-r--r--ext/json/parser/parser.c26
-rw-r--r--ext/json/parser/parser.rl10
3 files changed, 48 insertions, 20 deletions
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
index 403bd34b1d..272f5731bd 100644
--- a/ext/json/lib/json/common.rb
+++ b/ext/json/lib/json/common.rb
@@ -221,8 +221,12 @@ module JSON
# # Raises JSON::ParserError (783: unexpected token at ''):
# JSON.parse('')
#
- def parse(source, opts = {})
- Parser.new(source, **(opts||{})).parse
+ def parse(source, opts = nil)
+ if opts.nil?
+ Parser.new(source).parse
+ else
+ Parser.new(source, opts).parse
+ end
end
# :call-seq:
@@ -543,15 +547,23 @@ module JSON
# #<Admin:0x00000000064c41f8
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
#
- def load(source, proc = nil, options = {})
- opts = load_default_options.merge options
- if source.respond_to? :to_str
- source = source.to_str
- elsif source.respond_to? :to_io
- source = source.to_io.read
- elsif source.respond_to?(:read)
- source = source.read
+ def load(source, proc = nil, options = nil)
+ opts = if options.nil?
+ load_default_options
+ else
+ load_default_options.merge(options)
end
+
+ unless source.is_a?(String)
+ if source.respond_to? :to_str
+ source = source.to_str
+ elsif source.respond_to? :to_io
+ source = source.to_io.read
+ elsif source.respond_to?(:read)
+ source = source.read
+ end
+ end
+
if opts[:allow_blank] && (source.nil? || source.empty?)
source = 'null'
end
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 06b11a8dba..c584977955 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -1824,7 +1824,15 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
if (json->Vsource) {
rb_raise(rb_eTypeError, "already initialized instance");
}
- rb_scan_args(argc, argv, "1:", &source, &opts);
+
+ rb_check_arity(argc, 1, 2);
+ source = argv[0];
+ opts = Qnil;
+ if (argc == 2) {
+ opts = argv[1];
+ Check_Type(opts, T_HASH);
+ }
+
if (!NIL_P(opts)) {
VALUE tmp = ID2SYM(i_max_nesting);
if (option_given_p(opts, tmp)) {
@@ -1916,7 +1924,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
-#line 1920 "parser.c"
+#line 1928 "parser.c"
enum {JSON_start = 1};
enum {JSON_first_final = 10};
enum {JSON_error = 0};
@@ -1924,7 +1932,7 @@ enum {JSON_error = 0};
enum {JSON_en_main = 1};
-#line 828 "parser.rl"
+#line 836 "parser.rl"
/*
@@ -1942,16 +1950,16 @@ static VALUE cParser_parse(VALUE self)
GET_PARSER;
-#line 1946 "parser.c"
+#line 1954 "parser.c"
{
cs = JSON_start;
}
-#line 845 "parser.rl"
+#line 853 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1955 "parser.c"
+#line 1963 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1985,7 +1993,7 @@ st0:
cs = 0;
goto _out;
tr2:
-#line 820 "parser.rl"
+#line 828 "parser.rl"
{
char *np = JSON_parse_value(json, p, pe, &result, 0);
if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
@@ -1995,7 +2003,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 1999 "parser.c"
+#line 2007 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -2084,7 +2092,7 @@ case 9:
_out: {}
}
-#line 848 "parser.rl"
+#line 856 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl
index 2b1c743dbd..da4f7f919a 100644
--- a/ext/json/parser/parser.rl
+++ b/ext/json/parser/parser.rl
@@ -719,7 +719,15 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
if (json->Vsource) {
rb_raise(rb_eTypeError, "already initialized instance");
}
- rb_scan_args(argc, argv, "1:", &source, &opts);
+
+ rb_check_arity(argc, 1, 2);
+ source = argv[0];
+ opts = Qnil;
+ if (argc == 2) {
+ opts = argv[1];
+ Check_Type(opts, T_HASH);
+ }
+
if (!NIL_P(opts)) {
VALUE tmp = ID2SYM(i_max_nesting);
if (option_given_p(opts, tmp)) {