Skip to content

Commit

Permalink
Convert Hash object using rb_hash_foreach()
Browse files Browse the repository at this point in the history
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
```
  • Loading branch information
Watson1978 authored and flori committed Apr 29, 2019
1 parent 167ada8 commit a73323d
Showing 1 changed file with 55 additions and 22 deletions.
77 changes: 55 additions & 22 deletions ext/json/ext/generator/generator.c
Expand Up @@ -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);
Expand Down

0 comments on commit a73323d

Please sign in to comment.