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");
}