diff options
author | Watson <watson1978@gmail.com> | 2018-03-02 00:33:18 +0900 |
---|---|---|
committer | Florian Frank <flori@ping.de> | 2019-04-29 15:58:27 +0200 |
commit | a73323dc5e71bf285df3b82e02d3978fb8b2f16f (patch) | |
tree | 297da307fca85d24a429e8de913516efd388953d | |
parent | 167ada8da7299fd27314fadad3797b48cee442b1 (diff) | |
download | json-a73323dc5e71bf285df3b82e02d3978fb8b2f16f.tar.gz |
Convert Hash object using rb_hash_foreach()
To convert Hash convert, this part was using following pseudo code
```
obj.keys.each do |key|
value = obj[key]
...
end
```
and `rb_funcall()` was called for `obj.keys`.
It might be slightly heavy to call the Ruby method.
This patch will iterate to convert Hash object about key/value using `rb_hash_foreach()` Ruby API instead of `rb_funcall()`.
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 55.000 i/100ms
Calculating -------------------------------------
json 558.501 (± 1.1%) i/s - 2.805k in 5.022986s
```
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 65.000 i/100ms
Calculating -------------------------------------
json 659.576 (± 1.5%) i/s - 3.315k in 5.027127s
```
```
require 'json'
require 'benchmark/ips'
obj = []
1000.times do |i|
obj << {
"id" => i,
:age => 42,
}
end
Benchmark.ips do |x|
x.report "json" do |iter|
count = 0
while count < iter
JSON.generate(obj)
count += 1
end
end
end
```
-rw-r--r-- | ext/json/ext/generator/generator.c | 77 |
1 files changed, 55 insertions, 22 deletions
diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 2bf8074..e441236 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -715,43 +715,76 @@ static VALUE cState_aset(VALUE self, VALUE name, VALUE value) return Qnil; } -static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +struct hash_foreach_arg { + FBuffer *buffer; + JSON_Generator_State *state; + VALUE Vstate; + int iter; +}; + +static int +json_object_i(VALUE key, VALUE val, VALUE _arg) { + struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg; + FBuffer *buffer = arg->buffer; + JSON_Generator_State *state = arg->state; + VALUE Vstate = arg->Vstate; + char *object_nl = state->object_nl; long object_nl_len = state->object_nl_len; char *indent = state->indent; long indent_len = state->indent_len; - long max_nesting = state->max_nesting; char *delim = FBUFFER_PTR(state->object_delim); long delim_len = FBUFFER_LEN(state->object_delim); char *delim2 = FBUFFER_PTR(state->object_delim2); long delim2_len = FBUFFER_LEN(state->object_delim2); + long depth = state->depth; + int j; + VALUE key_to_s; + + if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len); + if (object_nl) { + fbuffer_append(buffer, object_nl, object_nl_len); + } + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + + key_to_s = rb_funcall(key, i_to_s, 0); + Check_Type(key_to_s, T_STRING); + generate_json(buffer, Vstate, state, key_to_s); + fbuffer_append(buffer, delim2, delim2_len); + generate_json(buffer, Vstate, state, val); + + arg->iter++; + return ST_CONTINUE; +} + +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) +{ + char *object_nl = state->object_nl; + long object_nl_len = state->object_nl_len; + char *indent = state->indent; + long indent_len = state->indent_len; + long max_nesting = state->max_nesting; long depth = ++state->depth; - int i, j; - VALUE key, key_to_s, keys; + int j; + struct hash_foreach_arg arg; + if (max_nesting != 0 && depth > max_nesting) { fbuffer_free(buffer); rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); } fbuffer_append_char(buffer, '{'); - keys = rb_funcall(obj, i_keys, 0); - for(i = 0; i < RARRAY_LEN(keys); i++) { - if (i > 0) fbuffer_append(buffer, delim, delim_len); - if (object_nl) { - fbuffer_append(buffer, object_nl, object_nl_len); - } - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - key = rb_ary_entry(keys, i); - key_to_s = rb_funcall(key, i_to_s, 0); - Check_Type(key_to_s, T_STRING); - generate_json(buffer, Vstate, state, key_to_s); - fbuffer_append(buffer, delim2, delim2_len); - generate_json(buffer, Vstate, state, rb_hash_aref(obj, key)); - } + + arg.buffer = buffer; + arg.state = state; + arg.Vstate = Vstate; + arg.iter = 0; + rb_hash_foreach(obj, json_object_i, (VALUE)&arg); + depth = --state->depth; if (object_nl) { fbuffer_append(buffer, object_nl, object_nl_len); |