Skip to content

Commit 75b4380

Browse files
etiennebarriebyroot
andcommitted
Optimize JSON escaping
Instead of using multiple gsub! calls, this uses binary encoding to do the regexp gsub with map, then forces encoding back to UTF-8. This uses the same approach as before ebe0c40, with binary encoding, which performs better. Co-authored-by: Jean Boussier <[email protected]>
1 parent 8d4cbef commit 75b4380

File tree

1 file changed

+24
-12
lines changed

1 file changed

+24
-12
lines changed

activesupport/lib/active_support/json/encoding.rb

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ def encode(value, options = nil)
3030
end
3131

3232
module Encoding # :nodoc:
33+
U2028 = -"\u2028".b
34+
U2029 = -"\u2029".b
35+
36+
ESCAPED_CHARS = {
37+
U2028 => '\u2028'.b,
38+
U2029 => '\u2029'.b,
39+
">".b => '\u003e'.b,
40+
"<".b => '\u003c'.b,
41+
"&".b => '\u0026'.b,
42+
}
43+
44+
ESCAPE_REGEX_WITH_HTML_ENTITIES = Regexp.union(*ESCAPED_CHARS.keys)
45+
ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = Regexp.union(U2028, U2029)
46+
3347
class JSONGemEncoder # :nodoc:
3448
attr_reader :options
3549

@@ -47,14 +61,13 @@ def encode(value)
4761
# Rails does more escaping than the JSON gem natively does (we
4862
# escape \u2028 and \u2029 and optionally >, <, & to work around
4963
# certain browser problems).
64+
json.force_encoding(::Encoding::BINARY)
5065
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
51-
json.gsub!(">", '\u003e')
52-
json.gsub!("<", '\u003c')
53-
json.gsub!("&", '\u0026')
66+
json.gsub!(ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS)
67+
else
68+
json.gsub!(ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS)
5469
end
55-
json.gsub!("\u2028", '\u2028')
56-
json.gsub!("\u2029", '\u2029')
57-
json
70+
json.force_encoding(::Encoding::UTF_8)
5871
end
5972

6073
private
@@ -130,14 +143,13 @@ def encode(value)
130143
# Rails does more escaping than the JSON gem natively does (we
131144
# escape \u2028 and \u2029 and optionally >, <, & to work around
132145
# certain browser problems).
146+
json.force_encoding(::Encoding::BINARY)
133147
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
134-
json.gsub!(">", '\u003e')
135-
json.gsub!("<", '\u003c')
136-
json.gsub!("&", '\u0026')
148+
json.gsub!(ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS)
149+
else
150+
json.gsub!(ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS)
137151
end
138-
json.gsub!("\u2028", '\u2028')
139-
json.gsub!("\u2029", '\u2029')
140-
json
152+
json.force_encoding(::Encoding::UTF_8)
141153
end
142154
end
143155
end

0 commit comments

Comments
 (0)