summaryrefslogtreecommitdiff
path: root/ext/json
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json')
-rw-r--r--ext/json/ext/generator/extconf.rb4
-rw-r--r--ext/json/ext/generator/generator.c235
-rw-r--r--ext/json/ext/parser/extconf.rb4
-rw-r--r--ext/json/ext/parser/parser.c363
-rw-r--r--ext/json/ext/parser/parser.rl60
5 files changed, 489 insertions, 177 deletions
diff --git a/ext/json/ext/generator/extconf.rb b/ext/json/ext/generator/extconf.rb
index d1d0401721..86ed7e6367 100644
--- a/ext/json/ext/generator/extconf.rb
+++ b/ext/json/ext/generator/extconf.rb
@@ -2,8 +2,8 @@ require 'mkmf'
require 'rbconfig'
if CONFIG['CC'] =~ /gcc/
- CONFIG['CC'] += ' -Wall -ggdb'
- #CONFIG['CC'] += ' -Wall'
+ #$CFLAGS += ' -Wall -ggdb'
+ $CFLAGS += ' -Wall'
end
have_header 'st.h'
diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c
index 07e348aa12..446235c695 100644
--- a/ext/json/ext/generator/generator.c
+++ b/ext/json/ext/generator/generator.c
@@ -4,15 +4,22 @@
#include "ruby/ruby.h"
#include "ruby/st.h"
#include "unicode.h"
+#include <math.h>
+
+#define check_max_nesting(state, depth) do { \
+ long current_nesting = 1 + depth; \
+ if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
+ rb_raise(eNestingError, "nesting of %u is too deep", current_nesting); \
+} while (0);
static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
mHash, mArray, mInteger, mFloat, mString, mString_Extend,
mTrueClass, mFalseClass, mNilClass, eGeneratorError,
- eCircularDatastructure;
+ eCircularDatastructure, eNestingError;
static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
- i_object_nl, i_array_nl, i_check_circular, i_pack, i_unpack,
- i_create_id, i_extend;
+ i_object_nl, i_array_nl, i_check_circular, i_max_nesting,
+ i_allow_nan, i_pack, i_unpack, i_create_id, i_extend;
typedef struct JSON_Generator_StateStruct {
VALUE indent;
@@ -24,7 +31,9 @@ typedef struct JSON_Generator_StateStruct {
VALUE seen;
VALUE memo;
VALUE depth;
+ long max_nesting;
int flag;
+ int allow_nan;
} JSON_Generator_State;
#define GET_STATE(self) \
@@ -138,6 +147,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
rb_str_buf_cat2(result, "}");
} else {
GET_STATE(Vstate);
+ check_max_nesting(state, depth);
if (state->check_circular) {
VALUE self_id = rb_obj_id(self);
if (RTEST(rb_hash_aref(state->seen, self_id))) {
@@ -162,6 +172,7 @@ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth
VALUE delim = rb_str_new2(",");
GET_STATE(Vstate);
+ check_max_nesting(state, depth);
if (state->check_circular) {
VALUE self_id = rb_obj_id(self);
rb_hash_aset(state->seen, self_id, Qtrue);
@@ -170,6 +181,7 @@ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth
shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
rb_str_buf_cat2(result, "[");
+ OBJ_INFECT(result, self);
rb_str_buf_append(result, state->array_nl);
for (i = 0; i < len; i++) {
VALUE element = RARRAY_PTR(self)[i];
@@ -190,6 +202,7 @@ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth
rb_hash_delete(state->seen, self_id);
} else {
result = rb_str_buf_new(len);
+ OBJ_INFECT(result, self);
if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
@@ -228,6 +241,7 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
long i, len = RARRAY_LEN(self);
result = rb_str_buf_new(2 + 2 * len);
rb_str_buf_cat2(result, "[");
+ OBJ_INFECT(result, self);
for (i = 0; i < len; i++) {
VALUE element = RARRAY_PTR(self)[i];
OBJ_INFECT(result, element);
@@ -259,7 +273,28 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
*/
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
{
- return rb_funcall(self, i_to_s, 0);
+ JSON_Generator_State *state = NULL;
+ VALUE Vstate, rest, tmp;
+ double value = RFLOAT(self)->value;
+ rb_scan_args(argc, argv, "01*", &Vstate, &rest);
+ if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
+ if (isinf(value)) {
+ if (!state || state->allow_nan) {
+ return rb_funcall(self, i_to_s, 0);
+ } else {
+ tmp = rb_funcall(self, i_to_s, 0);
+ rb_raise(eGeneratorError, "%s not allowed in JSON", StringValueCStr(tmp));
+ }
+ } else if (isnan(value)) {
+ if (!state || state->allow_nan) {
+ return rb_funcall(self, i_to_s, 0);
+ } else {
+ tmp = rb_funcall(self, i_to_s, 0);
+ rb_raise(eGeneratorError, "%s not allowed in JSON", StringValueCStr(tmp));
+ }
+ } else {
+ return rb_funcall(self, i_to_s, 0);
+ }
}
/*
@@ -404,6 +439,92 @@ static VALUE cState_s_allocate(VALUE klass)
}
/*
+ * call-seq: configure(opts)
+ *
+ * Configure this State instance with the Hash _opts_, and return
+ * itself.
+ */
+static inline VALUE cState_configure(VALUE self, VALUE opts)
+{
+ VALUE tmp;
+ GET_STATE(self);
+ tmp = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
+ if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
+ if (NIL_P(tmp)) {
+ rb_raise(rb_eArgError, "opts has to be hash like or convertable into a hash");
+ }
+ opts = tmp;
+ tmp = rb_hash_aref(opts, ID2SYM(i_indent));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->indent = tmp;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_space));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->space = tmp;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->space_before = tmp;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->array_nl = tmp;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->object_nl = tmp;
+ }
+ tmp = ID2SYM(i_check_circular);
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
+ tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
+ state->check_circular = RTEST(tmp);
+ } else {
+ state->check_circular = 1;
+ }
+ tmp = ID2SYM(i_max_nesting);
+ state->max_nesting = 19;
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ state->max_nesting = FIX2LONG(max_nesting);
+ } else {
+ state->max_nesting = 0;
+ }
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
+ state->allow_nan = RTEST(tmp);
+ return self;
+}
+
+/*
+ * call-seq: to_h
+ *
+ * Returns the configuration instance variables as a hash, that can be
+ * passed to the configure method.
+ */
+static VALUE cState_to_h(VALUE self)
+{
+ VALUE result = rb_hash_new();
+ GET_STATE(self);
+ rb_hash_aset(result, ID2SYM(i_indent), state->indent);
+ rb_hash_aset(result, ID2SYM(i_space), state->space);
+ rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
+ rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
+ rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
+ rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
+ return result;
+}
+
+
+/*
* call-seq: new(opts = {})
*
* Instantiates a new State object, configured by _opts_.
@@ -417,6 +538,9 @@ static VALUE cState_s_allocate(VALUE klass)
* * *array_nl*: a string that is put at the end of a JSON array (default: ''),
* * *check_circular*: true if checking for circular data structures
* should be done, false (the default) otherwise.
+ * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
+ * generated, otherwise an exception is thrown, if these values are
+ * encountered. This options defaults to false.
*/
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
{
@@ -424,53 +548,17 @@ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
GET_STATE(self);
rb_scan_args(argc, argv, "01", &opts);
+ state->indent = rb_str_new2("");
+ state->space = rb_str_new2("");
+ state->space_before = rb_str_new2("");
+ state->array_nl = rb_str_new2("");
+ state->object_nl = rb_str_new2("");
if (NIL_P(opts)) {
- state->indent = rb_str_new2("");
- state->space = rb_str_new2("");
- state->space_before = rb_str_new2("");
- state->array_nl = rb_str_new2("");
- state->object_nl = rb_str_new2("");
- state->check_circular = 0;
+ state->check_circular = 1;
+ state->allow_nan = 0;
+ state->max_nesting = 19;
} else {
- VALUE tmp;
- opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
- tmp = rb_hash_aref(opts, ID2SYM(i_indent));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->indent = tmp;
- } else {
- state->indent = rb_str_new2("");
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_space));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->space = tmp;
- } else {
- state->space = rb_str_new2("");
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->space_before = tmp;
- } else {
- state->space_before = rb_str_new2("");
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->array_nl = tmp;
- } else {
- state->array_nl = rb_str_new2("");
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->object_nl = tmp;
- } else {
- state->object_nl = rb_str_new2("");
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
- state->check_circular = RTEST(tmp);
+ cState_configure(self, opts);
}
state->seen = rb_hash_new();
state->memo = Qnil;
@@ -616,7 +704,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
}
/*
- * call-seq: check_circular?(object)
+ * call-seq: check_circular?
*
* Returns true, if circular data structures should be checked,
* otherwise returns false.
@@ -628,6 +716,44 @@ static VALUE cState_check_circular_p(VALUE self)
}
/*
+ * call-seq: max_nesting
+ *
+ * This integer returns the maximum level of data structure nesting in
+ * the generated JSON, max_nesting = 0 if no maximum is checked.
+ */
+static VALUE cState_max_nesting(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->max_nesting);
+}
+
+/*
+ * call-seq: max_nesting=(depth)
+ *
+ * This sets the maximum level of data structure nesting in the generated JSON
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
+ */
+static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
+{
+ GET_STATE(self);
+ Check_Type(depth, T_FIXNUM);
+ state->max_nesting = FIX2LONG(depth);
+ return Qnil;
+}
+
+/*
+ * call-seq: allow_nan?
+ *
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
+ * returns false.
+ */
+static VALUE cState_allow_nan_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->allow_nan ? Qtrue : Qfalse;
+}
+
+/*
* call-seq: seen?(object)
*
* Returns _true_, if _object_ was already seen during this generating run.
@@ -668,6 +794,7 @@ void Init_generator()
mGenerator = rb_define_module_under(mExt, "Generator");
eGeneratorError = rb_path2class("JSON::GeneratorError");
eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
+ eNestingError = rb_path2class("JSON::NestingError");
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
rb_define_alloc_func(cState, cState_s_allocate);
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
@@ -684,9 +811,15 @@ void Init_generator()
rb_define_method(cState, "array_nl", cState_array_nl, 0);
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
+ rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
+ rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
+ rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
rb_define_method(cState, "seen?", cState_seen_p, 1);
rb_define_method(cState, "remember", cState_remember, 1);
rb_define_method(cState, "forget", cState_forget, 1);
+ rb_define_method(cState, "configure", cState_configure, 1);
+ rb_define_method(cState, "to_h", cState_to_h, 0);
+
mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
mObject = rb_define_module_under(mGeneratorMethods, "Object");
rb_define_method(mObject, "to_json", mObject_to_json, -1);
@@ -721,6 +854,8 @@ void Init_generator()
i_object_nl = rb_intern("object_nl");
i_array_nl = rb_intern("array_nl");
i_check_circular = rb_intern("check_circular");
+ i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
i_pack = rb_intern("pack");
i_unpack = rb_intern("unpack");
i_create_id = rb_intern("create_id");
diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb
index cef706a4b9..b2e7b051a4 100644
--- a/ext/json/ext/parser/extconf.rb
+++ b/ext/json/ext/parser/extconf.rb
@@ -2,8 +2,8 @@ require 'mkmf'
require 'rbconfig'
if CONFIG['CC'] =~ /gcc/
- #CONFIG['CC'] += ' -Wall -ggdb'
- CONFIG['CC'] += ' -Wall'
+ #$CFLAGS += ' -Wall -ggdb'
+ $CFLAGS += ' -Wall'
end
have_header 'st.h'
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index 3b84110374..a1950b067a 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser/parser.c
@@ -9,8 +9,12 @@
#define EVIL 0x666
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
+static VALUE CNaN, CInfinity, CMinusInfinity;
-static ID i_json_creatable_p, i_json_create, i_create_id, i_chr, i_max_nesting;
+static ID i_json_creatable_p, i_json_create, i_create_id, i_chr, i_max_nesting,
+ i_allow_nan;
+
+#define MinusInfinity "-Infinity"
typedef struct JSON_ParserStruct {
VALUE Vsource;
@@ -20,6 +24,7 @@ typedef struct JSON_ParserStruct {
VALUE create_id;
int max_nesting;
int current_nesting;
+ int allow_nan;
} JSON_Parser;
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
@@ -33,18 +38,18 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
JSON_Parser *json; \
Data_Get_Struct(self, JSON_Parser, json);
-#line 58 "parser.rl"
+#line 66 "parser.rl"
-#line 41 "parser.c"
+#line 46 "parser.c"
static const int JSON_object_start = 1;
static const int JSON_object_first_final = 27;
static const int JSON_object_error = 0;
static const int JSON_object_en_main = 1;
-#line 91 "parser.rl"
+#line 99 "parser.rl"
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -59,13 +64,13 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = rb_hash_new();
-#line 63 "parser.c"
+#line 68 "parser.c"
{
cs = JSON_object_start;
}
-#line 105 "parser.rl"
+#line 113 "parser.rl"
-#line 69 "parser.c"
+#line 74 "parser.c"
{
if ( p == pe )
goto _out;
@@ -92,7 +97,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 77 "parser.rl"
+#line 85 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, &last_name);
if (np == NULL) goto _out3; else {p = (( np))-1;}
@@ -102,7 +107,7 @@ st3:
if ( ++p == pe )
goto _out3;
case 3:
-#line 106 "parser.c"
+#line 111 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -154,6 +159,8 @@ case 8:
case 34: goto tr11;
case 45: goto tr11;
case 47: goto st19;
+ case 73: goto tr11;
+ case 78: goto tr11;
case 91: goto tr11;
case 102: goto tr11;
case 110: goto tr11;
@@ -167,7 +174,7 @@ case 8:
goto st8;
goto st0;
tr11:
-#line 66 "parser.rl"
+#line 74 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v);
@@ -183,7 +190,7 @@ st9:
if ( ++p == pe )
goto _out9;
case 9:
-#line 187 "parser.c"
+#line 194 "parser.c"
switch( (*p) ) {
case 13: goto st9;
case 32: goto st9;
@@ -272,14 +279,14 @@ case 18:
goto st9;
goto st18;
tr4:
-#line 82 "parser.rl"
+#line 90 "parser.rl"
{ goto _out27; }
goto st27;
st27:
if ( ++p == pe )
goto _out27;
case 27:
-#line 283 "parser.c"
+#line 290 "parser.c"
goto st0;
st19:
if ( ++p == pe )
@@ -376,7 +383,7 @@ case 26:
_out: {}
}
-#line 106 "parser.rl"
+#line 114 "parser.rl"
if (cs >= JSON_object_first_final) {
VALUE klassname = rb_hash_aref(*result, json->create_id);
@@ -393,14 +400,14 @@ case 26:
}
-#line 397 "parser.c"
+#line 404 "parser.c"
static const int JSON_value_start = 1;
-static const int JSON_value_first_final = 12;
+static const int JSON_value_first_final = 21;
static const int JSON_value_error = 0;
static const int JSON_value_en_main = 1;
-#line 177 "parser.rl"
+#line 210 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -408,13 +415,13 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 412 "parser.c"
+#line 419 "parser.c"
{
cs = JSON_value_start;
}
-#line 184 "parser.rl"
+#line 217 "parser.rl"
-#line 418 "parser.c"
+#line 425 "parser.c"
{
if ( p == pe )
goto _out;
@@ -424,11 +431,13 @@ case 1:
switch( (*p) ) {
case 34: goto tr0;
case 45: goto tr2;
- case 91: goto tr3;
- case 102: goto st2;
- case 110: goto st6;
- case 116: goto st9;
- case 123: goto tr7;
+ case 73: goto st2;
+ case 78: goto st9;
+ case 91: goto tr5;
+ case 102: goto st11;
+ case 110: goto st15;
+ case 116: goto st18;
+ case 123: goto tr9;
}
if ( 48 <= (*p) && (*p) <= 57 )
goto tr2;
@@ -436,142 +445,234 @@ case 1:
st0:
goto _out0;
tr0:
-#line 136 "parser.rl"
+#line 158 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, result);
- if (np == NULL) goto _out12; else {p = (( np))-1;}
+ if (np == NULL) goto _out21; else {p = (( np))-1;}
}
- goto st12;
+ goto st21;
tr2:
-#line 141 "parser.rl"
+#line 163 "parser.rl"
{
char *np;
+ if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
+ if (json->allow_nan) {
+ *result = CMinusInfinity;
+ {p = (( p + 10))-1;}
+ goto _out21;
+ } else {
+ rb_raise(eParserError, "unexpected token at '%s'", p);
+ }
+ }
np = JSON_parse_float(json, p, pe, result);
if (np != NULL) {p = (( np))-1;}
np = JSON_parse_integer(json, p, pe, result);
if (np != NULL) {p = (( np))-1;}
- goto _out12;
+ goto _out21;
}
- goto st12;
-tr3:
-#line 150 "parser.rl"
+ goto st21;
+tr5:
+#line 181 "parser.rl"
{
char *np;
json->current_nesting += 1;
np = JSON_parse_array(json, p, pe, result);
json->current_nesting -= 1;
- if (np == NULL) goto _out12; else {p = (( np))-1;}
+ if (np == NULL) goto _out21; else {p = (( np))-1;}
}
- goto st12;
-tr7:
-#line 158 "parser.rl"
+ goto st21;
+tr9:
+#line 189 "parser.rl"
{
char *np;
json->current_nesting += 1;
np = JSON_parse_object(json, p, pe, result);
json->current_nesting -= 1;
- if (np == NULL) goto _out12; else {p = (( np))-1;}
+ if (np == NULL) goto _out21; else {p = (( np))-1;}
}
- goto st12;
-tr11:
-#line 130 "parser.rl"
+ goto st21;
+tr16:
+#line 151 "parser.rl"
+ {
+ if (json->allow_nan) {
+ *result = CInfinity;
+ } else {
+ rb_raise(eParserError, "unexpected token at '%s'", p - 8);
+ }
+ }
+ goto st21;
+tr18:
+#line 144 "parser.rl"
+ {
+ if (json->allow_nan) {
+ *result = CNaN;
+ } else {
+ rb_raise(eParserError, "unexpected token at '%s'", p - 2);
+ }
+ }
+ goto st21;
+tr22:
+#line 138 "parser.rl"
{
*result = Qfalse;
}
- goto st12;
-tr14:
-#line 127 "parser.rl"
+ goto st21;
+tr25:
+#line 135 "parser.rl"
{
*result = Qnil;
}
- goto st12;
-tr17:
-#line 133 "parser.rl"
+ goto st21;
+tr28:
+#line 141 "parser.rl"
{
*result = Qtrue;
}
- goto st12;
-st12:
+ goto st21;
+st21:
if ( ++p == pe )
- goto _out12;
-case 12:
-#line 166 "parser.rl"
- { goto _out12; }
-#line 501 "parser.c"
+ goto _out21;
+case 21:
+#line 197 "parser.rl"
+ { goto _out21; }
+#line 539 "parser.c"
goto st0;
st2:
if ( ++p == pe )
goto _out2;
case 2:
- if ( (*p) == 97 )
+ if ( (*p) == 110 )
goto st3;
goto st0;
st3:
if ( ++p == pe )
goto _out3;
case 3:
- if ( (*p) == 108 )
+ if ( (*p) == 102 )
goto st4;
goto st0;
st4:
if ( ++p == pe )
goto _out4;
case 4:
- if ( (*p) == 115 )
+ if ( (*p) == 105 )
goto st5;
goto st0;
st5:
if ( ++p == pe )
goto _out5;
case 5:
- if ( (*p) == 101 )
- goto tr11;
+ if ( (*p) == 110 )
+ goto st6;
goto st0;
st6:
if ( ++p == pe )
goto _out6;
case 6:
- if ( (*p) == 117 )
+ if ( (*p) == 105 )
goto st7;
goto st0;
st7:
if ( ++p == pe )
goto _out7;
case 7:
- if ( (*p) == 108 )
+ if ( (*p) == 116 )
goto st8;
goto st0;
st8:
if ( ++p == pe )
goto _out8;
case 8:
- if ( (*p) == 108 )
- goto tr14;
+ if ( (*p) == 121 )
+ goto tr16;
goto st0;
st9:
if ( ++p == pe )
goto _out9;
case 9:
- if ( (*p) == 114 )
+ if ( (*p) == 97 )
goto st10;
goto st0;
st10:
if ( ++p == pe )
goto _out10;
case 10:
- if ( (*p) == 117 )
- goto st11;
+ if ( (*p) == 78 )
+ goto tr18;
goto st0;
st11:
if ( ++p == pe )
goto _out11;
case 11:
+ if ( (*p) == 97 )
+ goto st12;
+ goto st0;
+st12:
+ if ( ++p == pe )
+ goto _out12;
+case 12:
+ if ( (*p) == 108 )
+ goto st13;
+ goto st0;
+st13:
+ if ( ++p == pe )
+ goto _out13;
+case 13:
+ if ( (*p) == 115 )
+ goto st14;
+ goto st0;
+st14:
+ if ( ++p == pe )
+ goto _out14;
+case 14:
+ if ( (*p) == 101 )
+ goto tr22;
+ goto st0;
+st15:
+ if ( ++p == pe )
+ goto _out15;
+case 15:
+ if ( (*p) == 117 )
+ goto st16;
+ goto st0;
+st16:
+ if ( ++p == pe )
+ goto _out16;
+case 16:
+ if ( (*p) == 108 )
+ goto st17;
+ goto st0;
+st17:
+ if ( ++p == pe )
+ goto _out17;
+case 17:
+ if ( (*p) == 108 )
+ goto tr25;
+ goto st0;
+st18:
+ if ( ++p == pe )
+ goto _out18;
+case 18:
+ if ( (*p) == 114 )
+ goto st19;
+ goto st0;
+st19:
+ if ( ++p == pe )
+ goto _out19;
+case 19:
+ if ( (*p) == 117 )
+ goto st20;
+ goto st0;
+st20:
+ if ( ++p == pe )
+ goto _out20;
+case 20:
if ( (*p) == 101 )
- goto tr17;
+ goto tr28;
goto st0;
}
_out0: cs = 0; goto _out;
- _out12: cs = 12; goto _out;
+ _out21: cs = 21; goto _out;
_out2: cs = 2; goto _out;
_out3: cs = 3; goto _out;
_out4: cs = 4; goto _out;
@@ -582,10 +683,19 @@ case 11:
_out9: cs = 9; goto _out;
_out10: cs = 10; goto _out;
_out11: cs = 11; goto _out;
+ _out12: cs = 12; goto _out;
+ _out13: cs = 13; goto _out;
+ _out14: cs = 14; goto _out;
+ _out15: cs = 15; goto _out;
+ _out16: cs = 16; goto _out;
+ _out17: cs = 17; goto _out;
+ _out18: cs = 18; goto _out;
+ _out19: cs = 19; goto _out;
+ _out20: cs = 20; goto _out;
_out: {}
}
-#line 185 "parser.rl"
+#line 218 "parser.rl"
if (cs >= JSON_value_first_final) {
return p;
@@ -595,14 +705,14 @@ case 11:
}
-#line 599 "parser.c"
+#line 709 "parser.c"
static const int JSON_integer_start = 1;
static const int JSON_integer_first_final = 5;
static const int JSON_integer_error = 0;
static const int JSON_integer_en_main = 1;
-#line 201 "parser.rl"
+#line 234 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -610,14 +720,14 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
int cs = EVIL;
-#line 614 "parser.c"
+#line 724 "parser.c"
{
cs = JSON_integer_start;
}
-#line 208 "parser.rl"
+#line 241 "parser.rl"
json->memo = p;
-#line 621 "parser.c"
+#line 731 "parser.c"
{
if ( p == pe )
goto _out;
@@ -650,14 +760,14 @@ case 3:
goto st0;
goto tr4;
tr4:
-#line 198 "parser.rl"
+#line 231 "parser.rl"
{ goto _out5; }
goto st5;
st5:
if ( ++p == pe )
goto _out5;
case 5:
-#line 661 "parser.c"
+#line 771 "parser.c"
goto st0;
st4:
if ( ++p == pe )
@@ -675,7 +785,7 @@ case 4:
_out: {}
}
-#line 210 "parser.rl"
+#line 243 "parser.rl"
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
@@ -687,14 +797,14 @@ case 4:
}
-#line 691 "parser.c"
+#line 801 "parser.c"
static const int JSON_float_start = 1;
static const int JSON_float_first_final = 10;
static const int JSON_float_error = 0;
static const int JSON_float_en_main = 1;
-#line 232 "parser.rl"
+#line 265 "parser.rl"
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -702,14 +812,14 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 706 "parser.c"
+#line 816 "parser.c"
{
cs = JSON_float_start;
}
-#line 239 "parser.rl"
+#line 272 "parser.rl"
json->memo = p;
-#line 713 "parser.c"
+#line 823 "parser.c"
{
if ( p == pe )
goto _out;
@@ -766,14 +876,14 @@ case 5:
goto st0;
goto tr7;
tr7:
-#line 226 "parser.rl"
+#line 259 "parser.rl"
{ goto _out10; }
goto st10;
st10:
if ( ++p == pe )
goto _out10;
case 10:
-#line 777 "parser.c"
+#line 887 "parser.c"
goto st0;
st6:
if ( ++p == pe )
@@ -833,7 +943,7 @@ case 9:
_out: {}
}
-#line 241 "parser.rl"
+#line 274 "parser.rl"
if (cs >= JSON_float_first_final) {
long len = p - json->memo;
@@ -846,14 +956,14 @@ case 9:
-#line 850 "parser.c"
+#line 960 "parser.c"
static const int JSON_array_start = 1;
static const int JSON_array_first_final = 17;
static const int JSON_array_error = 0;
static const int JSON_array_en_main = 1;
-#line 277 "parser.rl"
+#line 310 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -866,13 +976,13 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
*result = rb_ary_new();
-#line 870 "parser.c"
+#line 980 "parser.c"
{
cs = JSON_array_start;
}
-#line 289 "parser.rl"
+#line 322 "parser.rl"
-#line 876 "parser.c"
+#line 986 "parser.c"
{
if ( p == pe )
goto _out;
@@ -894,6 +1004,8 @@ case 2:
case 34: goto tr2;
case 45: goto tr2;
case 47: goto st13;
+ case 73: goto tr2;
+ case 78: goto tr2;
case 91: goto tr2;
case 93: goto tr4;
case 102: goto tr2;
@@ -908,7 +1020,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 258 "parser.rl"
+#line 291 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v);
@@ -924,7 +1036,7 @@ st3:
if ( ++p == pe )
goto _out3;
case 3:
-#line 928 "parser.c"
+#line 1040 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -945,6 +1057,8 @@ case 4:
case 34: goto tr2;
case 45: goto tr2;
case 47: goto st5;
+ case 73: goto tr2;
+ case 78: goto tr2;
case 91: goto tr2;
case 102: goto tr2;
case 110: goto tr2;
@@ -1022,14 +1136,14 @@ case 12:
goto st3;
goto st12;
tr4:
-#line 269 "parser.rl"
+#line 302 "parser.rl"
{ goto _out17; }
goto st17;
st17:
if ( ++p == pe )
goto _out17;
case 17:
-#line 1033 "parser.c"
+#line 1147 "parser.c"
goto st0;
st13:
if ( ++p == pe )
@@ -1084,7 +1198,7 @@ case 16:
_out: {}
}
-#line 290 "parser.rl"
+#line 323 "parser.rl"
if(cs >= JSON_array_first_final) {
return p + 1;
@@ -1150,14 +1264,14 @@ static VALUE json_string_unescape(char *p, char *pe)
}
-#line 1154 "parser.c"
+#line 1268 "parser.c"
static const int JSON_string_start = 1;
static const int JSON_string_first_final = 8;
static const int JSON_string_error = 0;
static const int JSON_string_en_main = 1;
-#line 368 "parser.rl"
+#line 401 "parser.rl"
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -1166,14 +1280,14 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = rb_str_new("", 0);
-#line 1170 "parser.c"
+#line 1284 "parser.c"
{
cs = JSON_string_start;
}
-#line 376 "parser.rl"
+#line 409 "parser.rl"
json->memo = p;
-#line 1177 "parser.c"
+#line 1291 "parser.c"
{
if ( p == pe )
goto _out;
@@ -1197,19 +1311,19 @@ case 2:
goto st0;
goto st2;
tr2:
-#line 360 "parser.rl"
+#line 393 "parser.rl"
{
*result = json_string_unescape(json->memo + 1, p);
if (NIL_P(*result)) goto _out8; else {p = (( p + 1))-1;}
}
-#line 365 "parser.rl"
+#line 398 "parser.rl"
{ goto _out8; }
goto st8;
st8:
if ( ++p == pe )
goto _out8;
case 8:
-#line 1213 "parser.c"
+#line 1327 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1284,7 +1398,7 @@ case 7:
_out: {}
}
-#line 378 "parser.rl"
+#line 411 "parser.rl"
if (cs >= JSON_string_first_final) {
return p + 1;
@@ -1295,14 +1409,14 @@ case 7:
-#line 1299 "parser.c"
+#line 1413 "parser.c"
static const int JSON_start = 1;
static const int JSON_first_final = 10;
static const int JSON_error = 0;
static const int JSON_en_main = 1;
-#line 412 "parser.rl"
+#line 445 "parser.rl"
/*
@@ -1329,7 +1443,11 @@ static const int JSON_en_main = 1;
*
* _opts_ can have the following keys:
* * *max_nesting*: The maximum depth of nesting allowed in the parsed data
- * structures. Disable depth checking with :max_nesting => false.
+ * structures. Disable depth checking with :max_nesting => false|nil|0, it
+ * defaults to 19.
+ * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
+ * defiance of RFC 4627 to be parsed by the Parser. This option defaults to
+ * false.
*/
static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
{
@@ -1345,14 +1463,15 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
rb_raise(eParserError, "A JSON text must at least contain two octets!");
}
json->max_nesting = 19;
+ json->allow_nan = 0;
if (!NIL_P(opts)) {
opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
if (NIL_P(opts)) {
rb_raise(rb_eArgError, "opts needs to be like a hash");
} else {
- VALUE s_max_nesting = ID2SYM(i_max_nesting);
- if (st_lookup(RHASH(opts)->tbl, s_max_nesting, 0)) {
- VALUE max_nesting = rb_hash_aref(opts, s_max_nesting);
+ VALUE tmp = ID2SYM(i_max_nesting);
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
if (RTEST(max_nesting)) {
Check_Type(max_nesting, T_FIXNUM);
json->max_nesting = FIX2INT(max_nesting);
@@ -1360,6 +1479,11 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
json->max_nesting = 0;
}
}
+ tmp = ID2SYM(i_allow_nan);
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
+ VALUE allow_nan = rb_hash_aref(opts, tmp);
+ if (RTEST(allow_nan)) json->allow_nan = 1;
+ }
}
}
json->current_nesting = 0;
@@ -1396,15 +1520,15 @@ static VALUE cParser_parse(VALUE self)
GET_STRUCT;
-#line 1400 "parser.c"
+#line 1524 "parser.c"
{
cs = JSON_start;
}
-#line 505 "parser.rl"
+#line 548 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1408 "parser.c"
+#line 1532 "parser.c"
{
if ( p == pe )
goto _out;
@@ -1459,7 +1583,7 @@ case 5:
goto st1;
goto st5;
tr3:
-#line 401 "parser.rl"
+#line 434 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1468,7 +1592,7 @@ tr3:
}
goto st10;
tr4:
-#line 394 "parser.rl"
+#line 427 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1480,7 +1604,7 @@ st10:
if ( ++p == pe )
goto _out10;
case 10:
-#line 1484 "parser.c"
+#line 1608 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -1536,7 +1660,7 @@ case 9:
_out: {}
}
-#line 508 "parser.rl"
+#line 551 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -1593,9 +1717,14 @@ void Init_parser()
rb_define_method(cParser, "parse", cParser_parse, 0);
rb_define_method(cParser, "source", cParser_source, 0);
+ CNaN = rb_const_get(mJSON, rb_intern("NaN"));
+ CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
+ CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
+
i_json_creatable_p = rb_intern("json_creatable?");
i_json_create = rb_intern("json_create");
i_create_id = rb_intern("create_id");
i_chr = rb_intern("chr");
i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
}
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl
index 9e2a5015eb..c1373c23dd 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser/parser.rl
@@ -8,8 +8,12 @@
#define EVIL 0x666
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
+static VALUE CNaN, CInfinity, CMinusInfinity;
-static ID i_json_creatable_p, i_json_create, i_create_id, i_chr, i_max_nesting;
+static ID i_json_creatable_p, i_json_create, i_create_id, i_chr, i_max_nesting,
+ i_allow_nan;
+
+#define MinusInfinity "-Infinity"
typedef struct JSON_ParserStruct {
VALUE Vsource;
@@ -19,6 +23,7 @@ typedef struct JSON_ParserStruct {
VALUE create_id;
int max_nesting;
int current_nesting;
+ int allow_nan;
} JSON_Parser;
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
@@ -47,7 +52,10 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
Vnull = 'null';
Vfalse = 'false';
Vtrue = 'true';
- begin_value = [nft"\-[{] | digit;
+ VNaN = 'NaN';
+ VInfinity = 'Infinity';
+ VMinusInfinity = '-Infinity';
+ begin_value = [nft"\-[{NI] | digit;
begin_object = '{';
end_object = '}';
begin_array = '[';
@@ -133,6 +141,20 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
action parse_true {
*result = Qtrue;
}
+ action parse_nan {
+ if (json->allow_nan) {
+ *result = CNaN;
+ } else {
+ rb_raise(eParserError, "unexpected token at '%s'", p - 2);
+ }
+ }
+ action parse_infinity {
+ if (json->allow_nan) {
+ *result = CInfinity;
+ } else {
+ rb_raise(eParserError, "unexpected token at '%s'", p - 8);
+ }
+ }
action parse_string {
char *np = JSON_parse_string(json, fpc, pe, result);
if (np == NULL) fbreak; else fexec np;
@@ -140,6 +162,15 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
action parse_number {
char *np;
+ if(pe > fpc + 9 && !strncmp(MinusInfinity, fpc, 9)) {
+ if (json->allow_nan) {
+ *result = CMinusInfinity;
+ fexec p + 10;
+ fbreak;
+ } else {
+ rb_raise(eParserError, "unexpected token at '%s'", p);
+ }
+ }
np = JSON_parse_float(json, fpc, pe, result);
if (np != NULL) fexec np;
np = JSON_parse_integer(json, fpc, pe, result);
@@ -169,6 +200,8 @@ main := (
Vnull @parse_null |
Vfalse @parse_false |
Vtrue @parse_true |
+ VNaN @parse_nan |
+ VInfinity @parse_infinity |
begin_number >parse_number |
begin_string >parse_string |
begin_array >parse_array |
@@ -435,7 +468,11 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
*
* _opts_ can have the following keys:
* * *max_nesting*: The maximum depth of nesting allowed in the parsed data
- * structures. Disable depth checking with :max_nesting => false.
+ * structures. Disable depth checking with :max_nesting => false|nil|0, it
+ * defaults to 19.
+ * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
+ * defiance of RFC 4627 to be parsed by the Parser. This option defaults to
+ * false.
*/
static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
{
@@ -451,14 +488,15 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
rb_raise(eParserError, "A JSON text must at least contain two octets!");
}
json->max_nesting = 19;
+ json->allow_nan = 0;
if (!NIL_P(opts)) {
opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
if (NIL_P(opts)) {
rb_raise(rb_eArgError, "opts needs to be like a hash");
} else {
- VALUE s_max_nesting = ID2SYM(i_max_nesting);
- if (st_lookup(RHASH(opts)->tbl, s_max_nesting, 0)) {
- VALUE max_nesting = rb_hash_aref(opts, s_max_nesting);
+ VALUE tmp = ID2SYM(i_max_nesting);
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
if (RTEST(max_nesting)) {
Check_Type(max_nesting, T_FIXNUM);
json->max_nesting = FIX2INT(max_nesting);
@@ -466,6 +504,11 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
json->max_nesting = 0;
}
}
+ tmp = ID2SYM(i_allow_nan);
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
+ VALUE allow_nan = rb_hash_aref(opts, tmp);
+ if (RTEST(allow_nan)) json->allow_nan = 1;
+ }
}
}
json->current_nesting = 0;
@@ -561,9 +604,14 @@ void Init_parser()
rb_define_method(cParser, "parse", cParser_parse, 0);
rb_define_method(cParser, "source", cParser_source, 0);
+ CNaN = rb_const_get(mJSON, rb_intern("NaN"));
+ CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
+ CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
+
i_json_creatable_p = rb_intern("json_creatable?");
i_json_create = rb_intern("json_create");
i_create_id = rb_intern("create_id");
i_chr = rb_intern("chr");
i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
}