ext/json/ext/parser/parser.rl in json_pure-1.0.4 vs ext/json/ext/parser/parser.rl in json_pure-1.1.0

- old
+ new

@@ -1,27 +1,26 @@ /* vim: set cin et sw=4 ts=4: */ #include "ruby.h" #include "re.h" +#include "st.h" #include "unicode.h" -#ifndef swap16 -#define swap16(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) -#endif - #define EVIL 0x666 -static VALUE mJSON, mExt, cParser, eParserError; +static VALUE mJSON, mExt, cParser, eParserError, eNestingError; -static ID i_json_creatable_p, i_json_create, i_create_id, i_chr; +static ID i_json_creatable_p, i_json_create, i_create_id, i_chr, i_max_nesting; typedef struct JSON_ParserStruct { VALUE Vsource; char *source; long len; char *memo; VALUE create_id; + int max_nesting; + int current_nesting; } JSON_Parser; static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result); static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result); static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result); @@ -93,10 +92,15 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; VALUE last_name = Qnil; + + if (json->max_nesting && json->current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is to deep", json->current_nesting); + } + *result = rb_hash_new(); %% write init; %% write exec; @@ -142,16 +146,22 @@ if (np != NULL) fexec np; fbreak; } action parse_array { - char *np = JSON_parse_array(json, fpc, pe, result); + char *np; + json->current_nesting += 1; + np = JSON_parse_array(json, fpc, pe, result); + json->current_nesting -= 1; if (np == NULL) fbreak; else fexec np; } action parse_object { - char *np = JSON_parse_object(json, fpc, pe, result); + char *np; + json->current_nesting += 1; + np = JSON_parse_object(json, fpc, pe, result); + json->current_nesting -= 1; if (np == NULL) fbreak; else fexec np; } action exit { fbreak; } @@ -267,10 +277,14 @@ }%% static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; + + if (json->max_nesting && json->current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is to deep", json->current_nesting); + } *result = rb_ary_new(); %% write init; %% write exec; @@ -279,11 +293,11 @@ } else { rb_raise(eParserError, "unexpected token at '%s'", p); } } -static VALUE json_string_escape(char *p, char *pe) +static VALUE json_string_unescape(char *p, char *pe) { VALUE result = rb_str_buf_new(pe - p + 1); while (p < pe) { if (*p == '\\') { @@ -320,10 +334,14 @@ return Qnil; } else { p = JSON_convert_UTF16_to_UTF8(result, p, pe, strictConversion); } break; + default: + rb_str_buf_cat(result, p, 1); + p++; + break; } } else { char *q = p; while (*q != '\\' && q < pe) q++; rb_str_buf_cat(result, p, q - p); @@ -338,17 +356,17 @@ include JSON_common; write data; action parse_string { - *result = json_string_escape(json->memo + 1, p); + *result = json_string_unescape(json->memo + 1, p); if (NIL_P(*result)) fbreak; else fexec p + 1; } action exit { fbreak; } - main := '"' ((^(["\\] | 0..0x1f) | '\\'["\\/bfnrt] | '\\u'[0-9a-fA-F]{4})* %parse_string) '"' @exit; + main := '"' ((^(["\\] | 0..0x1f) | '\\'["\\/bfnrt] | '\\u'[0-9a-fA-F]{4} | '\\'^(["\\/bfnrtu]|0..0x1f))* %parse_string) '"' @exit; }%% static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; @@ -372,16 +390,20 @@ write data; include JSON_common; action parse_object { - char *np = JSON_parse_object(json, fpc, pe, &result); + char *np; + json->current_nesting = 1; + np = JSON_parse_object(json, fpc, pe, &result); if (np == NULL) fbreak; else fexec np; } action parse_array { - char *np = JSON_parse_array(json, fpc, pe, &result); + char *np; + json->current_nesting = 1; + np = JSON_parse_array(json, fpc, pe, &result); if (np == NULL) fbreak; else fexec np; } main := ignore* ( begin_object >parse_object | @@ -400,25 +422,55 @@ * with the method parser= in JSON. * */ /* - * call-seq: new(source) + * call-seq: new(source, opts => {}) * * Creates a new JSON::Ext::Parser instance for the string _source_. + * + * Creates a new JSON::Ext::Parser instance for the string _source_. + * + * It will be configured by the _opts_ hash. _opts_ can have the following + * keys: + * + * _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. */ -static VALUE cParser_initialize(VALUE self, VALUE source) +static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { char *ptr; long len; + VALUE source, opts; GET_STRUCT; + rb_scan_args(argc, argv, "11", &source, &opts); source = StringValue(source); ptr = RSTRING_PTR(source); len = RSTRING_LEN(source); if (len < 2) { rb_raise(eParserError, "A JSON text must at least contain two octets!"); } + json->max_nesting = 19; + 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); + if (RTEST(max_nesting)) { + Check_Type(max_nesting, T_FIXNUM); + json->max_nesting = FIX2INT(max_nesting); + } else { + json->max_nesting = 0; + } + } + } + } + json->current_nesting = 0; /* Convert these? if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) { rb_raise(eParserError, "Only UTF8 octet streams are supported atm!"); } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) { @@ -501,15 +553,17 @@ { mJSON = rb_define_module("JSON"); mExt = rb_define_module_under(mJSON, "Ext"); cParser = rb_define_class_under(mExt, "Parser", rb_cObject); eParserError = rb_path2class("JSON::ParserError"); + eNestingError = rb_path2class("JSON::NestingError"); rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, 1); + rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); 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"); }