[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>
This commit is contained in:
Jean Boussier 2025-11-03 10:46:20 +01:00 committed by git
parent 35a5e55133
commit edebd83ea9
2 changed files with 12 additions and 25 deletions

View File

@ -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);

View File

@ -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}