-
Notifications
You must be signed in to change notification settings - Fork 339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize JSON::Pure::Generator by 2x-4x for simple options cases #586
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -239,6 +239,8 @@ def configure(opts) | |
opts.each do |key, value| | ||
instance_variable_set "@#{key}", value | ||
end | ||
|
||
# NOTE: If adding new instance variables here, check whether #generate should check them for #generate_json | ||
@indent = opts[:indent] if opts.key?(:indent) | ||
@space = opts[:space] if opts.key?(:space) | ||
@space_before = opts[:space_before] if opts.key?(:space_before) | ||
|
@@ -288,12 +290,70 @@ def to_h | |
# created this method raises a | ||
# GeneratorError exception. | ||
def generate(obj) | ||
result = obj.to_json(self) | ||
if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and | ||
!@ascii_only and !@script_safe and @max_nesting == 0 and !@strict | ||
result = generate_json(obj, '') | ||
else | ||
result = obj.to_json(self) | ||
end | ||
JSON.valid_utf8?(result) or raise GeneratorError, | ||
"source sequence #{result.inspect} is illegal/malformed utf-8" | ||
result | ||
end | ||
|
||
# Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above) | ||
private def generate_json(obj, buf) | ||
case obj | ||
when Hash | ||
buf << '{'.freeze | ||
first = true | ||
eregon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
obj.each_pair do |k,v| | ||
buf << ','.freeze unless first | ||
fast_serialize_string(k.to_s, buf) | ||
buf << ':'.freeze | ||
generate_json(v, buf) | ||
first = false | ||
end | ||
buf << '}'.freeze | ||
when Array | ||
buf << '['.freeze | ||
first = true | ||
obj.each do |e| | ||
buf << ','.freeze unless first | ||
generate_json(e, buf) | ||
first = false | ||
end | ||
buf << ']'.freeze | ||
when String | ||
fast_serialize_string(obj, buf) | ||
when Integer | ||
buf << obj.to_s | ||
else | ||
# Note: Float is handled this way since Float#to_s is slow anyway | ||
buf << obj.to_json(self) | ||
end | ||
end | ||
|
||
# Assumes !@ascii_only, !@script_safe | ||
if Regexp.method_defined?(:match?) | ||
private def fast_serialize_string(string, buf) # :nodoc: | ||
buf << '"'.freeze | ||
string = string.encode(::Encoding::UTF_8) unless string.encoding == ::Encoding::UTF_8 | ||
|
||
if /["\\\x0-\x1f]/n.match?(string) | ||
buf << string.gsub(/["\\\x0-\x1f]/n, MAP) | ||
else | ||
buf << string | ||
end | ||
buf << '"'.freeze | ||
end | ||
else | ||
# Ruby 2.3 compatibility | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point we might consider bumping the required version to 2.4 or even 2.5 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be nice, but this is not much overhead, so that could be cleaned up later when we bump the min Ruby version. |
||
private def fast_serialize_string(string, buf) # :nodoc: | ||
buf << string.to_json(self) | ||
end | ||
end | ||
|
||
# Return the value returned by method +name+. | ||
def [](name) | ||
if respond_to?(name) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like this could be precomputed in
configure
so you'd only need to check a single ivar, no?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmh, I'm not sure what's the protocol, the State class has a bunch of public
attr_accessor
so it looks likeconfigure
is not necessarily called after such accessors would be used to write.Or maybe JSON users are not supposed to call these accessors? What are they for then?
BTW it seems pretty inefficient that e.g. JSON.generate would create a new State every time (which runs the State constructor and configure every time) instead of using
SAFE_STATE_PROTOTYPE.dup
or so, but that's a separate thing so improve later.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity I did
To see if there were any inconsistency and the test suite didn't find any.
But still, I think we need to understand better how these accessors are used (or maybe remove them if they're not needed).