diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2025-11-03 10:46:20 +0100 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2025-11-03 09:58:01 +0000 |
| commit | edebd83ea99651a47d4592f2cef12618da3a0736 (patch) | |
| tree | 55d6dc6488e06dc5ef79ce1e7ac3f0fb0e2c4d54 | |
| parent | 35a5e5513377d0d1f13c3ab15966a5c51b24066c (diff) | |
[ruby/json] parser.c: Skip checking for escape sequences in `rstring_cache_fetch`
The caller already know if the string contains escape sequences
so this check is redundant.
Also stop calling `rstring_cache_fetch` from `json_string_unescape`
as we know it won't match anyways.
```
== Parsing twitter.json (567916 bytes)
ruby 3.4.6 (2025-09-16 revision https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
after 122.000 i/100ms
Calculating -------------------------------------
after 1.226k (± 0.3%) i/s (815.85 μs/i) - 6.222k in 5.076282s
Comparison:
before: 1206.2 i/s
after: 1225.7 i/s - 1.02x faster
```
https://github.com/ruby/json/commit/b8cdf3282d
Co-Authored-By: Scott Myron <samyron@gmail.com>
| -rw-r--r-- | ext/json/parser/parser.c | 25 | ||||
| -rw-r--r-- | test/json/json_parser_test.rb | 12 |
2 files changed, 12 insertions, 25 deletions
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index d9b578f245..68ff6d7ac3 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -122,12 +122,6 @@ static VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const lon } } - if (RB_UNLIKELY(memchr(str, '\\', length))) { - // We assume the overwhelming majority of names don't need to be escaped. - // But if they do, we have to fallback to the slow path. - return Qfalse; - } - VALUE rstring = build_interned_string(str, length); if (cache->length < JSON_RVALUE_CACHE_CAPA) { @@ -174,12 +168,6 @@ static VALUE rsymbol_cache_fetch(rvalue_cache *cache, const char *str, const lon } } - if (RB_UNLIKELY(memchr(str, '\\', length))) { - // We assume the overwhelming majority of names don't need to be escaped. - // But if they do, we have to fallback to the slow path. - return Qfalse; - } - VALUE rsymbol = build_symbol(str, length); if (cache->length < JSON_RVALUE_CACHE_CAPA) { @@ -652,19 +640,6 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c int unescape_len; char buf[4]; - if (is_name && state->in_array) { - VALUE cached_key; - if (RB_UNLIKELY(symbolize)) { - cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); - } else { - cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); - } - - if (RB_LIKELY(cached_key)) { - return cached_key; - } - } - VALUE result = rb_str_buf_new(bufferSize); rb_enc_associate_index(result, utf8_encindex); buffer = RSTRING_PTR(result); diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index bab16a6fc2..6315c3e667 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -344,6 +344,18 @@ class JSONParserTest < Test::Unit::TestCase assert_equal orig, parse(json5) end + def test_parse_escaped_key + doc = { + "test\r1" => 1, + "entries" => [ + "test\t2" => 2, + "test\n3" => 3, + ] + } + + assert_equal doc, parse(JSON.generate(doc)) + end + def test_parse_duplicate_key expected = {"a" => 2} expected_sym = {a: 2} |
