[flori/json] 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
```

https://github.com/flori/json/commit/a73323dc5e
This commit is contained in:
Watson 2018-03-02 00:33:18 +09:00 коммит произвёл Nobuyoshi Nakada
Родитель 2003755a2c
Коммит a2f9c38a71
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4BC7D6DF58D8DF60
1 изменённых файлов: 57 добавлений и 24 удалений

Просмотреть файл

@ -719,6 +719,53 @@ static VALUE cState_aset(VALUE self, VALUE name, VALUE value)
return Qnil;
}
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;
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;
@ -726,36 +773,22 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
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 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);