Skip to content

Commit 25db79d

Browse files
committed
Convert Generator initialize and configure method into Ruby
This helps very marginally with allocation speed.
1 parent 45ba1f8 commit 25db79d

File tree

4 files changed

+160
-239
lines changed

4 files changed

+160
-239
lines changed

ext/json/ext/generator/generator.c

Lines changed: 30 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
1616
mTrueClass, mFalseClass, mNilClass, eGeneratorError,
1717
eNestingError;
1818

19-
static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
20-
i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
21-
i_pack, i_unpack, i_create_id, i_extend, i_key_p,
22-
i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
23-
i_buffer_initial_length, i_dup, i_script_safe, i_escape_slash, i_strict;
19+
static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend;
2420

2521
/* Converts in_string to a JSON string (without the wrapping '"'
2622
* characters) in FBuffer out_buffer.
@@ -451,180 +447,10 @@ static const rb_data_type_t JSON_Generator_State_type = {
451447
static VALUE cState_s_allocate(VALUE klass)
452448
{
453449
JSON_Generator_State *state;
454-
return TypedData_Make_Struct(klass, JSON_Generator_State,
455-
&JSON_Generator_State_type, state);
456-
}
457-
458-
/*
459-
* call-seq: configure(opts)
460-
*
461-
* Configure this State instance with the Hash _opts_, and return
462-
* itself.
463-
*/
464-
static VALUE cState_configure(VALUE self, VALUE opts)
465-
{
466-
VALUE tmp;
467-
GET_STATE(self);
468-
tmp = rb_check_convert_type(opts, T_HASH, "Hash", "to_hash");
469-
if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
470-
opts = tmp;
471-
tmp = rb_hash_aref(opts, ID2SYM(i_indent));
472-
if (RTEST(tmp)) {
473-
unsigned long len;
474-
Check_Type(tmp, T_STRING);
475-
len = RSTRING_LEN(tmp);
476-
state->indent = fstrndup(RSTRING_PTR(tmp), len + 1);
477-
state->indent_len = len;
478-
}
479-
tmp = rb_hash_aref(opts, ID2SYM(i_space));
480-
if (RTEST(tmp)) {
481-
unsigned long len;
482-
Check_Type(tmp, T_STRING);
483-
len = RSTRING_LEN(tmp);
484-
state->space = fstrndup(RSTRING_PTR(tmp), len + 1);
485-
state->space_len = len;
486-
}
487-
tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
488-
if (RTEST(tmp)) {
489-
unsigned long len;
490-
Check_Type(tmp, T_STRING);
491-
len = RSTRING_LEN(tmp);
492-
state->space_before = fstrndup(RSTRING_PTR(tmp), len + 1);
493-
state->space_before_len = len;
494-
}
495-
tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
496-
if (RTEST(tmp)) {
497-
unsigned long len;
498-
Check_Type(tmp, T_STRING);
499-
len = RSTRING_LEN(tmp);
500-
state->array_nl = fstrndup(RSTRING_PTR(tmp), len + 1);
501-
state->array_nl_len = len;
502-
}
503-
tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
504-
if (RTEST(tmp)) {
505-
unsigned long len;
506-
Check_Type(tmp, T_STRING);
507-
len = RSTRING_LEN(tmp);
508-
state->object_nl = fstrndup(RSTRING_PTR(tmp), len + 1);
509-
state->object_nl_len = len;
510-
}
511-
tmp = ID2SYM(i_max_nesting);
450+
VALUE obj = TypedData_Make_Struct(klass, JSON_Generator_State, &JSON_Generator_State_type, state);
512451
state->max_nesting = 100;
513-
if (option_given_p(opts, tmp)) {
514-
VALUE max_nesting = rb_hash_aref(opts, tmp);
515-
if (RTEST(max_nesting)) {
516-
Check_Type(max_nesting, T_FIXNUM);
517-
state->max_nesting = FIX2LONG(max_nesting);
518-
} else {
519-
state->max_nesting = 0;
520-
}
521-
}
522-
tmp = ID2SYM(i_depth);
523-
state->depth = 0;
524-
if (option_given_p(opts, tmp)) {
525-
VALUE depth = rb_hash_aref(opts, tmp);
526-
if (RTEST(depth)) {
527-
Check_Type(depth, T_FIXNUM);
528-
state->depth = FIX2LONG(depth);
529-
} else {
530-
state->depth = 0;
531-
}
532-
}
533-
tmp = ID2SYM(i_buffer_initial_length);
534-
if (option_given_p(opts, tmp)) {
535-
VALUE buffer_initial_length = rb_hash_aref(opts, tmp);
536-
if (RTEST(buffer_initial_length)) {
537-
long initial_length;
538-
Check_Type(buffer_initial_length, T_FIXNUM);
539-
initial_length = FIX2LONG(buffer_initial_length);
540-
if (initial_length > 0) state->buffer_initial_length = initial_length;
541-
}
542-
}
543-
tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
544-
state->allow_nan = RTEST(tmp);
545-
tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
546-
state->ascii_only = RTEST(tmp);
547-
tmp = rb_hash_aref(opts, ID2SYM(i_script_safe));
548-
state->script_safe = RTEST(tmp);
549-
if (!state->script_safe) {
550-
tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
551-
state->script_safe = RTEST(tmp);
552-
}
553-
tmp = rb_hash_aref(opts, ID2SYM(i_strict));
554-
state->strict = RTEST(tmp);
555-
return self;
556-
}
557-
558-
static void set_state_ivars(VALUE hash, VALUE state)
559-
{
560-
VALUE ivars = rb_obj_instance_variables(state);
561-
int i = 0;
562-
for (i = 0; i < RARRAY_LEN(ivars); i++) {
563-
VALUE key = rb_funcall(rb_ary_entry(ivars, i), i_to_s, 0);
564-
long key_len = RSTRING_LEN(key);
565-
VALUE value = rb_iv_get(state, StringValueCStr(key));
566-
rb_hash_aset(hash, rb_str_intern(rb_str_substr(key, 1, key_len - 1)), value);
567-
}
568-
}
569-
570-
/*
571-
* call-seq: to_h
572-
*
573-
* Returns the configuration instance variables as a hash, that can be
574-
* passed to the configure method.
575-
*/
576-
static VALUE cState_to_h(VALUE self)
577-
{
578-
VALUE result = rb_hash_new();
579-
GET_STATE(self);
580-
set_state_ivars(result, self);
581-
rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len));
582-
rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len));
583-
rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len));
584-
rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new(state->object_nl, state->object_nl_len));
585-
rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len));
586-
rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
587-
rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
588-
rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
589-
rb_hash_aset(result, ID2SYM(i_script_safe), state->script_safe ? Qtrue : Qfalse);
590-
rb_hash_aset(result, ID2SYM(i_strict), state->strict ? Qtrue : Qfalse);
591-
rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
592-
rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
593-
return result;
594-
}
595-
596-
/*
597-
* call-seq: [](name)
598-
*
599-
* Returns the value returned by method +name+.
600-
*/
601-
static VALUE cState_aref(VALUE self, VALUE name)
602-
{
603-
name = rb_funcall(name, i_to_s, 0);
604-
if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) {
605-
return rb_funcall(self, i_send, 1, name);
606-
} else {
607-
return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
608-
}
609-
}
610-
611-
/*
612-
* call-seq: []=(name, value)
613-
*
614-
* Sets the attribute name to value.
615-
*/
616-
static VALUE cState_aset(VALUE self, VALUE name, VALUE value)
617-
{
618-
VALUE name_writer;
619-
620-
name = rb_funcall(name, i_to_s, 0);
621-
name_writer = rb_str_cat2(rb_str_dup(name), "=");
622-
if (RTEST(rb_funcall(self, i_respond_to_p, 1, name_writer))) {
623-
return rb_funcall(self, i_send, 2, name_writer, value);
624-
} else {
625-
rb_ivar_set(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)), value);
626-
}
627-
return Qnil;
452+
state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
453+
return obj;
628454
}
629455

630456
struct hash_foreach_arg {
@@ -933,37 +759,6 @@ static VALUE cState_generate(VALUE self, VALUE obj)
933759
return result;
934760
}
935761

936-
/*
937-
* call-seq: new(opts = {})
938-
*
939-
* Instantiates a new State object, configured by _opts_.
940-
*
941-
* _opts_ can have the following keys:
942-
*
943-
* * *indent*: a string used to indent levels (default: ''),
944-
* * *space*: a string that is put after, a : or , delimiter (default: ''),
945-
* * *space_before*: a string that is put before a : pair delimiter (default: ''),
946-
* * *object_nl*: a string that is put at the end of a JSON object (default: ''),
947-
* * *array_nl*: a string that is put at the end of a JSON array (default: ''),
948-
* * *allow_nan*: true if NaN, Infinity, and -Infinity should be
949-
* generated, otherwise an exception is thrown, if these values are
950-
* encountered. This options defaults to false.
951-
* * *ascii_only*: true if only ASCII characters should be generated. This
952-
* option defaults to false.
953-
* * *buffer_initial_length*: sets the initial length of the generator's
954-
* internal buffer.
955-
*/
956-
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
957-
{
958-
VALUE opts;
959-
GET_STATE(self);
960-
state->max_nesting = 100;
961-
state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
962-
rb_scan_args(argc, argv, "01", &opts);
963-
if (!NIL_P(opts)) cState_configure(self, opts);
964-
return self;
965-
}
966-
967762
/*
968763
* call-seq: initialize_copy(orig)
969764
*
@@ -1294,6 +1089,18 @@ static VALUE cState_allow_nan_p(VALUE self)
12941089
return state->allow_nan ? Qtrue : Qfalse;
12951090
}
12961091

1092+
/*
1093+
* call-seq: allow_nan=(enable)
1094+
*
1095+
* This sets whether or not to serialize NaN, Infinity, and -Infinity
1096+
*/
1097+
static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
1098+
{
1099+
GET_STATE(self);
1100+
state->allow_nan = RTEST(enable);
1101+
return Qnil;
1102+
}
1103+
12971104
/*
12981105
* call-seq: ascii_only?
12991106
*
@@ -1306,6 +1113,18 @@ static VALUE cState_ascii_only_p(VALUE self)
13061113
return state->ascii_only ? Qtrue : Qfalse;
13071114
}
13081115

1116+
/*
1117+
* call-seq: ascii_only=(enable)
1118+
*
1119+
* This sets whether only ASCII characters should be generated.
1120+
*/
1121+
static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
1122+
{
1123+
GET_STATE(self);
1124+
state->ascii_only = RTEST(enable);
1125+
return Qnil;
1126+
}
1127+
13091128
/*
13101129
* call-seq: depth
13111130
*
@@ -1384,7 +1203,6 @@ void Init_generator(void)
13841203
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
13851204
rb_define_alloc_func(cState, cState_s_allocate);
13861205
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
1387-
rb_define_method(cState, "initialize", cState_initialize, -1);
13881206
rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
13891207
rb_define_method(cState, "indent", cState_indent, 0);
13901208
rb_define_method(cState, "indent=", cState_indent_set, 1);
@@ -1409,17 +1227,13 @@ void Init_generator(void)
14091227
rb_define_method(cState, "strict=", cState_strict_set, 1);
14101228
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
14111229
rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
1230+
rb_define_method(cState, "allow_nan=", cState_allow_nan_set, 1);
14121231
rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
1232+
rb_define_method(cState, "ascii_only=", cState_ascii_only_set, 1);
14131233
rb_define_method(cState, "depth", cState_depth, 0);
14141234
rb_define_method(cState, "depth=", cState_depth_set, 1);
14151235
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
14161236
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
1417-
rb_define_method(cState, "configure", cState_configure, 1);
1418-
rb_define_alias(cState, "merge", "configure");
1419-
rb_define_method(cState, "to_h", cState_to_h, 0);
1420-
rb_define_alias(cState, "to_hash", "to_h");
1421-
rb_define_method(cState, "[]", cState_aref, 1);
1422-
rb_define_method(cState, "[]=", cState_aset, 2);
14231237
rb_define_method(cState, "generate", cState_generate, 1);
14241238

14251239
mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
@@ -1457,30 +1271,10 @@ void Init_generator(void)
14571271
i_to_s = rb_intern("to_s");
14581272
i_to_json = rb_intern("to_json");
14591273
i_new = rb_intern("new");
1460-
i_indent = rb_intern("indent");
1461-
i_space = rb_intern("space");
1462-
i_space_before = rb_intern("space_before");
1463-
i_object_nl = rb_intern("object_nl");
1464-
i_array_nl = rb_intern("array_nl");
1465-
i_max_nesting = rb_intern("max_nesting");
1466-
i_script_safe = rb_intern("script_safe");
1467-
i_escape_slash = rb_intern("escape_slash");
1468-
i_strict = rb_intern("strict");
1469-
i_allow_nan = rb_intern("allow_nan");
1470-
i_ascii_only = rb_intern("ascii_only");
1471-
i_depth = rb_intern("depth");
1472-
i_buffer_initial_length = rb_intern("buffer_initial_length");
14731274
i_pack = rb_intern("pack");
14741275
i_unpack = rb_intern("unpack");
14751276
i_create_id = rb_intern("create_id");
14761277
i_extend = rb_intern("extend");
1477-
i_key_p = rb_intern("key?");
1478-
i_aref = rb_intern("[]");
1479-
i_send = rb_intern("__send__");
1480-
i_respond_to_p = rb_intern("respond_to?");
1481-
i_match = rb_intern("match");
1482-
i_keys = rb_intern("keys");
1483-
i_dup = rb_intern("dup");
14841278

14851279
usascii_encindex = rb_usascii_encindex();
14861280
utf8_encindex = rb_utf8_encindex();

ext/json/ext/generator/generator.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self);
103103
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self);
104104
static void State_free(void *state);
105105
static VALUE cState_s_allocate(VALUE klass);
106-
static VALUE cState_configure(VALUE self, VALUE opts);
107-
static VALUE cState_to_h(VALUE self);
108106
static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
109107
static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
110108
static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
@@ -120,7 +118,6 @@ static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
120118
static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
121119
static VALUE cState_partial_generate(VALUE self, VALUE obj);
122120
static VALUE cState_generate(VALUE self, VALUE obj);
123-
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self);
124121
static VALUE cState_from_state_s(VALUE self, VALUE opts);
125122
static VALUE cState_indent(VALUE self);
126123
static VALUE cState_indent_set(VALUE self, VALUE indent);

lib/json/ext.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require 'json/common'
24

35
module JSON
@@ -13,6 +15,9 @@ module Ext
1315
else
1416
require 'json/ext/parser'
1517
require 'json/ext/generator'
18+
unless RUBY_ENGINE == 'jruby'
19+
require 'json/ext/generator/state'
20+
end
1621
$DEBUG and warn "Using Ext extension for JSON."
1722
JSON.parser = Parser
1823
JSON.generator = Generator

0 commit comments

Comments
 (0)