From 59eab2b7ad1b44dc424fb3fffa5520947f7360f3 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Sat, 24 Jul 2010 17:23:05 +0200 Subject: use method dispatch for generation again --- ext/json/ext/generator/generator.c | 439 +++++++++++++++++++++---------------- ext/json/ext/generator/generator.h | 10 +- lib/json/common.rb | 12 +- 3 files changed, 264 insertions(+), 197 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index f9a7d06..3f5bb05 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -6,9 +6,10 @@ static ID i_encoding, i_encode; #endif static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, - mHash, mArray, mInteger, mFloat, mString, mString_Extend, + mHash, mArray, mFixnum, mBignum, mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE; + eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE, + DO_NOT_APPEND; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, @@ -426,11 +427,66 @@ static FBuffer *fbuffer_dup(FBuffer *fb) */ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, Vdepth; + + rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(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); + int i, j; + VALUE key, key_name, keys, value; + long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth); + FBuffer *buffer = state->buffer; + depth++; + if (max_nesting != 0 && depth > max_nesting) { + rb_raise(eNestingError, "nesting of %ld is too deep", depth); + } + fbuffer_append_char(buffer, '{'); + keys = rb_funcall(self, rb_intern("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_name = rb_funcall(key, i_to_s, 0); + Check_Type(key_name, T_STRING); + key_name = rb_funcall(key_name, i_to_json, 2, Vstate, LONG2FIX(depth)); + fbuffer_append(buffer, delim2, delim2_len); + value = rb_funcall(rb_hash_aref(self, key), i_to_json, 2, Vstate, LONG2FIX(depth)); + if (value != DO_NOT_APPEND) { + Check_Type(value, T_STRING); + fbuffer_append(buffer, RSTRING_PAIR(value)); + } + } + depth--; + 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); + } + } + } + fbuffer_append_char(buffer, '}'); + } + return cState_result(Vstate, returnResult); } /* @@ -443,10 +499,55 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) * _depth_ is used to find out nesting depth, to indent accordingly. */ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, Vdepth; + rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(Vstate); + FBuffer *buffer = state->buffer; + VALUE value; + char *array_nl = state->array_nl; + long array_nl_len = state->array_nl_len; + char *indent = state->indent; + long indent_len = state->indent_len; + long max_nesting = state->max_nesting; + char *delim = FBUFFER_PTR(state->array_delim); + long delim_len = FBUFFER_LEN(state->array_delim); + int i, j; + long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth); + depth++; + if (max_nesting != 0 && depth > max_nesting) { + rb_raise(eNestingError, "nesting of %ld is too deep", depth); + } + fbuffer_append_char(buffer, '['); + if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); + for(i = 0; i < RARRAY_LEN(self); i++) { + if (i > 0) fbuffer_append(buffer, delim, delim_len); + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + value = rb_funcall(rb_ary_entry(self, i), i_to_json, 2, Vstate, LONG2FIX(depth)); + if (value != DO_NOT_APPEND) { + Check_Type(value, T_STRING); + fbuffer_append(buffer, RSTRING_PAIR(value)); + } + } + depth--; + if (array_nl) { + fbuffer_append(buffer, array_nl, array_nl_len); + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + } + fbuffer_append_char(buffer, ']'); + } + return cState_result(Vstate, returnResult); } /* @@ -454,12 +555,39 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { * * Returns a JSON string representation for this Integer number. */ -static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) +static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, depth; + rb_scan_args(argc, argv, "02", &Vstate, &depth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(Vstate); + fbuffer_append_long(state->buffer, FIX2LONG(self)); + } + return cState_result(Vstate, returnResult); +} + +/* + * call-seq: to_json(*) + * + * Returns a JSON string representation for this Integer number. + */ +static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) +{ + int returnResult; + VALUE Vstate, depth; + rb_scan_args(argc, argv, "02", &Vstate, &depth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + VALUE tmp; + GET_STATE(Vstate); + tmp = rb_funcall(self, i_to_s, 0); + fbuffer_append(state->buffer, RSTRING_PAIR(tmp)); + } + return cState_result(Vstate, returnResult); } /* @@ -469,10 +597,27 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, depth; + rb_scan_args(argc, argv, "02", &Vstate, &depth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + VALUE tmp; + GET_STATE(Vstate); + double value = RFLOAT_VALUE(self); + char allow_nan = state->allow_nan; + tmp = rb_funcall(self, i_to_s, 0); + if (!allow_nan) { + if (isinf(value)) { + rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); + } else if (isnan(value)) { + rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); + } + } + fbuffer_append(state->buffer, RSTRING_PAIR(tmp)); + } + return cState_result(Vstate, returnResult); } /* @@ -485,6 +630,7 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { return result; } + /* * call-seq: to_json(*) * @@ -494,10 +640,16 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { */ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, depth; + rb_scan_args(argc, argv, "02", &Vstate, &depth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(Vstate); + State_append_ruby_string_to_fbuffer(state, self); + } + return cState_result(Vstate, returnResult); } /* @@ -552,10 +704,16 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) */ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, Vdepth; + rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(Vstate); + fbuffer_append(state->buffer, "true", 4); + } + return cState_result(Vstate, returnResult); } /* @@ -565,10 +723,16 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, Vdepth; + rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(Vstate); + fbuffer_append(state->buffer, "false", 5); + } + return cState_result(Vstate, returnResult); } /* @@ -577,10 +741,16 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - rb_scan_args(argc, argv, "02", &state, &depth); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, self, depth); + int returnResult; + VALUE Vstate, Vdepth; + rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + GET_STATE(Vstate); + fbuffer_append(state->buffer, "null", 4); + } + return cState_result(Vstate, returnResult); } /* @@ -592,12 +762,19 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; - VALUE string = rb_funcall(self, i_to_s, 0); - rb_scan_args(argc, argv, "02", &state, &depth); - Check_Type(string, T_STRING); - state = cState_from_state_s(cState, state); - return cState_partial_generate(state, string, depth); + int returnResult; + VALUE Vstate, Vdepth; + rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + Vstate = cState_from_state_s(cState, Vstate); + returnResult = cState_prepare_buffer(Vstate); + { + VALUE tmp; + GET_STATE(Vstate); + tmp = rb_funcall(self, i_to_s, 0); + Check_Type(tmp, T_STRING); + State_append_ruby_string_to_fbuffer(state, tmp); + } + return cState_result(Vstate, returnResult); } static void State_free(JSON_Generator_State *state) @@ -610,6 +787,7 @@ static void State_free(JSON_Generator_State *state) if (state->array_delim) fbuffer_free(state->array_delim); if (state->object_delim) fbuffer_free(state->object_delim); if (state->object_delim2) fbuffer_free(state->object_delim2); + if (state->buffer) fbuffer_free(state->buffer); ruby_xfree(state); } @@ -619,6 +797,22 @@ static JSON_Generator_State *State_allocate() return state; } +static void State_append_ruby_string_to_fbuffer(JSON_Generator_State *state, VALUE string) +{ + FBuffer *buffer = state->buffer; + + fbuffer_append_char(buffer, '"'); +#ifdef HAVE_RUBY_ENCODING_H + string = rb_funcall(string, i_encode, 1, CEncoding_UTF_8); +#endif + if (state->ascii_only) { + convert_UTF8_to_JSON_ASCII(buffer, string); + } else { + convert_UTF8_to_JSON(buffer, string); + } + fbuffer_append_char(buffer, '"'); +} + static VALUE cState_s_allocate(VALUE klass) { JSON_Generator_State *state = State_allocate(); @@ -735,136 +929,15 @@ static VALUE cState_aref(VALUE self, VALUE name) } } -static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static VALUE cState_result(VALUE self, int returnResult) { - VALUE tmp; - VALUE klass = CLASS_OF(obj); - if (klass == rb_cHash) { - 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); - int i, j; - VALUE key, key_to_s, keys; - depth++; - if (max_nesting != 0 && depth > max_nesting) { - fbuffer_free(buffer); - rb_raise(eNestingError, "nesting of %ld is too deep", depth); - } - fbuffer_append_char(buffer, '{'); - keys = rb_funcall(obj, rb_intern("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, depth); - fbuffer_append(buffer, delim2, delim2_len); - generate_json(buffer, Vstate, state, rb_hash_aref(obj, key), depth); - } - depth--; - 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); - } - } - } - fbuffer_append_char(buffer, '}'); - } else if (klass == rb_cArray) { - char *array_nl = state->array_nl; - long array_nl_len = state->array_nl_len; - char *indent = state->indent; - long indent_len = state->indent_len; - long max_nesting = state->max_nesting; - char *delim = FBUFFER_PTR(state->array_delim); - long delim_len = FBUFFER_LEN(state->array_delim); - int i, j; - depth++; - if (max_nesting != 0 && depth > max_nesting) { - fbuffer_free(buffer); - rb_raise(eNestingError, "nesting of %ld is too deep", depth); - } - fbuffer_append_char(buffer, '['); - if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); - for(i = 0; i < RARRAY_LEN(obj); i++) { - if (i > 0) fbuffer_append(buffer, delim, delim_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - generate_json(buffer, Vstate, state, rb_ary_entry(obj, i), depth); - } - depth--; - if (array_nl) { - fbuffer_append(buffer, array_nl, array_nl_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - } - fbuffer_append_char(buffer, ']'); - } else if (klass == rb_cString) { - fbuffer_append_char(buffer, '"'); -#ifdef HAVE_RUBY_ENCODING_H - obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8); -#endif - if (state->ascii_only) { - convert_UTF8_to_JSON_ASCII(buffer, obj); - } else { - convert_UTF8_to_JSON(buffer, obj); - } - fbuffer_append_char(buffer, '"'); - } else if (obj == Qnil) { - fbuffer_append(buffer, "null", 4); - } else if (obj == Qfalse) { - fbuffer_append(buffer, "false", 5); - } else if (obj == Qtrue) { - fbuffer_append(buffer, "true", 4); - } else if (klass == rb_cFixnum) { - fbuffer_append_long(buffer, FIX2LONG(obj)); - } else if (klass == rb_cBignum) { - tmp = rb_funcall(obj, i_to_s, 0); - fbuffer_append(buffer, RSTRING_PAIR(tmp)); - } else if (klass == rb_cFloat) { - double value = RFLOAT_VALUE(obj); - char allow_nan = state->allow_nan; - tmp = rb_funcall(obj, i_to_s, 0); - if (!allow_nan) { - if (isinf(value)) { - fbuffer_free(buffer); - rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); - } else if (isnan(value)) { - fbuffer_free(buffer); - rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); - } - } - fbuffer_append(buffer, RSTRING_PAIR(tmp)); - } else if (rb_respond_to(obj, i_to_json)) { - tmp = rb_funcall(obj, i_to_json, 2, Vstate, INT2FIX(depth + 1)); - Check_Type(tmp, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(tmp)); - } else { - tmp = rb_funcall(obj, i_to_s, 0); - Check_Type(tmp, T_STRING); - generate_json(buffer, Vstate, state, tmp, depth + 1); + VALUE result = DO_NOT_APPEND; + if (returnResult) { + GET_STATE(self); + result = rb_str_new(FBUFFER_PAIR(state->buffer)); + FORCE_UTF8(result); } + return result; } /* @@ -873,12 +946,12 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s * Generates a part of a JSON document from object +obj+ and returns the * result. */ -static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) +static int cState_prepare_buffer(VALUE self) { - VALUE result; - FBuffer *buffer = fbuffer_alloc(); GET_STATE(self); + if (state->buffer) return 0; + state->buffer = fbuffer_alloc(); if (state->object_delim) { fbuffer_clear(state->object_delim); } else { @@ -900,12 +973,7 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) } fbuffer_append_char(state->array_delim, ','); if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len); - - generate_json(buffer, self, state, obj, NIL_P(depth) ? 0 : FIX2INT(depth)); - result = rb_str_new(FBUFFER_PAIR(buffer)); - fbuffer_free(buffer); - FORCE_UTF8(result); - return result; + return 1; } /* @@ -917,7 +985,7 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) */ static VALUE cState_generate(VALUE self, VALUE obj) { - VALUE result = cState_partial_generate(self, obj, Qnil); + VALUE result = rb_funcall(obj, i_to_json, 1, self); VALUE re, args[2]; args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); args[1] = CRegexp_MULTILINE; @@ -998,7 +1066,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, rb_intern("SAFE_STATE_PROTOTYPE")); } - return CJSON_SAFE_STATE_PROTOTYPE; + return rb_obj_dup(CJSON_SAFE_STATE_PROTOTYPE); } } @@ -1266,7 +1334,6 @@ void Init_generator() rb_define_method(cState, "to_h", cState_to_h, 0); rb_define_method(cState, "[]", cState_aref, 1); rb_define_method(cState, "generate", cState_generate, 1); - rb_define_method(cState, "partial_generate", cState_partial_generate, 1); mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); mObject = rb_define_module_under(mGeneratorMethods, "Object"); @@ -1275,8 +1342,10 @@ void Init_generator() rb_define_method(mHash, "to_json", mHash_to_json, -1); mArray = rb_define_module_under(mGeneratorMethods, "Array"); rb_define_method(mArray, "to_json", mArray_to_json, -1); - mInteger = rb_define_module_under(mGeneratorMethods, "Integer"); - rb_define_method(mInteger, "to_json", mInteger_to_json, -1); + mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); + rb_define_method(mBignum, "to_json", mBignum_to_json, -1); + mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); + rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1); mFloat = rb_define_module_under(mGeneratorMethods, "Float"); rb_define_method(mFloat, "to_json", mFloat_to_json, -1); mString = rb_define_module_under(mGeneratorMethods, "String"); @@ -1320,4 +1389,6 @@ void Init_generator() i_encode = rb_intern("encode"); #endif CJSON_SAFE_STATE_PROTOTYPE = Qnil; + DO_NOT_APPEND = rb_obj_alloc(rb_cObject); + rb_obj_call_init(DO_NOT_APPEND, 0, NULL); } diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index 37240a9..fcf68bb 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -123,6 +123,7 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; + FBuffer *buffer; } JSON_Generator_State; #define GET_STATE(self) \ @@ -131,7 +132,8 @@ typedef struct JSON_Generator_StateStruct { static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self); static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self); +static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self); +static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self); static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self); static VALUE mString_included_s(VALUE self, VALUE modul); static VALUE mString_to_json(int argc, VALUE *argv, VALUE self); @@ -147,9 +149,8 @@ static JSON_Generator_State *State_allocate(); static VALUE cState_s_allocate(VALUE klass); static VALUE cState_configure(VALUE self, VALUE opts); static VALUE cState_to_h(VALUE self); -static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth); -static VALUE cState_generate(VALUE self, VALUE obj); +static int cState_prepare_buffer(VALUE self); +static VALUE cState_result(VALUE self, int returnResult); static VALUE cState_initialize(int argc, VALUE *argv, VALUE self); static VALUE cState_from_state_s(VALUE self, VALUE opts); static VALUE cState_indent(VALUE self); @@ -166,5 +167,6 @@ static VALUE cState_max_nesting(VALUE self); static VALUE cState_max_nesting_set(VALUE self, VALUE depth); static VALUE cState_allow_nan_p(VALUE self); static VALUE cState_ascii_only_p(VALUE self); +static void State_append_ruby_string_to_fbuffer(JSON_Generator_State *state, VALUE string); #endif diff --git a/lib/json/common.rb b/lib/json/common.rb index 244634b..13c09ca 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -196,6 +196,7 @@ module JSON # amount of sanity checks, and the pretty_generate method for some # defaults for a pretty output. def generate(obj, opts = nil) + state = SAFE_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -204,10 +205,7 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end - state = SAFE_STATE_PROTOTYPE.dup state = state.configure(opts) - else - state = SAFE_STATE_PROTOTYPE end state.generate(obj) end @@ -225,6 +223,7 @@ module JSON # *WARNING*: Be careful not to pass any Ruby data structures with circles as # _obj_ argument, because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) + state = FAST_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -233,10 +232,7 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end - state = FAST_STATE_PROTOTYPE.dup state.configure(opts) - else - state = FAST_STATE_PROTOTYPE end state.generate(obj) end @@ -254,6 +250,7 @@ module JSON # The _opts_ argument can be used to configure the generator, see the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) + state = PRETTY_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -262,10 +259,7 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end - state = PRETTY_STATE_PROTOTYPE.dup state.configure(opts) - else - state = PRETTY_STATE_PROTOTYPE end state.generate(obj) end -- cgit v1.2.1 From 9f156be0fe40f57e4a5727904d5c2b761fa2cef2 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Tue, 3 Aug 2010 03:47:48 +0200 Subject: use rb_funcall for duping rbx doesn't support rb_obj_dup :-( --- ext/json/ext/generator/generator.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 761274b..7658acd 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -14,7 +14,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, - i_respond_to_p, i_match; + i_respond_to_p, i_match, i_keys, i_dup; /* * Copyright 2001-2004 Unicode, Inc. @@ -1017,7 +1017,6 @@ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self) { VALUE opts; GET_STATE(self); - MEMZERO(state, JSON_Generator_State, 1); state->max_nesting = 19; rb_scan_args(argc, argv, "01", &opts); if (!NIL_P(opts)) cState_configure(self, opts); @@ -1067,7 +1066,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, rb_intern("SAFE_STATE_PROTOTYPE")); } - return rb_obj_dup(CJSON_SAFE_STATE_PROTOTYPE); + return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0); } } @@ -1384,6 +1383,8 @@ void Init_generator() i_send = rb_intern("__send__"); i_respond_to_p = rb_intern("respond_to?"); i_match = rb_intern("match"); + i_keys = rb_intern("keys"); + i_dup = rb_intern("dup"); #ifdef HAVE_RUBY_ENCODING_H CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); i_encoding = rb_intern("encoding"); -- cgit v1.2.1 From f0a14faf1d59a5ea86a79d3a5780e8433bfc9e16 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Sat, 24 Jul 2010 17:23:05 +0200 Subject: use method dispatch for generation again --- ext/json/ext/generator/generator.c | 2 +- tests/test_json.rb | 36 +++++++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 7658acd..3786b65 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -453,7 +453,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) rb_raise(eNestingError, "nesting of %ld is too deep", depth); } fbuffer_append_char(buffer, '{'); - keys = rb_funcall(self, rb_intern("keys"), 0); + keys = rb_funcall(self, i_keys, 0); for(i = 0; i < RARRAY_LEN(keys); i++) { if (i > 0) fbuffer_append(buffer, delim, delim_len); if (object_nl) { diff --git a/tests/test_json.rb b/tests/test_json.rb index 19b742f..a88049a 100755 --- a/tests/test_json.rb +++ b/tests/test_json.rb @@ -160,6 +160,20 @@ class TC_JSON < Test::Unit::TestCase class SubArray < Array; end + class SubArray2 < Array + def to_json(*a) + { + JSON.create_id => self.class.name, + 'ary' => to_a, + }.to_json(*a) + end + + def self.json_create(o) + o.delete JSON.create_id + o['ary'] + end + end + def test_parse_array_custom_class res = parse('[]', :array_class => SubArray) assert_equal([], res) @@ -173,7 +187,9 @@ class TC_JSON < Test::Unit::TestCase assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } ')) end - class SubHash < Hash + class SubHash < Hash; end + + class SubHash2 < Hash def to_json(*a) { JSON.create_id => self.class.name, @@ -187,19 +203,25 @@ class TC_JSON < Test::Unit::TestCase end def test_parse_object_custom_class - res = parse('{}', :object_class => SubHash) + res = parse('{}', :object_class => SubHash2) assert_equal({}, res) - assert_equal(SubHash, res.class) + assert_equal(SubHash2, res.class) end - def test_generation_of_core_subclasses - obj = SubHash.new.merge( "foo" => SubHash.new.merge("bar" => true)) + def test_generation_of_core_subclasses_with_new_to_json + obj = SubHash2.new.merge( "foo" => SubHash2.new.merge("bar" => true)) obj_json = JSON(obj) obj_again = JSON(obj_json) - assert_kind_of SubHash, obj_again - assert_kind_of SubHash, obj_again['foo'] + assert_kind_of SubHash2, obj_again + assert_kind_of SubHash2, obj_again['foo'] assert obj_again['foo']['bar'] assert_equal obj, obj_again + assert_equal ["foo"], JSON(JSON(SubArray2["foo"])) + end + + def test_generation_of_core_subclasses_with_default_to_json + assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"]) + assert_equal '["foo"]', JSON(SubArray["foo"]) end def test_parser_reset -- cgit v1.2.1 From 646cc01a6677905fe0ef7fb35aff7bca4fd68123 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Thu, 5 Aug 2010 03:10:21 +0200 Subject: Revert "use method dispatch for generation again" This reverts commit f0a14faf1d59a5ea86a79d3a5780e8433bfc9e16. --- ext/json/ext/generator/generator.c | 2 +- tests/test_json.rb | 36 +++++++----------------------------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 3786b65..7658acd 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -453,7 +453,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) rb_raise(eNestingError, "nesting of %ld is too deep", depth); } fbuffer_append_char(buffer, '{'); - keys = rb_funcall(self, i_keys, 0); + keys = rb_funcall(self, rb_intern("keys"), 0); for(i = 0; i < RARRAY_LEN(keys); i++) { if (i > 0) fbuffer_append(buffer, delim, delim_len); if (object_nl) { diff --git a/tests/test_json.rb b/tests/test_json.rb index a88049a..19b742f 100755 --- a/tests/test_json.rb +++ b/tests/test_json.rb @@ -160,20 +160,6 @@ class TC_JSON < Test::Unit::TestCase class SubArray < Array; end - class SubArray2 < Array - def to_json(*a) - { - JSON.create_id => self.class.name, - 'ary' => to_a, - }.to_json(*a) - end - - def self.json_create(o) - o.delete JSON.create_id - o['ary'] - end - end - def test_parse_array_custom_class res = parse('[]', :array_class => SubArray) assert_equal([], res) @@ -187,9 +173,7 @@ class TC_JSON < Test::Unit::TestCase assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } ')) end - class SubHash < Hash; end - - class SubHash2 < Hash + class SubHash < Hash def to_json(*a) { JSON.create_id => self.class.name, @@ -203,25 +187,19 @@ class TC_JSON < Test::Unit::TestCase end def test_parse_object_custom_class - res = parse('{}', :object_class => SubHash2) + res = parse('{}', :object_class => SubHash) assert_equal({}, res) - assert_equal(SubHash2, res.class) + assert_equal(SubHash, res.class) end - def test_generation_of_core_subclasses_with_new_to_json - obj = SubHash2.new.merge( "foo" => SubHash2.new.merge("bar" => true)) + def test_generation_of_core_subclasses + obj = SubHash.new.merge( "foo" => SubHash.new.merge("bar" => true)) obj_json = JSON(obj) obj_again = JSON(obj_json) - assert_kind_of SubHash2, obj_again - assert_kind_of SubHash2, obj_again['foo'] + assert_kind_of SubHash, obj_again + assert_kind_of SubHash, obj_again['foo'] assert obj_again['foo']['bar'] assert_equal obj, obj_again - assert_equal ["foo"], JSON(JSON(SubArray2["foo"])) - end - - def test_generation_of_core_subclasses_with_default_to_json - assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"]) - assert_equal '["foo"]', JSON(SubArray["foo"]) end def test_parser_reset -- cgit v1.2.1 From cc3dab7b8367b9328d3645e9667e5ba2c5d106f9 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Thu, 5 Aug 2010 03:45:04 +0200 Subject: Revert "use method dispatch for generation again" This reverts commit 59eab2b7ad1b44dc424fb3fffa5520947f7360f3. Conflicts: ext/json/ext/generator/generator.c --- ext/json/ext/generator/generator.c | 472 +++++++++++++++++-------------------- ext/json/ext/generator/generator.h | 14 +- lib/json/common.rb | 12 +- tests/test_json.rb | 45 +++- 4 files changed, 272 insertions(+), 271 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 7658acd..5722ed6 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -6,10 +6,9 @@ static ID i_encoding, i_encode; #endif static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, - mHash, mArray, mFixnum, mBignum, mFloat, mString, mString_Extend, + mHash, mArray, mInteger, mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE, - DO_NOT_APPEND; + eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, @@ -427,66 +426,16 @@ static FBuffer *fbuffer_dup(FBuffer *fb) */ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; + FBuffer *buffer; + VALUE Vstate, depth; + JSON_Generator_State *state; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); + rb_scan_args(argc, argv, "02", &Vstate, &depth); Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(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); - int i, j; - VALUE key, key_name, keys, value; - long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth); - FBuffer *buffer = state->buffer; - depth++; - if (max_nesting != 0 && depth > max_nesting) { - rb_raise(eNestingError, "nesting of %ld is too deep", depth); - } - fbuffer_append_char(buffer, '{'); - keys = rb_funcall(self, rb_intern("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_name = rb_funcall(key, i_to_s, 0); - Check_Type(key_name, T_STRING); - key_name = rb_funcall(key_name, i_to_json, 2, Vstate, LONG2FIX(depth)); - fbuffer_append(buffer, delim2, delim2_len); - value = rb_funcall(rb_hash_aref(self, key), i_to_json, 2, Vstate, LONG2FIX(depth)); - if (value != DO_NOT_APPEND) { - Check_Type(value, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(value)); - } - } - depth--; - 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); - } - } - } - fbuffer_append_char(buffer, '}'); - } - return cState_result(Vstate, returnResult); + Data_Get_Struct(Vstate, JSON_Generator_State, state); + buffer = cState_prepare_buffer(Vstate); + generate_json_object(buffer, Vstate, state, self, NIL_P(depth) ? 0 : FIX2INT(depth)); + return fbuffer_to_s(buffer); } /* @@ -499,74 +448,16 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) * _depth_ is used to find out nesting depth, to indent accordingly. */ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - FBuffer *buffer = state->buffer; - VALUE value; - char *array_nl = state->array_nl; - long array_nl_len = state->array_nl_len; - char *indent = state->indent; - long indent_len = state->indent_len; - long max_nesting = state->max_nesting; - char *delim = FBUFFER_PTR(state->array_delim); - long delim_len = FBUFFER_LEN(state->array_delim); - int i, j; - long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth); - depth++; - if (max_nesting != 0 && depth > max_nesting) { - rb_raise(eNestingError, "nesting of %ld is too deep", depth); - } - fbuffer_append_char(buffer, '['); - if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); - for(i = 0; i < RARRAY_LEN(self); i++) { - if (i > 0) fbuffer_append(buffer, delim, delim_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - value = rb_funcall(rb_ary_entry(self, i), i_to_json, 2, Vstate, LONG2FIX(depth)); - if (value != DO_NOT_APPEND) { - Check_Type(value, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(value)); - } - } - depth--; - if (array_nl) { - fbuffer_append(buffer, array_nl, array_nl_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - } - fbuffer_append_char(buffer, ']'); - } - return cState_result(Vstate, returnResult); -} - -/* - * call-seq: to_json(*) - * - * Returns a JSON string representation for this Integer number. - */ -static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) -{ - int returnResult; + FBuffer *buffer; VALUE Vstate, depth; + JSON_Generator_State *state; + rb_scan_args(argc, argv, "02", &Vstate, &depth); Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append_long(state->buffer, FIX2LONG(self)); - } - return cState_result(Vstate, returnResult); + Data_Get_Struct(Vstate, JSON_Generator_State, state); + buffer = cState_prepare_buffer(Vstate); + generate_json_array(buffer, Vstate, state, self, NIL_P(depth) ? 0 : FIX2INT(depth)); + return fbuffer_to_s(buffer); } /* @@ -574,20 +465,12 @@ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) * * Returns a JSON string representation for this Integer number. */ -static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) +static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - VALUE tmp; - GET_STATE(Vstate); - tmp = rb_funcall(self, i_to_s, 0); - fbuffer_append(state->buffer, RSTRING_PAIR(tmp)); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + rb_scan_args(argc, argv, "02", &state, &depth); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, self, depth); } /* @@ -597,27 +480,10 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - VALUE tmp; - GET_STATE(Vstate); - double value = RFLOAT_VALUE(self); - char allow_nan = state->allow_nan; - tmp = rb_funcall(self, i_to_s, 0); - if (!allow_nan) { - if (isinf(value)) { - rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); - } else if (isnan(value)) { - rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); - } - } - fbuffer_append(state->buffer, RSTRING_PAIR(tmp)); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + rb_scan_args(argc, argv, "02", &state, &depth); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, self, depth); } /* @@ -630,7 +496,6 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { return result; } - /* * call-seq: to_json(*) * @@ -640,16 +505,10 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { */ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - State_append_ruby_string_to_fbuffer(state, self); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + rb_scan_args(argc, argv, "02", &state, &depth); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, self, depth); } /* @@ -704,16 +563,10 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) */ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append(state->buffer, "true", 4); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + rb_scan_args(argc, argv, "02", &state, &depth); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, self, depth); } /* @@ -723,16 +576,10 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append(state->buffer, "false", 5); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + rb_scan_args(argc, argv, "02", &state, &depth); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, self, depth); } /* @@ -741,16 +588,10 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append(state->buffer, "null", 4); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + rb_scan_args(argc, argv, "02", &state, &depth); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, self, depth); } /* @@ -762,19 +603,12 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - VALUE tmp; - GET_STATE(Vstate); - tmp = rb_funcall(self, i_to_s, 0); - Check_Type(tmp, T_STRING); - State_append_ruby_string_to_fbuffer(state, tmp); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + VALUE string = rb_funcall(self, i_to_s, 0); + rb_scan_args(argc, argv, "02", &state, &depth); + Check_Type(string, T_STRING); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, string, depth); } static void State_free(JSON_Generator_State *state) @@ -787,7 +621,6 @@ static void State_free(JSON_Generator_State *state) if (state->array_delim) fbuffer_free(state->array_delim); if (state->object_delim) fbuffer_free(state->object_delim); if (state->object_delim2) fbuffer_free(state->object_delim2); - if (state->buffer) fbuffer_free(state->buffer); ruby_xfree(state); } @@ -798,22 +631,6 @@ static JSON_Generator_State *State_allocate() return state; } -static void State_append_ruby_string_to_fbuffer(JSON_Generator_State *state, VALUE string) -{ - FBuffer *buffer = state->buffer; - - fbuffer_append_char(buffer, '"'); -#ifdef HAVE_RUBY_ENCODING_H - string = rb_funcall(string, i_encode, 1, CEncoding_UTF_8); -#endif - if (state->ascii_only) { - convert_UTF8_to_JSON_ASCII(buffer, string); - } else { - convert_UTF8_to_JSON(buffer, string); - } - fbuffer_append_char(buffer, '"'); -} - static VALUE cState_s_allocate(VALUE klass) { JSON_Generator_State *state = State_allocate(); @@ -930,29 +747,153 @@ static VALUE cState_aref(VALUE self, VALUE name) } } -static VALUE cState_result(VALUE self, int returnResult) +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) { - VALUE result = DO_NOT_APPEND; - if (returnResult) { - GET_STATE(self); - result = rb_str_new(FBUFFER_PAIR(state->buffer)); - FORCE_UTF8(result); + 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); + int i, j; + VALUE key, key_to_s, keys; + depth++; + if (max_nesting != 0 && depth > max_nesting) { + fbuffer_free(buffer); + rb_raise(eNestingError, "nesting of %ld is too deep", depth); } - return result; + fbuffer_append_char(buffer, '{'); + keys = rb_funcall(obj, rb_intern("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, depth); + fbuffer_append(buffer, delim2, delim2_len); + generate_json(buffer, Vstate, state, rb_hash_aref(obj, key), depth); + } + depth--; + 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); + } + } + } + fbuffer_append_char(buffer, '}'); } -/* - * call-seq: partial_generate(obj) - * - * Generates a part of a JSON document from object +obj+ and returns the - * result. - */ -static int cState_prepare_buffer(VALUE self) +static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + char *array_nl = state->array_nl; + long array_nl_len = state->array_nl_len; + char *indent = state->indent; + long indent_len = state->indent_len; + long max_nesting = state->max_nesting; + char *delim = FBUFFER_PTR(state->array_delim); + long delim_len = FBUFFER_LEN(state->array_delim); + int i, j; + depth++; + if (max_nesting != 0 && depth > max_nesting) { + fbuffer_free(buffer); + rb_raise(eNestingError, "nesting of %ld is too deep", depth); + } + fbuffer_append_char(buffer, '['); + if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); + for(i = 0; i < RARRAY_LEN(obj); i++) { + if (i > 0) fbuffer_append(buffer, delim, delim_len); + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + generate_json(buffer, Vstate, state, rb_ary_entry(obj, i), depth); + } + depth--; + if (array_nl) { + fbuffer_append(buffer, array_nl, array_nl_len); + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + } + fbuffer_append_char(buffer, ']'); +} + +static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + VALUE tmp; + VALUE klass = CLASS_OF(obj); + if (klass == rb_cHash) { + generate_json_object(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cArray) { + generate_json_array(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cString) { + fbuffer_append_char(buffer, '"'); +#ifdef HAVE_RUBY_ENCODING_H + obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8); +#endif + if (state->ascii_only) { + convert_UTF8_to_JSON_ASCII(buffer, obj); + } else { + convert_UTF8_to_JSON(buffer, obj); + } + fbuffer_append_char(buffer, '"'); + } else if (obj == Qnil) { + fbuffer_append(buffer, "null", 4); + } else if (obj == Qfalse) { + fbuffer_append(buffer, "false", 5); + } else if (obj == Qtrue) { + fbuffer_append(buffer, "true", 4); + } else if (klass == rb_cFixnum) { + fbuffer_append_long(buffer, FIX2LONG(obj)); + } else if (klass == rb_cBignum) { + tmp = rb_funcall(obj, i_to_s, 0); + fbuffer_append(buffer, RSTRING_PAIR(tmp)); + } else if (klass == rb_cFloat) { + double value = RFLOAT_VALUE(obj); + char allow_nan = state->allow_nan; + tmp = rb_funcall(obj, i_to_s, 0); + if (!allow_nan) { + if (isinf(value)) { + fbuffer_free(buffer); + rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); + } else if (isnan(value)) { + fbuffer_free(buffer); + rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); + } + } + fbuffer_append(buffer, RSTRING_PAIR(tmp)); + } else if (rb_respond_to(obj, i_to_json)) { + tmp = rb_funcall(obj, i_to_json, 2, Vstate, INT2FIX(depth + 1)); + Check_Type(tmp, T_STRING); + fbuffer_append(buffer, RSTRING_PAIR(tmp)); + } else { + tmp = rb_funcall(obj, i_to_s, 0); + Check_Type(tmp, T_STRING); + generate_json(buffer, Vstate, state, tmp, depth + 1); + } +} + +static FBuffer *cState_prepare_buffer(VALUE self) { + FBuffer *buffer = fbuffer_alloc(); GET_STATE(self); - if (state->buffer) return 0; - state->buffer = fbuffer_alloc(); if (state->object_delim) { fbuffer_clear(state->object_delim); } else { @@ -974,7 +915,29 @@ static int cState_prepare_buffer(VALUE self) } fbuffer_append_char(state->array_delim, ','); if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len); - return 1; + return buffer; +} + +static VALUE fbuffer_to_s(FBuffer *fb) +{ + VALUE result = rb_str_new(FBUFFER_PAIR(fb)); + fbuffer_free(fb); + FORCE_UTF8(result); + return result; +} + +/* + * call-seq: partial_generate(obj) + * + * Generates a part of a JSON document from object +obj+ and returns the + * result. + */ +static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) +{ + FBuffer *buffer = cState_prepare_buffer(self); + GET_STATE(self); + generate_json(buffer, self, state, obj, NIL_P(depth) ? 0 : FIX2INT(depth)); + return fbuffer_to_s(buffer); } /* @@ -986,7 +949,7 @@ static int cState_prepare_buffer(VALUE self) */ static VALUE cState_generate(VALUE self, VALUE obj) { - VALUE result = rb_funcall(obj, i_to_json, 1, self); + VALUE result = cState_partial_generate(self, obj, Qnil); VALUE re, args[2]; args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); args[1] = CRegexp_MULTILINE; @@ -1066,7 +1029,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, rb_intern("SAFE_STATE_PROTOTYPE")); } - return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0); + return CJSON_SAFE_STATE_PROTOTYPE; } } @@ -1334,6 +1297,7 @@ void Init_generator() rb_define_method(cState, "to_h", cState_to_h, 0); rb_define_method(cState, "[]", cState_aref, 1); rb_define_method(cState, "generate", cState_generate, 1); + rb_define_method(cState, "partial_generate", cState_partial_generate, 1); mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); mObject = rb_define_module_under(mGeneratorMethods, "Object"); @@ -1342,10 +1306,8 @@ void Init_generator() rb_define_method(mHash, "to_json", mHash_to_json, -1); mArray = rb_define_module_under(mGeneratorMethods, "Array"); rb_define_method(mArray, "to_json", mArray_to_json, -1); - mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); - rb_define_method(mBignum, "to_json", mBignum_to_json, -1); - mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); - rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1); + mInteger = rb_define_module_under(mGeneratorMethods, "Integer"); + rb_define_method(mInteger, "to_json", mInteger_to_json, -1); mFloat = rb_define_module_under(mGeneratorMethods, "Float"); rb_define_method(mFloat, "to_json", mFloat_to_json, -1); mString = rb_define_module_under(mGeneratorMethods, "String"); @@ -1391,6 +1353,4 @@ void Init_generator() i_encode = rb_intern("encode"); #endif CJSON_SAFE_STATE_PROTOTYPE = Qnil; - DO_NOT_APPEND = rb_obj_alloc(rb_cObject); - rb_obj_call_init(DO_NOT_APPEND, 0, NULL); } diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index fcf68bb..ad79950 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -73,6 +73,7 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len); static void fbuffer_append_long(FBuffer *fb, long number); static void fbuffer_append_char(FBuffer *fb, char newchr); static FBuffer *fbuffer_dup(FBuffer *fb); +static VALUE fbuffer_to_s(FBuffer *fb); /* unicode defintions */ @@ -123,7 +124,6 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; - FBuffer *buffer; } JSON_Generator_State; #define GET_STATE(self) \ @@ -132,8 +132,7 @@ typedef struct JSON_Generator_StateStruct { static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self); static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self); -static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self); +static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self); static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self); static VALUE mString_included_s(VALUE self, VALUE modul); static VALUE mString_to_json(int argc, VALUE *argv, VALUE self); @@ -149,8 +148,11 @@ static JSON_Generator_State *State_allocate(); static VALUE cState_s_allocate(VALUE klass); static VALUE cState_configure(VALUE self, VALUE opts); static VALUE cState_to_h(VALUE self); -static int cState_prepare_buffer(VALUE self); -static VALUE cState_result(VALUE self, int returnResult); +static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth); +static VALUE cState_generate(VALUE self, VALUE obj); static VALUE cState_initialize(int argc, VALUE *argv, VALUE self); static VALUE cState_from_state_s(VALUE self, VALUE opts); static VALUE cState_indent(VALUE self); @@ -167,6 +169,6 @@ static VALUE cState_max_nesting(VALUE self); static VALUE cState_max_nesting_set(VALUE self, VALUE depth); static VALUE cState_allow_nan_p(VALUE self); static VALUE cState_ascii_only_p(VALUE self); -static void State_append_ruby_string_to_fbuffer(JSON_Generator_State *state, VALUE string); +static FBuffer *cState_prepare_buffer(VALUE self); #endif diff --git a/lib/json/common.rb b/lib/json/common.rb index 13c09ca..244634b 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -196,7 +196,6 @@ module JSON # amount of sanity checks, and the pretty_generate method for some # defaults for a pretty output. def generate(obj, opts = nil) - state = SAFE_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -205,7 +204,10 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end + state = SAFE_STATE_PROTOTYPE.dup state = state.configure(opts) + else + state = SAFE_STATE_PROTOTYPE end state.generate(obj) end @@ -223,7 +225,6 @@ module JSON # *WARNING*: Be careful not to pass any Ruby data structures with circles as # _obj_ argument, because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) - state = FAST_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -232,7 +233,10 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end + state = FAST_STATE_PROTOTYPE.dup state.configure(opts) + else + state = FAST_STATE_PROTOTYPE end state.generate(obj) end @@ -250,7 +254,6 @@ module JSON # The _opts_ argument can be used to configure the generator, see the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) - state = PRETTY_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -259,7 +262,10 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end + state = PRETTY_STATE_PROTOTYPE.dup state.configure(opts) + else + state = PRETTY_STATE_PROTOTYPE end state.generate(obj) end diff --git a/tests/test_json.rb b/tests/test_json.rb index 19b742f..00e52f5 100755 --- a/tests/test_json.rb +++ b/tests/test_json.rb @@ -160,6 +160,20 @@ class TC_JSON < Test::Unit::TestCase class SubArray < Array; end + class SubArray2 < Array + def to_json(*a) + { + JSON.create_id => self.class.name, + 'ary' => to_a, + }.to_json(*a) + end + + def self.json_create(o) + o.delete JSON.create_id + o['ary'] + end + end + def test_parse_array_custom_class res = parse('[]', :array_class => SubArray) assert_equal([], res) @@ -174,6 +188,9 @@ class TC_JSON < Test::Unit::TestCase end class SubHash < Hash + end + + class SubHash2 < Hash def to_json(*a) { JSON.create_id => self.class.name, @@ -182,22 +199,38 @@ class TC_JSON < Test::Unit::TestCase def self.json_create(o) o.delete JSON.create_id - new.merge(o) + self[o] end end def test_parse_object_custom_class - res = parse('{}', :object_class => SubHash) + res = parse('{}', :object_class => SubHash2) assert_equal({}, res) - assert_equal(SubHash, res.class) + assert_equal(SubHash2, res.class) + end + + def test_generation_of_core_subclasses_with_new_to_json + obj = SubHash2["foo" => SubHash2["bar" => true]] + obj_json = JSON(obj) + obj_again = JSON(obj_json) + assert_kind_of SubHash2, obj_again + assert_kind_of SubHash2, obj_again['foo'] + assert obj_again['foo']['bar'] + assert_equal obj, obj_again + assert_equal ["foo"], JSON(JSON(SubArray2["foo"])) + end + + def test_generation_of_core_subclasses_with_default_to_json + assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"]) + assert_equal '["foo"]', JSON(SubArray["foo"]) end def test_generation_of_core_subclasses - obj = SubHash.new.merge( "foo" => SubHash.new.merge("bar" => true)) + obj = SubHash["foo" => SubHash["bar" => true]] obj_json = JSON(obj) obj_again = JSON(obj_json) - assert_kind_of SubHash, obj_again - assert_kind_of SubHash, obj_again['foo'] + assert_kind_of Hash, obj_again + assert_kind_of Hash, obj_again['foo'] assert obj_again['foo']['bar'] assert_equal obj, obj_again end -- cgit v1.2.1 From 6c5ec83475acf01df831858b20c9ccb512d5a8a5 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Thu, 5 Aug 2010 03:45:04 +0200 Subject: Revert "use method dispatch for generation again" This reverts commit 59eab2b7ad1b44dc424fb3fffa5520947f7360f3. Conflicts: ext/json/ext/generator/generator.c --- ext/json/ext/generator/generator.c | 467 +++++++++++++++++-------------------- ext/json/ext/generator/generator.h | 30 ++- lib/json/common.rb | 12 +- tests/test_json.rb | 45 +++- 4 files changed, 293 insertions(+), 261 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 7658acd..3565186 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -8,8 +8,7 @@ static ID i_encoding, i_encode; static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, mHash, mArray, mFixnum, mBignum, mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE, - DO_NOT_APPEND; + eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, @@ -427,66 +426,7 @@ static FBuffer *fbuffer_dup(FBuffer *fb) */ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(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); - int i, j; - VALUE key, key_name, keys, value; - long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth); - FBuffer *buffer = state->buffer; - depth++; - if (max_nesting != 0 && depth > max_nesting) { - rb_raise(eNestingError, "nesting of %ld is too deep", depth); - } - fbuffer_append_char(buffer, '{'); - keys = rb_funcall(self, rb_intern("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_name = rb_funcall(key, i_to_s, 0); - Check_Type(key_name, T_STRING); - key_name = rb_funcall(key_name, i_to_json, 2, Vstate, LONG2FIX(depth)); - fbuffer_append(buffer, delim2, delim2_len); - value = rb_funcall(rb_hash_aref(self, key), i_to_json, 2, Vstate, LONG2FIX(depth)); - if (value != DO_NOT_APPEND) { - Check_Type(value, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(value)); - } - } - depth--; - 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); - } - } - } - fbuffer_append_char(buffer, '}'); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(object); } /* @@ -499,55 +439,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) * _depth_ is used to find out nesting depth, to indent accordingly. */ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - FBuffer *buffer = state->buffer; - VALUE value; - char *array_nl = state->array_nl; - long array_nl_len = state->array_nl_len; - char *indent = state->indent; - long indent_len = state->indent_len; - long max_nesting = state->max_nesting; - char *delim = FBUFFER_PTR(state->array_delim); - long delim_len = FBUFFER_LEN(state->array_delim); - int i, j; - long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth); - depth++; - if (max_nesting != 0 && depth > max_nesting) { - rb_raise(eNestingError, "nesting of %ld is too deep", depth); - } - fbuffer_append_char(buffer, '['); - if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); - for(i = 0; i < RARRAY_LEN(self); i++) { - if (i > 0) fbuffer_append(buffer, delim, delim_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - value = rb_funcall(rb_ary_entry(self, i), i_to_json, 2, Vstate, LONG2FIX(depth)); - if (value != DO_NOT_APPEND) { - Check_Type(value, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(value)); - } - } - depth--; - if (array_nl) { - fbuffer_append(buffer, array_nl, array_nl_len); - if (indent) { - for (j = 0; j < depth; j++) { - fbuffer_append(buffer, indent, indent_len); - } - } - } - fbuffer_append_char(buffer, ']'); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(array); } /* @@ -557,16 +449,7 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { */ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append_long(state->buffer, FIX2LONG(self)); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(fixnum); } /* @@ -576,18 +459,7 @@ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - VALUE tmp; - GET_STATE(Vstate); - tmp = rb_funcall(self, i_to_s, 0); - fbuffer_append(state->buffer, RSTRING_PAIR(tmp)); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(bignum); } /* @@ -597,27 +469,7 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - VALUE tmp; - GET_STATE(Vstate); - double value = RFLOAT_VALUE(self); - char allow_nan = state->allow_nan; - tmp = rb_funcall(self, i_to_s, 0); - if (!allow_nan) { - if (isinf(value)) { - rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); - } else if (isnan(value)) { - rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); - } - } - fbuffer_append(state->buffer, RSTRING_PAIR(tmp)); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(float); } /* @@ -630,7 +482,6 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { return result; } - /* * call-seq: to_json(*) * @@ -640,16 +491,7 @@ static VALUE mString_included_s(VALUE self, VALUE modul) { */ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, depth; - rb_scan_args(argc, argv, "02", &Vstate, &depth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - State_append_ruby_string_to_fbuffer(state, self); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(string); } /* @@ -704,16 +546,7 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) */ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append(state->buffer, "true", 4); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(true); } /* @@ -723,16 +556,7 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append(state->buffer, "false", 5); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(false); } /* @@ -741,16 +565,7 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - GET_STATE(Vstate); - fbuffer_append(state->buffer, "null", 4); - } - return cState_result(Vstate, returnResult); + GENERATE_JSON(null); } /* @@ -762,19 +577,12 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) { - int returnResult; - VALUE Vstate, Vdepth; - rb_scan_args(argc, argv, "02", &Vstate, &Vdepth); - Vstate = cState_from_state_s(cState, Vstate); - returnResult = cState_prepare_buffer(Vstate); - { - VALUE tmp; - GET_STATE(Vstate); - tmp = rb_funcall(self, i_to_s, 0); - Check_Type(tmp, T_STRING); - State_append_ruby_string_to_fbuffer(state, tmp); - } - return cState_result(Vstate, returnResult); + VALUE state, depth; + VALUE string = rb_funcall(self, i_to_s, 0); + rb_scan_args(argc, argv, "02", &state, &depth); + Check_Type(string, T_STRING); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, string, depth); } static void State_free(JSON_Generator_State *state) @@ -787,7 +595,6 @@ static void State_free(JSON_Generator_State *state) if (state->array_delim) fbuffer_free(state->array_delim); if (state->object_delim) fbuffer_free(state->object_delim); if (state->object_delim2) fbuffer_free(state->object_delim2); - if (state->buffer) fbuffer_free(state->buffer); ruby_xfree(state); } @@ -798,22 +605,6 @@ static JSON_Generator_State *State_allocate() return state; } -static void State_append_ruby_string_to_fbuffer(JSON_Generator_State *state, VALUE string) -{ - FBuffer *buffer = state->buffer; - - fbuffer_append_char(buffer, '"'); -#ifdef HAVE_RUBY_ENCODING_H - string = rb_funcall(string, i_encode, 1, CEncoding_UTF_8); -#endif - if (state->ascii_only) { - convert_UTF8_to_JSON_ASCII(buffer, string); - } else { - convert_UTF8_to_JSON(buffer, string); - } - fbuffer_append_char(buffer, '"'); -} - static VALUE cState_s_allocate(VALUE klass) { JSON_Generator_State *state = State_allocate(); @@ -930,29 +721,188 @@ static VALUE cState_aref(VALUE self, VALUE name) } } -static VALUE cState_result(VALUE self, int returnResult) +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) { - VALUE result = DO_NOT_APPEND; - if (returnResult) { - GET_STATE(self); - result = rb_str_new(FBUFFER_PAIR(state->buffer)); - FORCE_UTF8(result); + 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); + int i, j; + VALUE key, key_to_s, keys; + depth++; + if (max_nesting != 0 && depth > max_nesting) { + fbuffer_free(buffer); + rb_raise(eNestingError, "nesting of %ld is too deep", depth); } - return result; + fbuffer_append_char(buffer, '{'); + keys = rb_funcall(obj, rb_intern("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, depth); + fbuffer_append(buffer, delim2, delim2_len); + generate_json(buffer, Vstate, state, rb_hash_aref(obj, key), depth); + } + depth--; + 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); + } + } + } + fbuffer_append_char(buffer, '}'); } -/* - * call-seq: partial_generate(obj) - * - * Generates a part of a JSON document from object +obj+ and returns the - * result. - */ -static int cState_prepare_buffer(VALUE self) +static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) { + char *array_nl = state->array_nl; + long array_nl_len = state->array_nl_len; + char *indent = state->indent; + long indent_len = state->indent_len; + long max_nesting = state->max_nesting; + char *delim = FBUFFER_PTR(state->array_delim); + long delim_len = FBUFFER_LEN(state->array_delim); + int i, j; + depth++; + if (max_nesting != 0 && depth > max_nesting) { + fbuffer_free(buffer); + rb_raise(eNestingError, "nesting of %ld is too deep", depth); + } + fbuffer_append_char(buffer, '['); + if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len); + for(i = 0; i < RARRAY_LEN(obj); i++) { + if (i > 0) fbuffer_append(buffer, delim, delim_len); + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + generate_json(buffer, Vstate, state, rb_ary_entry(obj, i), depth); + } + depth--; + if (array_nl) { + fbuffer_append(buffer, array_nl, array_nl_len); + if (indent) { + for (j = 0; j < depth; j++) { + fbuffer_append(buffer, indent, indent_len); + } + } + } + fbuffer_append_char(buffer, ']'); +} + +static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + fbuffer_append_char(buffer, '"'); +#ifdef HAVE_RUBY_ENCODING_H + obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8); +#endif + if (state->ascii_only) { + convert_UTF8_to_JSON_ASCII(buffer, obj); + } else { + convert_UTF8_to_JSON(buffer, obj); + } + fbuffer_append_char(buffer, '"'); +} + +static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + fbuffer_append(buffer, "null", 4); +} + +static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + fbuffer_append(buffer, "false", 5); +} + +static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + fbuffer_append(buffer, "true", 4); +} + +static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + fbuffer_append_long(buffer, FIX2LONG(obj)); +} + +static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + VALUE tmp = rb_funcall(obj, i_to_s, 0); + fbuffer_append(buffer, RSTRING_PAIR(tmp)); +} + +static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + double value = RFLOAT_VALUE(obj); + char allow_nan = state->allow_nan; + VALUE tmp = rb_funcall(obj, i_to_s, 0); + if (!allow_nan) { + if (isinf(value)) { + fbuffer_free(buffer); + rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); + } else if (isnan(value)) { + fbuffer_free(buffer); + rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); + } + } + fbuffer_append(buffer, RSTRING_PAIR(tmp)); +} + +static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +{ + VALUE tmp; + VALUE klass = CLASS_OF(obj); + if (klass == rb_cHash) { + generate_json_object(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cArray) { + generate_json_array(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cString) { + generate_json_string(buffer, Vstate, state, obj, depth); + } else if (obj == Qnil) { + generate_json_null(buffer, Vstate, state, obj, depth); + } else if (obj == Qfalse) { + generate_json_false(buffer, Vstate, state, obj, depth); + } else if (obj == Qtrue) { + generate_json_true(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cFixnum) { + generate_json_fixnum(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cBignum) { + generate_json_bignum(buffer, Vstate, state, obj, depth); + } else if (klass == rb_cFloat) { + generate_json_float(buffer, Vstate, state, obj, depth); + } else if (rb_respond_to(obj, i_to_json)) { + tmp = rb_funcall(obj, i_to_json, 2, Vstate, INT2FIX(depth + 1)); + Check_Type(tmp, T_STRING); + fbuffer_append(buffer, RSTRING_PAIR(tmp)); + } else { + tmp = rb_funcall(obj, i_to_s, 0); + Check_Type(tmp, T_STRING); + generate_json(buffer, Vstate, state, tmp, depth + 1); + } +} + +static FBuffer *cState_prepare_buffer(VALUE self) +{ + FBuffer *buffer = fbuffer_alloc(); GET_STATE(self); - if (state->buffer) return 0; - state->buffer = fbuffer_alloc(); if (state->object_delim) { fbuffer_clear(state->object_delim); } else { @@ -974,7 +924,29 @@ static int cState_prepare_buffer(VALUE self) } fbuffer_append_char(state->array_delim, ','); if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len); - return 1; + return buffer; +} + +static VALUE fbuffer_to_s(FBuffer *fb) +{ + VALUE result = rb_str_new(FBUFFER_PAIR(fb)); + fbuffer_free(fb); + FORCE_UTF8(result); + return result; +} + +/* + * call-seq: partial_generate(obj) + * + * Generates a part of a JSON document from object +obj+ and returns the + * result. + */ +static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) +{ + FBuffer *buffer = cState_prepare_buffer(self); + GET_STATE(self); + generate_json(buffer, self, state, obj, NIL_P(depth) ? 0 : FIX2INT(depth)); + return fbuffer_to_s(buffer); } /* @@ -986,7 +958,7 @@ static int cState_prepare_buffer(VALUE self) */ static VALUE cState_generate(VALUE self, VALUE obj) { - VALUE result = rb_funcall(obj, i_to_json, 1, self); + VALUE result = cState_partial_generate(self, obj, Qnil); VALUE re, args[2]; args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); args[1] = CRegexp_MULTILINE; @@ -1066,7 +1038,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, rb_intern("SAFE_STATE_PROTOTYPE")); } - return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0); + return CJSON_SAFE_STATE_PROTOTYPE; } } @@ -1334,6 +1306,7 @@ void Init_generator() rb_define_method(cState, "to_h", cState_to_h, 0); rb_define_method(cState, "[]", cState_aref, 1); rb_define_method(cState, "generate", cState_generate, 1); + rb_define_method(cState, "partial_generate", cState_partial_generate, 1); mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); mObject = rb_define_module_under(mGeneratorMethods, "Object"); @@ -1342,10 +1315,10 @@ void Init_generator() rb_define_method(mHash, "to_json", mHash_to_json, -1); mArray = rb_define_module_under(mGeneratorMethods, "Array"); rb_define_method(mArray, "to_json", mArray_to_json, -1); - mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); - rb_define_method(mBignum, "to_json", mBignum_to_json, -1); mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1); + mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); + rb_define_method(mBignum, "to_json", mBignum_to_json, -1); mFloat = rb_define_module_under(mGeneratorMethods, "Float"); rb_define_method(mFloat, "to_json", mFloat_to_json, -1); mString = rb_define_module_under(mGeneratorMethods, "String"); @@ -1391,6 +1364,4 @@ void Init_generator() i_encode = rb_intern("encode"); #endif CJSON_SAFE_STATE_PROTOTYPE = Qnil; - DO_NOT_APPEND = rb_obj_alloc(rb_cObject); - rb_obj_call_init(DO_NOT_APPEND, 0, NULL); } diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index fcf68bb..dbfc4d0 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -73,6 +73,7 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len); static void fbuffer_append_long(FBuffer *fb, long number); static void fbuffer_append_char(FBuffer *fb, char newchr); static FBuffer *fbuffer_dup(FBuffer *fb); +static VALUE fbuffer_to_s(FBuffer *fb); /* unicode defintions */ @@ -123,13 +124,24 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; - FBuffer *buffer; } JSON_Generator_State; #define GET_STATE(self) \ JSON_Generator_State *state; \ Data_Get_Struct(self, JSON_Generator_State, state) +#define GENERATE_JSON(type) \ + FBuffer *buffer; \ + VALUE Vstate, depth; \ + JSON_Generator_State *state; \ + \ + rb_scan_args(argc, argv, "02", &Vstate, &depth); \ + Vstate = cState_from_state_s(cState, Vstate); \ + Data_Get_Struct(Vstate, JSON_Generator_State, state); \ + buffer = cState_prepare_buffer(Vstate); \ + generate_json_##type(buffer, Vstate, state, self, NIL_P(depth) ? 0 : FIX2INT(depth)); \ + return fbuffer_to_s(buffer) + static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self); static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self); static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self); @@ -149,8 +161,18 @@ static JSON_Generator_State *State_allocate(); static VALUE cState_s_allocate(VALUE klass); static VALUE cState_configure(VALUE self, VALUE opts); static VALUE cState_to_h(VALUE self); -static int cState_prepare_buffer(VALUE self); -static VALUE cState_result(VALUE self, int returnResult); +static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); +static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth); +static VALUE cState_generate(VALUE self, VALUE obj); static VALUE cState_initialize(int argc, VALUE *argv, VALUE self); static VALUE cState_from_state_s(VALUE self, VALUE opts); static VALUE cState_indent(VALUE self); @@ -167,6 +189,6 @@ static VALUE cState_max_nesting(VALUE self); static VALUE cState_max_nesting_set(VALUE self, VALUE depth); static VALUE cState_allow_nan_p(VALUE self); static VALUE cState_ascii_only_p(VALUE self); -static void State_append_ruby_string_to_fbuffer(JSON_Generator_State *state, VALUE string); +static FBuffer *cState_prepare_buffer(VALUE self); #endif diff --git a/lib/json/common.rb b/lib/json/common.rb index 13c09ca..244634b 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -196,7 +196,6 @@ module JSON # amount of sanity checks, and the pretty_generate method for some # defaults for a pretty output. def generate(obj, opts = nil) - state = SAFE_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -205,7 +204,10 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end + state = SAFE_STATE_PROTOTYPE.dup state = state.configure(opts) + else + state = SAFE_STATE_PROTOTYPE end state.generate(obj) end @@ -223,7 +225,6 @@ module JSON # *WARNING*: Be careful not to pass any Ruby data structures with circles as # _obj_ argument, because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) - state = FAST_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -232,7 +233,10 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end + state = FAST_STATE_PROTOTYPE.dup state.configure(opts) + else + state = FAST_STATE_PROTOTYPE end state.generate(obj) end @@ -250,7 +254,6 @@ module JSON # The _opts_ argument can be used to configure the generator, see the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) - state = PRETTY_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -259,7 +262,10 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end + state = PRETTY_STATE_PROTOTYPE.dup state.configure(opts) + else + state = PRETTY_STATE_PROTOTYPE end state.generate(obj) end diff --git a/tests/test_json.rb b/tests/test_json.rb index 19b742f..00e52f5 100755 --- a/tests/test_json.rb +++ b/tests/test_json.rb @@ -160,6 +160,20 @@ class TC_JSON < Test::Unit::TestCase class SubArray < Array; end + class SubArray2 < Array + def to_json(*a) + { + JSON.create_id => self.class.name, + 'ary' => to_a, + }.to_json(*a) + end + + def self.json_create(o) + o.delete JSON.create_id + o['ary'] + end + end + def test_parse_array_custom_class res = parse('[]', :array_class => SubArray) assert_equal([], res) @@ -174,6 +188,9 @@ class TC_JSON < Test::Unit::TestCase end class SubHash < Hash + end + + class SubHash2 < Hash def to_json(*a) { JSON.create_id => self.class.name, @@ -182,22 +199,38 @@ class TC_JSON < Test::Unit::TestCase def self.json_create(o) o.delete JSON.create_id - new.merge(o) + self[o] end end def test_parse_object_custom_class - res = parse('{}', :object_class => SubHash) + res = parse('{}', :object_class => SubHash2) assert_equal({}, res) - assert_equal(SubHash, res.class) + assert_equal(SubHash2, res.class) + end + + def test_generation_of_core_subclasses_with_new_to_json + obj = SubHash2["foo" => SubHash2["bar" => true]] + obj_json = JSON(obj) + obj_again = JSON(obj_json) + assert_kind_of SubHash2, obj_again + assert_kind_of SubHash2, obj_again['foo'] + assert obj_again['foo']['bar'] + assert_equal obj, obj_again + assert_equal ["foo"], JSON(JSON(SubArray2["foo"])) + end + + def test_generation_of_core_subclasses_with_default_to_json + assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"]) + assert_equal '["foo"]', JSON(SubArray["foo"]) end def test_generation_of_core_subclasses - obj = SubHash.new.merge( "foo" => SubHash.new.merge("bar" => true)) + obj = SubHash["foo" => SubHash["bar" => true]] obj_json = JSON(obj) obj_again = JSON(obj_json) - assert_kind_of SubHash, obj_again - assert_kind_of SubHash, obj_again['foo'] + assert_kind_of Hash, obj_again + assert_kind_of Hash, obj_again['foo'] assert obj_again['foo']['bar'] assert_equal obj, obj_again end -- cgit v1.2.1 From 560599a3c85dde8735c7db910152cd0017338069 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Fri, 6 Aug 2010 01:08:40 +0200 Subject: use sdoc --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 3922c26..dfcf3cd 100644 --- a/Rakefile +++ b/Rakefile @@ -174,7 +174,7 @@ task :benchmark => [ :benchmark_parser, :benchmark_generator ] desc "Create RDOC documentation" task :doc => [ :version, EXT_PARSER_SRC ] do - sh "rdoc -o doc -t '#{PKG_TITLE}' -m README README lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}" + sh "sdoc -o doc -t '#{PKG_TITLE}' -m README README lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}" end if defined?(Gem) and defined?(Rake::GemPackageTask) -- cgit v1.2.1 From 37d70383addefb18944740c7c6b88bb210d54485 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Fri, 6 Aug 2010 02:17:24 +0200 Subject: cleaned up code a bit --- ext/json/ext/generator/generator.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 3565186..5414cd8 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -8,12 +8,13 @@ static ID i_encoding, i_encode; static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, mHash, mArray, mFixnum, mBignum, mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE; + eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE, + i_SAFE_STATE_PROTOTYPE; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, - i_respond_to_p, i_match, i_keys, i_dup; + i_respond_to_p, i_match, i_keys; /* * Copyright 2001-2004 Unicode, Inc. @@ -740,7 +741,7 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S rb_raise(eNestingError, "nesting of %ld is too deep", depth); } fbuffer_append_char(buffer, '{'); - keys = rb_funcall(obj, rb_intern("keys"), 0); + 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) { @@ -935,12 +936,6 @@ static VALUE fbuffer_to_s(FBuffer *fb) return result; } -/* - * call-seq: partial_generate(obj) - * - * Generates a part of a JSON document from object +obj+ and returns the - * result. - */ static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) { FBuffer *buffer = cState_prepare_buffer(self); @@ -1036,7 +1031,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) return rb_funcall(self, i_new, 1, opts); } else { if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { - CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, rb_intern("SAFE_STATE_PROTOTYPE")); + CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE); } return CJSON_SAFE_STATE_PROTOTYPE; } @@ -1306,7 +1301,6 @@ void Init_generator() rb_define_method(cState, "to_h", cState_to_h, 0); rb_define_method(cState, "[]", cState_aref, 1); rb_define_method(cState, "generate", cState_generate, 1); - rb_define_method(cState, "partial_generate", cState_partial_generate, 1); mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); mObject = rb_define_module_under(mGeneratorMethods, "Object"); @@ -1357,11 +1351,11 @@ void Init_generator() i_respond_to_p = rb_intern("respond_to?"); i_match = rb_intern("match"); i_keys = rb_intern("keys"); - i_dup = rb_intern("dup"); #ifdef HAVE_RUBY_ENCODING_H CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); i_encoding = rb_intern("encoding"); i_encode = rb_intern("encode"); #endif + i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE"); CJSON_SAFE_STATE_PROTOTYPE = Qnil; } -- cgit v1.2.1 From 7bcff4cc5de6b484393de8adb4f4d4488df669e6 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Fri, 6 Aug 2010 02:53:05 +0200 Subject: changed documentation --- README | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README b/README index f679136..22249ba 100644 --- a/README +++ b/README @@ -11,13 +11,11 @@ will be two variants available: generated by the ragel state machine compiler http://www.cs.queensu.ca/~thurston/ragel . -Both variants of the JSON generator escape all non-ASCII and control characters -with \uXXXX escape sequences, and support UTF-16 surrogate pairs in order to be -able to generate the whole range of unicode code points. This means that -generated JSON document is encoded as UTF-8 (because ASCII is a subset of -UTF-8) and at the same time avoids decoding problems for receiving endpoints, -that don't expect UTF-8 encoded texts. On the negative side this may lead to a -bit longer strings than necessarry. +Both variants of the JSON generator generate UTF-8 character sequences by +default. If an :ascii_only option with a true value is given, they escape all +non-ASCII and control characters with \uXXXX escape sequences, and support +UTF-16 surrogate pairs in order to be able to generate the whole range of +unicode code points. All strings, that are to be encoded as JSON strings, should be UTF-8 byte sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8 -- cgit v1.2.1 From 8095392057046800e07e445ca121c0aa3bc51c39 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Fri, 6 Aug 2010 04:02:47 +0200 Subject: make sure to use toplevel ::Encoding --- lib/json/pure/parser.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index f6de8d1..41b8ac7 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -70,24 +70,25 @@ module JSON # * *array_class*: Defaults to Array def initialize(source, opts = {}) if defined?(::Encoding) - if source.encoding == Encoding::ASCII_8BIT + if source.encoding == ::Encoding::ASCII_8BIT b = source[0, 4].bytes.to_a source = case when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - source.dup.force_encoding(Encoding::UTF_32BE).encode!(Encoding::UTF_8) + source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) when b.size >= 4 && b[0] == 0 && b[2] == 0 - source.dup.force_encoding(Encoding::UTF_16BE).encode!(Encoding::UTF_8) + source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - source.dup.force_encoding(Encoding::UTF_32LE).encode!(Encoding::UTF_8) + source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[1] == 0 && b[3] == 0 - source.dup.force_encoding(Encoding::UTF_16LE).encode!(Encoding::UTF_8) + source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) else source.dup end else - source = source.encode(Encoding::UTF_8) + source = source.encode(::Encoding::UTF_8) end - source.force_encoding(Encoding::ASCII_8BIT) + source.force_encoding(::Encoding::ASCII_8BIT) else b = source source = case @@ -180,7 +181,7 @@ module JSON end end if string.respond_to?(:force_encoding) - string.force_encoding(Encoding::UTF_8) + string.force_encoding(::Encoding::UTF_8) end string else -- cgit v1.2.1 From 9c19d632fa7071a93eead3aa544dde3e791ad9c4 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Fri, 6 Aug 2010 09:23:18 +0200 Subject: Prepare new version --- CHANGES | 4 ++++ VERSION | 2 +- lib/json/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index bba29be..41944e6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +2010-08-06 (1.4.4) + * Fixes build problem for rubinius under OS X, http://github.com/flori/json/issues/closed#issue/25 + * Fixes crashes described in http://github.com/flori/json/issues/closed#issue/21 and + http://github.com/flori/json/issues/closed#issue/23 2010-05-05 (1.4.3) * Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu. * Fixed issue http://github.com/flori/json/issues/#issue/20 reported by diff --git a/VERSION b/VERSION index 428b770..1c99cf0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.3 +1.4.4 diff --git a/lib/json/version.rb b/lib/json/version.rb index a06d157..ab4972c 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,6 +1,6 @@ module JSON # JSON version - VERSION = '1.4.3' + VERSION = '1.4.4' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: -- cgit v1.2.1