summaryrefslogtreecommitdiff
path: root/ext/json
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json')
-rw-r--r--ext/json/ext/generator/generator.c71
-rw-r--r--ext/json/ext/parser/parser.c205
-rw-r--r--ext/json/ext/parser/parser.rl30
-rwxr-xr-x[-rw-r--r--]ext/json/lib/json.rb162
-rw-r--r--ext/json/lib/json/add/core.rb2
-rwxr-xr-x[-rw-r--r--]ext/json/lib/json/common.rb0
-rwxr-xr-x[-rw-r--r--]ext/json/lib/json/editor.rb13
-rw-r--r--ext/json/lib/json/ext.rb2
-rw-r--r--ext/json/lib/json/pure.rb22
-rw-r--r--ext/json/lib/json/pure/generator.rb394
-rw-r--r--ext/json/lib/json/pure/parser.rb269
-rw-r--r--ext/json/lib/json/version.rb2
12 files changed, 339 insertions, 833 deletions
diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c
index 1f48d3c780..108e80fd65 100644
--- a/ext/json/ext/generator/generator.c
+++ b/ext/json/ext/generator/generator.c
@@ -1,8 +1,33 @@
#include <string.h>
#include "ruby.h"
+#if HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#endif
+#if HAVE_ST_H
+#include "st.h"
+#endif
#include "unicode.h"
#include <math.h>
+#ifndef RHASH_TBL
+#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
+#endif
+
+#ifndef RHASH_SIZE
+#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
+#endif
+
+#ifndef RFLOAT_VALUE
+#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
+#endif
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
+#else
+#define FORCE_UTF8(obj)
+#endif
+
#define check_max_nesting(state, depth) do { \
long current_nesting = 1 + depth; \
if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
@@ -163,6 +188,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
}
}
OBJ_INFECT(result, self);
+ FORCE_UTF8(result);
return result;
}
@@ -260,6 +286,7 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
result = mArray_json_transfrom(self, Vstate, Vdepth);
}
OBJ_INFECT(result, self);
+ FORCE_UTF8(result);
return result;
}
@@ -270,7 +297,9 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
*/
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
{
- return rb_funcall(self, i_to_s, 0);
+ VALUE result = rb_funcall(self, i_to_s, 0);
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -281,27 +310,29 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
{
JSON_Generator_State *state = NULL;
- VALUE Vstate, rest, tmp;
+ VALUE Vstate, rest, tmp, result;
double value = RFLOAT_VALUE(self);
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);
+ result = rb_funcall(self, i_to_s, 0);
} else {
tmp = rb_funcall(self, i_to_s, 0);
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
}
} else if (isnan(value)) {
if (!state || state->allow_nan) {
- return rb_funcall(self, i_to_s, 0);
+ result = rb_funcall(self, i_to_s, 0);
} else {
tmp = rb_funcall(self, i_to_s, 0);
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
}
} else {
- return rb_funcall(self, i_to_s, 0);
+ result = rb_funcall(self, i_to_s, 0);
}
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -310,7 +341,9 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
* Extends _modul_ with the String::Extend module.
*/
static VALUE mString_included_s(VALUE self, VALUE modul) {
- return rb_funcall(modul, i_extend, 1, mString_Extend);
+ VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -326,6 +359,7 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
rb_str_buf_cat2(result, "\"");
JSON_convert_UTF8_to_JSON(result, self, strictConversion);
rb_str_buf_cat2(result, "\"");
+ FORCE_UTF8(result);
return result;
}
@@ -343,6 +377,7 @@ static VALUE mString_to_json_raw_object(VALUE self) {
rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
rb_hash_aset(result, rb_str_new2("raw"), ary);
+ FORCE_UTF8(result);
return result;
}
@@ -353,9 +388,11 @@ static VALUE mString_to_json_raw_object(VALUE self) {
* to_json_raw_object of this String.
*/
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
- VALUE obj = mString_to_json_raw_object(self);
+ VALUE result, obj = mString_to_json_raw_object(self);
Check_Type(obj, T_HASH);
- return mHash_to_json(argc, argv, obj);
+ result = mHash_to_json(argc, argv, obj);
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -378,7 +415,9 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
*/
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
{
- return rb_str_new2("true");
+ VALUE result = rb_str_new2("true");
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -388,7 +427,9 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
*/
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
{
- return rb_str_new2("false");
+ VALUE result = rb_str_new2("false");
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -397,7 +438,9 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
*/
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
{
- return rb_str_new2("null");
+ VALUE result = rb_str_new2("null");
+ FORCE_UTF8(result);
+ return result;
}
/*
@@ -409,9 +452,11 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
*/
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
{
- VALUE string = rb_funcall(self, i_to_s, 0);
+ VALUE result, string = rb_funcall(self, i_to_s, 0);
Check_Type(string, T_STRING);
- return mString_to_json(argc, argv, string);
+ result = mString_to_json(argc, argv, string);
+ FORCE_UTF8(result);
+ return result;
}
/*
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index 9de618d0e6..6928eda9e5 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser/parser.c
@@ -1,10 +1,30 @@
+
#line 1 "parser.rl"
#include "ruby.h"
-#include "ruby/encoding.h"
#include "unicode.h"
+#if HAVE_RE_H
+#include "re.h"
+#endif
+#if HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#endif
+#if HAVE_ST_H
+#include "st.h"
+#endif
#define EVIL 0x666
+#ifndef RHASH_TBL
+#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
+#endif
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
+#else
+#define FORCE_UTF8(obj)
+#endif
+
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
static VALUE CNaN, CInfinity, CMinusInfinity;
@@ -35,18 +55,20 @@ 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 64 "parser.rl"
+#line 82 "parser.rl"
-#line 44 "parser.c"
+
+#line 64 "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 97 "parser.rl"
+
+#line 115 "parser.rl"
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -61,13 +83,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = rb_hash_new();
-#line 66 "parser.c"
+#line 87 "parser.c"
{
cs = JSON_object_start;
}
-#line 111 "parser.rl"
+
+#line 129 "parser.rl"
-#line 72 "parser.c"
+#line 94 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -95,7 +118,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 83 "parser.rl"
+#line 101 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, &last_name);
if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;}
@@ -105,7 +128,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 110 "parser.c"
+#line 132 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -172,7 +195,7 @@ case 8:
goto st8;
goto st0;
tr11:
-#line 72 "parser.rl"
+#line 90 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v);
@@ -188,7 +211,7 @@ st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
-#line 193 "parser.c"
+#line 215 "parser.c"
switch( (*p) ) {
case 13: goto st9;
case 32: goto st9;
@@ -277,14 +300,14 @@ case 18:
goto st9;
goto st18;
tr4:
-#line 88 "parser.rl"
+#line 106 "parser.rl"
{ p--; {p++; cs = 27; goto _out;} }
goto st27;
st27:
if ( ++p == pe )
goto _test_eof27;
case 27:
-#line 289 "parser.c"
+#line 311 "parser.c"
goto st0;
st19:
if ( ++p == pe )
@@ -381,7 +404,8 @@ case 26:
_test_eof: {}
_out: {}
}
-#line 112 "parser.rl"
+
+#line 130 "parser.rl"
if (cs >= JSON_object_first_final) {
if (RTEST(json->create_id)) {
@@ -400,14 +424,15 @@ case 26:
}
-#line 405 "parser.c"
+#line 428 "parser.c"
static const int JSON_value_start = 1;
static const int JSON_value_first_final = 21;
static const int JSON_value_error = 0;
static const int JSON_value_en_main = 1;
-#line 210 "parser.rl"
+
+#line 228 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -415,13 +440,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 420 "parser.c"
+#line 444 "parser.c"
{
cs = JSON_value_start;
}
-#line 217 "parser.rl"
+
+#line 235 "parser.rl"
-#line 426 "parser.c"
+#line 451 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -446,14 +472,14 @@ st0:
cs = 0;
goto _out;
tr0:
-#line 158 "parser.rl"
+#line 176 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, result);
if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
}
goto st21;
tr2:
-#line 163 "parser.rl"
+#line 181 "parser.rl"
{
char *np;
if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
@@ -473,7 +499,7 @@ tr2:
}
goto st21;
tr5:
-#line 181 "parser.rl"
+#line 199 "parser.rl"
{
char *np;
json->current_nesting++;
@@ -483,7 +509,7 @@ tr5:
}
goto st21;
tr9:
-#line 189 "parser.rl"
+#line 207 "parser.rl"
{
char *np;
json->current_nesting++;
@@ -493,7 +519,7 @@ tr9:
}
goto st21;
tr16:
-#line 151 "parser.rl"
+#line 169 "parser.rl"
{
if (json->allow_nan) {
*result = CInfinity;
@@ -503,7 +529,7 @@ tr16:
}
goto st21;
tr18:
-#line 144 "parser.rl"
+#line 162 "parser.rl"
{
if (json->allow_nan) {
*result = CNaN;
@@ -513,19 +539,19 @@ tr18:
}
goto st21;
tr22:
-#line 138 "parser.rl"
+#line 156 "parser.rl"
{
*result = Qfalse;
}
goto st21;
tr25:
-#line 135 "parser.rl"
+#line 153 "parser.rl"
{
*result = Qnil;
}
goto st21;
tr28:
-#line 141 "parser.rl"
+#line 159 "parser.rl"
{
*result = Qtrue;
}
@@ -534,9 +560,9 @@ st21:
if ( ++p == pe )
goto _test_eof21;
case 21:
-#line 197 "parser.rl"
+#line 215 "parser.rl"
{ p--; {p++; cs = 21; goto _out;} }
-#line 541 "parser.c"
+#line 566 "parser.c"
goto st0;
st2:
if ( ++p == pe )
@@ -696,7 +722,8 @@ case 20:
_test_eof: {}
_out: {}
}
-#line 218 "parser.rl"
+
+#line 236 "parser.rl"
if (cs >= JSON_value_first_final) {
return p;
@@ -706,14 +733,15 @@ case 20:
}
-#line 711 "parser.c"
+#line 737 "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 234 "parser.rl"
+
+#line 252 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -721,14 +749,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
int cs = EVIL;
-#line 726 "parser.c"
+#line 753 "parser.c"
{
cs = JSON_integer_start;
}
-#line 241 "parser.rl"
+
+#line 259 "parser.rl"
json->memo = p;
-#line 733 "parser.c"
+#line 761 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -762,14 +791,14 @@ case 3:
goto st0;
goto tr4;
tr4:
-#line 231 "parser.rl"
+#line 249 "parser.rl"
{ p--; {p++; cs = 5; goto _out;} }
goto st5;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
-#line 774 "parser.c"
+#line 802 "parser.c"
goto st0;
st4:
if ( ++p == pe )
@@ -787,7 +816,8 @@ case 4:
_test_eof: {}
_out: {}
}
-#line 243 "parser.rl"
+
+#line 261 "parser.rl"
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
@@ -799,14 +829,15 @@ case 4:
}
-#line 804 "parser.c"
+#line 833 "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 265 "parser.rl"
+
+#line 283 "parser.rl"
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -814,14 +845,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 819 "parser.c"
+#line 849 "parser.c"
{
cs = JSON_float_start;
}
-#line 272 "parser.rl"
+
+#line 290 "parser.rl"
json->memo = p;
-#line 826 "parser.c"
+#line 857 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -879,14 +911,14 @@ case 5:
goto st0;
goto tr7;
tr7:
-#line 259 "parser.rl"
+#line 277 "parser.rl"
{ p--; {p++; cs = 10; goto _out;} }
goto st10;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 891 "parser.c"
+#line 922 "parser.c"
goto st0;
st6:
if ( ++p == pe )
@@ -946,7 +978,8 @@ case 9:
_test_eof: {}
_out: {}
}
-#line 274 "parser.rl"
+
+#line 292 "parser.rl"
if (cs >= JSON_float_first_final) {
long len = p - json->memo;
@@ -959,14 +992,15 @@ case 9:
-#line 964 "parser.c"
+#line 996 "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 310 "parser.rl"
+
+#line 328 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -979,13 +1013,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
*result = rb_ary_new();
-#line 984 "parser.c"
+#line 1017 "parser.c"
{
cs = JSON_array_start;
}
-#line 322 "parser.rl"
+
+#line 340 "parser.rl"
-#line 990 "parser.c"
+#line 1024 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1024,7 +1059,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 291 "parser.rl"
+#line 309 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v);
@@ -1040,7 +1075,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 1045 "parser.c"
+#line 1079 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -1140,14 +1175,14 @@ case 12:
goto st3;
goto st12;
tr4:
-#line 302 "parser.rl"
+#line 320 "parser.rl"
{ p--; {p++; cs = 17; goto _out;} }
goto st17;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
-#line 1152 "parser.c"
+#line 1186 "parser.c"
goto st0;
st13:
if ( ++p == pe )
@@ -1202,7 +1237,8 @@ case 16:
_test_eof: {}
_out: {}
}
-#line 323 "parser.rl"
+
+#line 341 "parser.rl"
if(cs >= JSON_array_first_final) {
return p + 1;
@@ -1268,14 +1304,15 @@ static VALUE json_string_unescape(char *p, char *pe)
}
-#line 1273 "parser.c"
+#line 1308 "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 401 "parser.rl"
+
+#line 425 "parser.rl"
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -1284,14 +1321,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = rb_str_new("", 0);
-#line 1289 "parser.c"
+#line 1325 "parser.c"
{
cs = JSON_string_start;
}
-#line 409 "parser.rl"
+
+#line 433 "parser.rl"
json->memo = p;
-#line 1296 "parser.c"
+#line 1333 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1316,19 +1354,25 @@ case 2:
goto st0;
goto st2;
tr2:
-#line 393 "parser.rl"
+#line 411 "parser.rl"
{
*result = json_string_unescape(json->memo + 1, p);
- if (NIL_P(*result)) { p--; {p++; cs = 8; goto _out;} } else {p = (( p + 1))-1;}
- }
-#line 398 "parser.rl"
+ if (NIL_P(*result)) {
+ p--;
+ {p++; cs = 8; goto _out;}
+ } else {
+ FORCE_UTF8(*result);
+ {p = (( p + 1))-1;}
+ }
+ }
+#line 422 "parser.rl"
{ p--; {p++; cs = 8; goto _out;} }
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
-#line 1333 "parser.c"
+#line 1376 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1403,11 +1447,11 @@ case 7:
_test_eof: {}
_out: {}
}
-#line 411 "parser.rl"
+
+#line 435 "parser.rl"
if (cs >= JSON_string_first_final) {
- rb_enc_associate(*result, rb_utf8_encoding());
- return p + 1;
+ return p + 1;
} else {
return NULL;
}
@@ -1415,14 +1459,15 @@ case 7:
-#line 1419 "parser.c"
+#line 1463 "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 445 "parser.rl"
+
+#line 469 "parser.rl"
/*
@@ -1545,15 +1590,16 @@ static VALUE cParser_parse(VALUE self)
GET_STRUCT;
-#line 1549 "parser.c"
+#line 1594 "parser.c"
{
cs = JSON_start;
}
-#line 567 "parser.rl"
+
+#line 591 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1557 "parser.c"
+#line 1603 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1609,7 +1655,7 @@ case 5:
goto st1;
goto st5;
tr3:
-#line 434 "parser.rl"
+#line 458 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1618,7 +1664,7 @@ tr3:
}
goto st10;
tr4:
-#line 427 "parser.rl"
+#line 451 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1630,7 +1676,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 1634 "parser.c"
+#line 1680 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -1686,7 +1732,8 @@ case 9:
_test_eof: {}
_out: {}
}
-#line 570 "parser.rl"
+
+#line 594 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl
index 8325a93795..ca1876a7fc 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser/parser.rl
@@ -1,8 +1,28 @@
#include "ruby.h"
#include "unicode.h"
+#if HAVE_RE_H
+#include "re.h"
+#endif
+#if HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#endif
+#if HAVE_ST_H
+#include "st.h"
+#endif
#define EVIL 0x666
+#ifndef RHASH_TBL
+#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
+#endif
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
+#else
+#define FORCE_UTF8(obj)
+#endif
+
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
static VALUE CNaN, CInfinity, CMinusInfinity;
@@ -390,8 +410,14 @@ static VALUE json_string_unescape(char *p, char *pe)
action parse_string {
*result = json_string_unescape(json->memo + 1, p);
- if (NIL_P(*result)) { fhold; fbreak; } else fexec p + 1;
- }
+ if (NIL_P(*result)) {
+ fhold;
+ fbreak;
+ } else {
+ FORCE_UTF8(*result);
+ fexec p + 1;
+ }
+ }
action exit { fhold; fbreak; }
diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb
index 640baaebb6..35fab1e11d 100644..100755
--- a/ext/json/lib/json.rb
+++ b/ext/json/lib/json.rb
@@ -77,57 +77,121 @@ require 'json/common'
#
# == Speed Comparisons
#
-# I have created some benchmark results (see the benchmarks subdir of the
-# package) for the JSON-Parser to estimate the speed up in the C extension:
-#
-# JSON::Pure::Parser:: 28.90 calls/second
-# JSON::Ext::Parser:: 505.50 calls/second
-#
-# This is ca. <b>17.5</b> times the speed of the pure Ruby implementation.
-#
-# I have benchmarked the JSON-Generator as well. This generates a few more
-# values, because there are different modes, that also influence the achieved
+# I have created some benchmark results (see the benchmarks/data-p4-3Ghz
+# subdir of the package) for the JSON-parser to estimate the speed up in the C
+# extension:
+#
+# Comparing times (call_time_mean):
+# 1 ParserBenchmarkExt#parser 900 repeats:
+# 553.922304770 ( real) -> 21.500x
+# 0.001805307
+# 2 ParserBenchmarkYAML#parser 1000 repeats:
+# 224.513358139 ( real) -> 8.714x
+# 0.004454078
+# 3 ParserBenchmarkPure#parser 1000 repeats:
+# 26.755020642 ( real) -> 1.038x
+# 0.037376163
+# 4 ParserBenchmarkRails#parser 1000 repeats:
+# 25.763381731 ( real) -> 1.000x
+# 0.038814780
+# calls/sec ( time) -> speed covers
+# secs/call
+#
+# In the table above 1 is JSON::Ext::Parser, 2 is YAML.load with YAML
+# compatbile JSON document, 3 is is JSON::Pure::Parser, and 4 is
+# ActiveSupport::JSON.decode. The ActiveSupport JSON-decoder converts the
+# input first to YAML and then uses the YAML-parser, the conversion seems to
+# slow it down so much that it is only as fast as the JSON::Pure::Parser!
+#
+# If you look at the benchmark data you can see that this is mostly caused by
+# the frequent high outliers - the median of the Rails-parser runs is still
+# overall smaller than the median of the JSON::Pure::Parser runs:
+#
+# Comparing times (call_time_median):
+# 1 ParserBenchmarkExt#parser 900 repeats:
+# 800.592479481 ( real) -> 26.936x
+# 0.001249075
+# 2 ParserBenchmarkYAML#parser 1000 repeats:
+# 271.002390644 ( real) -> 9.118x
+# 0.003690004
+# 3 ParserBenchmarkRails#parser 1000 repeats:
+# 30.227910865 ( real) -> 1.017x
+# 0.033082008
+# 4 ParserBenchmarkPure#parser 1000 repeats:
+# 29.722384421 ( real) -> 1.000x
+# 0.033644676
+# calls/sec ( time) -> speed covers
+# secs/call
+#
+# I have benchmarked the JSON-Generator as well. This generated a few more
+# values, because there are different modes that also influence the achieved
# speed:
#
-# * JSON::Pure::Generator:
-# generate:: 35.06 calls/second
-# pretty_generate:: 34.00 calls/second
-# fast_generate:: 41.06 calls/second
-#
-# * JSON::Ext::Generator:
-# generate:: 492.11 calls/second
-# pretty_generate:: 348.85 calls/second
-# fast_generate:: 541.60 calls/second
-#
-# * Speedup Ext/Pure:
-# generate safe:: 14.0 times
-# generate pretty:: 10.3 times
-# generate fast:: 13.2 times
-#
-# The rails framework includes a generator as well, also it seems to be rather
-# slow: I measured only 23.87 calls/second which is slower than any of my pure
-# generator results. Here a comparison of the different speedups with the Rails
-# measurement as the divisor:
-#
-# * Speedup Pure/Rails:
-# generate safe:: 1.5 times
-# generate pretty:: 1.4 times
-# generate fast:: 1.7 times
-#
-# * Speedup Ext/Rails:
-# generate safe:: 20.6 times
-# generate pretty:: 14.6 times
-# generate fast:: 22.7 times
-#
-# To achieve the fastest JSON text output, you can use the
-# fast_generate/fast_unparse methods. Beware, that this will disable the
-# checking for circular Ruby data structures, which may cause JSON to go into
-# an infinite loop.
+# Comparing times (call_time_mean):
+# 1 GeneratorBenchmarkExt#generator_fast 1000 repeats:
+# 547.354332608 ( real) -> 15.090x
+# 0.001826970
+# 2 GeneratorBenchmarkExt#generator_safe 1000 repeats:
+# 443.968212317 ( real) -> 12.240x
+# 0.002252414
+# 3 GeneratorBenchmarkExt#generator_pretty 900 repeats:
+# 375.104545883 ( real) -> 10.341x
+# 0.002665923
+# 4 GeneratorBenchmarkPure#generator_fast 1000 repeats:
+# 49.978706968 ( real) -> 1.378x
+# 0.020008521
+# 5 GeneratorBenchmarkRails#generator 1000 repeats:
+# 38.531868759 ( real) -> 1.062x
+# 0.025952543
+# 6 GeneratorBenchmarkPure#generator_safe 1000 repeats:
+# 36.927649925 ( real) -> 1.018x 7 (>=3859)
+# 0.027079979
+# 7 GeneratorBenchmarkPure#generator_pretty 1000 repeats:
+# 36.272134441 ( real) -> 1.000x 6 (>=3859)
+# 0.027569373
+# calls/sec ( time) -> speed covers
+# secs/call
+#
+# In the table above 1-3 are JSON::Ext::Generator methods. 4, 6, and 7 are
+# JSON::Pure::Generator methods and 5 is the Rails JSON generator. It is now a
+# bit faster than the generator_safe and generator_pretty methods of the pure
+# variant but slower than the others.
+#
+# To achieve the fastest JSON text output, you can use the fast_generate
+# method. Beware, that this will disable the checking for circular Ruby data
+# structures, which may cause JSON to go into an infinite loop.
+#
+# Here are the median comparisons for completeness' sake:
+#
+# Comparing times (call_time_median):
+# 1 GeneratorBenchmarkExt#generator_fast 1000 repeats:
+# 708.258020939 ( real) -> 16.547x
+# 0.001411915
+# 2 GeneratorBenchmarkExt#generator_safe 1000 repeats:
+# 569.105020353 ( real) -> 13.296x
+# 0.001757145
+# 3 GeneratorBenchmarkExt#generator_pretty 900 repeats:
+# 482.825371244 ( real) -> 11.280x
+# 0.002071142
+# 4 GeneratorBenchmarkPure#generator_fast 1000 repeats:
+# 62.717626652 ( real) -> 1.465x
+# 0.015944481
+# 5 GeneratorBenchmarkRails#generator 1000 repeats:
+# 43.965681162 ( real) -> 1.027x
+# 0.022745013
+# 6 GeneratorBenchmarkPure#generator_safe 1000 repeats:
+# 43.929073409 ( real) -> 1.026x 7 (>=3859)
+# 0.022763968
+# 7 GeneratorBenchmarkPure#generator_pretty 1000 repeats:
+# 42.802514491 ( real) -> 1.000x 6 (>=3859)
+# 0.023363113
+# calls/sec ( time) -> speed covers
+# secs/call
#
# == Examples
#
-# To create a JSON text from a ruby data structure, you
-# can call JSON.generate (or JSON.unparse) like that:
+# To create a JSON text from a ruby data structure, you can call JSON.generate
+# like that:
#
# json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
@@ -210,8 +274,8 @@ require 'json/common'
# }
# ]
#
-# There are also the methods Kernel#j for unparse, and Kernel#jj for
-# pretty_unparse output to the console, that work analogous to Core Ruby's p
+# There are also the methods Kernel#j for generate, and Kernel#jj for
+# pretty_generate output to the console, that work analogous to Core Ruby's p
# and the pp library's pp methods.
#
# The script tools/server.rb contains a small example if you want to test, how
@@ -230,6 +294,4 @@ module JSON
require 'json/pure'
end
end
-
- JSON_LOADED = true
end
diff --git a/ext/json/lib/json/add/core.rb b/ext/json/lib/json/add/core.rb
index 7121a77ff1..4423e7ad75 100644
--- a/ext/json/lib/json/add/core.rb
+++ b/ext/json/lib/json/add/core.rb
@@ -96,7 +96,7 @@ class Struct
def to_json(*args)
klass = self.class.name
- klass.nil? and raise JSON::JSONError, "Only named structs are supported!"
+ klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
{
'json_class' => klass,
'v' => values,
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
index 1bd0ad5fbd..1bd0ad5fbd 100644..100755
--- a/ext/json/lib/json/common.rb
+++ b/ext/json/lib/json/common.rb
diff --git a/ext/json/lib/json/editor.rb b/ext/json/lib/json/editor.rb
index ad86fdb15e..9e05f44b5b 100644..100755
--- a/ext/json/lib/json/editor.rb
+++ b/ext/json/lib/json/editor.rb
@@ -769,7 +769,12 @@ module JSON
iter.type, iter.content = 'FalseClass', 'false'
end
when 'Numeric'
- iter.content = (Integer(value) rescue Float(value) rescue 0).to_s
+ iter.content =
+ if value == 'Infinity'
+ value
+ else
+ (Integer(value) rescue Float(value) rescue 0).to_s
+ end
when 'String'
iter.content = value
when 'Hash', 'Array'
@@ -937,7 +942,11 @@ module JSON
type = types[type_input.active]
@content = case type
when 'Numeric'
- Integer(value_input.text) rescue Float(value_input.text) rescue 0
+ if (t = value_input.text) == 'Infinity'
+ 1 / 0.0
+ else
+ Integer(t) rescue Float(t) rescue 0
+ end
else
value_input.text
end.to_s
diff --git a/ext/json/lib/json/ext.rb b/ext/json/lib/json/ext.rb
index ff4fa42329..719e56025c 100644
--- a/ext/json/lib/json/ext.rb
+++ b/ext/json/lib/json/ext.rb
@@ -10,4 +10,6 @@ module JSON
JSON.parser = Parser
JSON.generator = Generator
end
+
+ JSON_LOADED = true
end
diff --git a/ext/json/lib/json/pure.rb b/ext/json/lib/json/pure.rb
deleted file mode 100644
index 6af8705c5b..0000000000
--- a/ext/json/lib/json/pure.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'json/common'
-require 'json/pure/parser'
-require 'json/pure/generator'
-
-module JSON
- # Swap consecutive bytes of _string_ in place.
- def self.swap!(string) # :nodoc:
- 0.upto(string.size / 2) do |i|
- break unless string[2 * i + 1]
- string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
- end
- string
- end
-
- # This module holds all the modules/classes that implement JSON's
- # functionality in pure ruby.
- module Pure
- $DEBUG and warn "Using pure library for JSON."
- JSON.parser = Parser
- JSON.generator = Generator
- end
-end
diff --git a/ext/json/lib/json/pure/generator.rb b/ext/json/lib/json/pure/generator.rb
deleted file mode 100644
index 4a9b05f545..0000000000
--- a/ext/json/lib/json/pure/generator.rb
+++ /dev/null
@@ -1,394 +0,0 @@
-module JSON
- MAP = {
- "\x0" => '\u0000',
- "\x1" => '\u0001',
- "\x2" => '\u0002',
- "\x3" => '\u0003',
- "\x4" => '\u0004',
- "\x5" => '\u0005',
- "\x6" => '\u0006',
- "\x7" => '\u0007',
- "\b" => '\b',
- "\t" => '\t',
- "\n" => '\n',
- "\xb" => '\u000b',
- "\f" => '\f',
- "\r" => '\r',
- "\xe" => '\u000e',
- "\xf" => '\u000f',
- "\x10" => '\u0010',
- "\x11" => '\u0011',
- "\x12" => '\u0012',
- "\x13" => '\u0013',
- "\x14" => '\u0014',
- "\x15" => '\u0015',
- "\x16" => '\u0016',
- "\x17" => '\u0017',
- "\x18" => '\u0018',
- "\x19" => '\u0019',
- "\x1a" => '\u001a',
- "\x1b" => '\u001b',
- "\x1c" => '\u001c',
- "\x1d" => '\u001d',
- "\x1e" => '\u001e',
- "\x1f" => '\u001f',
- '"' => '\"',
- '\\' => '\\\\',
- '/' => '\/',
- } # :nodoc:
-
- # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
- # UTF16 big endian characters as \u????, and return it.
- def utf8_to_json(string) # :nodoc:
- string = string.dup.force_encoding(Encoding::ASCII_8BIT)
- string.gsub!(/["\\\/\x0-\x1f]/) { MAP[$&] }
- string.gsub!(/(
- (?:
- [\xc2-\xdf][\x80-\xbf] |
- [\xe0-\xef][\x80-\xbf]{2} |
- [\xf0-\xf4][\x80-\xbf]{3}
- )+ |
- [\x80-\xc1\xf5-\xff] # invalid
- )/nx) { |c|
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
- c.unpack("U*").map{|c|
- c>0xFFFF ? ('\ud%03x\ud%03x'%[0x7C0+c/1024,0xC00+c%1024]) : ('\u%04x'%c)
- }.join("")
- }
- string
- end
- module_function :utf8_to_json
-
- module Pure
- module Generator
- # This class is used to create State instances, that are use to hold data
- # while generating a JSON text from a a Ruby data structure.
- class State
- # Creates a State object from _opts_, which ought to be Hash to create
- # a new State instance configured by _opts_, something else to create
- # an unconfigured instance. If _opts_ is a State object, it is just
- # returned.
- def self.from_state(opts)
- case opts
- when self
- opts
- when Hash
- new(opts)
- else
- new
- end
- end
-
- # Instantiates a new State object, configured by _opts_.
- #
- # _opts_ can have the following keys:
- #
- # * *indent*: a string used to indent levels (default: ''),
- # * *space*: a string that is put after, a : or , delimiter (default: ''),
- # * *space_before*: a string that is put before a : pair delimiter (default: ''),
- # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
- # * *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 (the default), false otherwise.
- # * *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.
- def initialize(opts = {})
- @seen = {}
- @indent = ''
- @space = ''
- @space_before = ''
- @object_nl = ''
- @array_nl = ''
- @check_circular = true
- @allow_nan = false
- configure opts
- end
-
- # This string is used to indent levels in the JSON text.
- attr_accessor :indent
-
- # This string is used to insert a space between the tokens in a JSON
- # string.
- attr_accessor :space
-
- # This string is used to insert a space before the ':' in JSON objects.
- attr_accessor :space_before
-
- # This string is put at the end of a line that holds a JSON object (or
- # Hash).
- attr_accessor :object_nl
-
- # This string is put at the end of a line that holds a JSON array.
- attr_accessor :array_nl
-
- # This integer returns the maximum level of data structure nesting in
- # the generated JSON, max_nesting = 0 if no maximum is checked.
- attr_accessor :max_nesting
-
- def check_max_nesting(depth) # :nodoc:
- return if @max_nesting.zero?
- current_nesting = depth + 1
- current_nesting > @max_nesting and
- raise NestingError, "nesting of #{current_nesting} is too deep"
- end
-
- # Returns true, if circular data structures should be checked,
- # otherwise returns false.
- def check_circular?
- @check_circular
- end
-
- # Returns true if NaN, Infinity, and -Infinity should be considered as
- # valid JSON and output.
- def allow_nan?
- @allow_nan
- end
-
- # Returns _true_, if _object_ was already seen during this generating
- # run.
- def seen?(object)
- @seen.key?(object.__id__)
- end
-
- # Remember _object_, to find out if it was already encountered (if a
- # cyclic data structure is if a cyclic data structure is rendered).
- def remember(object)
- @seen[object.__id__] = true
- end
-
- # Forget _object_ for this generating run.
- def forget(object)
- @seen.delete object.__id__
- end
-
- # Configure this State instance with the Hash _opts_, and return
- # itself.
- def configure(opts)
- @indent = opts[:indent] if opts.key?(:indent)
- @space = opts[:space] if opts.key?(:space)
- @space_before = opts[:space_before] if opts.key?(:space_before)
- @object_nl = opts[:object_nl] if opts.key?(:object_nl)
- @array_nl = opts[:array_nl] if opts.key?(:array_nl)
- @check_circular = !!opts[:check_circular] if opts.key?(:check_circular)
- @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
- if !opts.key?(:max_nesting) # defaults to 19
- @max_nesting = 19
- elsif opts[:max_nesting]
- @max_nesting = opts[:max_nesting]
- else
- @max_nesting = 0
- end
- self
- end
-
- # Returns the configuration instance variables as a hash, that can be
- # passed to the configure method.
- def to_h
- result = {}
- for iv in %w[indent space space_before object_nl array_nl check_circular allow_nan max_nesting]
- result[iv.intern] = instance_variable_get("@#{iv}")
- end
- result
- end
- end
-
- module GeneratorMethods
- module Object
- # Converts this object to a string (calling #to_s), converts
- # it to a JSON string, and returns the result. This is a fallback, if no
- # special method #to_json was defined for some object.
- def to_json(*) to_s.to_json end
- end
-
- module Hash
- # Returns a JSON string containing a JSON object, that is unparsed from
- # this Hash instance.
- # _state_ is a JSON::State object, that can also be used to configure the
- # produced JSON string output further.
- # _depth_ is used to find out nesting depth, to indent accordingly.
- def to_json(state = nil, depth = 0, *)
- if state
- state = JSON.state.from_state(state)
- state.check_max_nesting(depth)
- json_check_circular(state) { json_transform(state, depth) }
- else
- json_transform(state, depth)
- end
- end
-
- private
-
- def json_check_circular(state)
- if state and state.check_circular?
- state.seen?(self) and raise JSON::CircularDatastructure,
- "circular data structures not supported!"
- state.remember self
- end
- yield
- ensure
- state and state.forget self
- end
-
- def json_shift(state, depth)
- state and not state.object_nl.empty? or return ''
- state.indent * depth
- end
-
- def json_transform(state, depth)
- delim = ','
- delim << state.object_nl if state
- result = '{'
- result << state.object_nl if state
- result << map { |key,value|
- s = json_shift(state, depth + 1)
- s << key.to_s.to_json(state, depth + 1)
- s << state.space_before if state
- s << ':'
- s << state.space if state
- s << value.to_json(state, depth + 1)
- }.join(delim)
- result << state.object_nl if state
- result << json_shift(state, depth)
- result << '}'
- result
- end
- end
-
- module Array
- # Returns a JSON string containing a JSON array, that is unparsed from
- # this Array instance.
- # _state_ is a JSON::State object, that can also be used to configure the
- # produced JSON string output further.
- # _depth_ is used to find out nesting depth, to indent accordingly.
- def to_json(state = nil, depth = 0, *)
- if state
- state = JSON.state.from_state(state)
- state.check_max_nesting(depth)
- json_check_circular(state) { json_transform(state, depth) }
- else
- json_transform(state, depth)
- end
- end
-
- private
-
- def json_check_circular(state)
- if state and state.check_circular?
- state.seen?(self) and raise JSON::CircularDatastructure,
- "circular data structures not supported!"
- state.remember self
- end
- yield
- ensure
- state and state.forget self
- end
-
- def json_shift(state, depth)
- state and not state.array_nl.empty? or return ''
- state.indent * depth
- end
-
- def json_transform(state, depth)
- delim = ','
- delim << state.array_nl if state
- result = '['
- result << state.array_nl if state
- result << map { |value|
- json_shift(state, depth + 1) << value.to_json(state, depth + 1)
- }.join(delim)
- result << state.array_nl if state
- result << json_shift(state, depth)
- result << ']'
- result
- end
- end
-
- module Integer
- # Returns a JSON string representation for this Integer number.
- def to_json(*) to_s end
- end
-
- module Float
- # Returns a JSON string representation for this Float number.
- def to_json(state = nil, *)
- case
- when infinite?
- if !state || state.allow_nan?
- to_s
- else
- raise GeneratorError, "#{self} not allowed in JSON"
- end
- when nan?
- if !state || state.allow_nan?
- to_s
- else
- raise GeneratorError, "#{self} not allowed in JSON"
- end
- else
- to_s
- end
- end
- end
-
- module String
- # This string should be encoded with UTF-8 A call to this method
- # returns a JSON string encoded with UTF16 big endian characters as
- # \u????.
- def to_json(*)
- '"' << JSON.utf8_to_json(self) << '"'
- end
-
- # Module that holds the extinding methods if, the String module is
- # included.
- module Extend
- # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
- # key "raw"). The Ruby String can be created by this module method.
- def json_create(o)
- o['raw'].pack('C*')
- end
- end
-
- # Extends _modul_ with the String::Extend module.
- def self.included(modul)
- modul.extend Extend
- end
-
- # This method creates a raw object hash, that can be nested into
- # other data structures and will be unparsed as a raw string. This
- # method should be used, if you want to convert raw strings to JSON
- # instead of UTF-8 strings, e. g. binary data.
- def to_json_raw_object
- {
- JSON.create_id => self.class.name,
- 'raw' => self.unpack('C*'),
- }
- end
-
- # This method creates a JSON text from the result of
- # a call to to_json_raw_object of this String.
- def to_json_raw(*args)
- to_json_raw_object.to_json(*args)
- end
- end
-
- module TrueClass
- # Returns a JSON string for true: 'true'.
- def to_json(*) 'true' end
- end
-
- module FalseClass
- # Returns a JSON string for false: 'false'.
- def to_json(*) 'false' end
- end
-
- module NilClass
- # Returns a JSON string for nil: 'null'.
- def to_json(*) 'null' end
- end
- end
- end
- end
-end
diff --git a/ext/json/lib/json/pure/parser.rb b/ext/json/lib/json/pure/parser.rb
deleted file mode 100644
index f5ff574d11..0000000000
--- a/ext/json/lib/json/pure/parser.rb
+++ /dev/null
@@ -1,269 +0,0 @@
-require 'strscan'
-
-module JSON
- module Pure
- # This class implements the JSON parser that is used to parse a JSON string
- # into a Ruby data structure.
- class Parser < StringScanner
- STRING = /" ((?:[^\x0-\x1f"\\] |
- \\["\\\/bfnrt] |
- \\u[0-9a-fA-F]{4} |
- \\[\x20-\xff])*)
- "/nx
- INTEGER = /(-?0|-?[1-9]\d*)/
- FLOAT = /(-?
- (?:0|[1-9]\d*)
- (?:
- \.\d+(?i:e[+-]?\d+) |
- \.\d+ |
- (?i:e[+-]?\d+)
- )
- )/x
- NAN = /NaN/
- INFINITY = /Infinity/
- MINUS_INFINITY = /-Infinity/
- OBJECT_OPEN = /\{/
- OBJECT_CLOSE = /\}/
- ARRAY_OPEN = /\[/
- ARRAY_CLOSE = /\]/
- PAIR_DELIMITER = /:/
- COLLECTION_DELIMITER = /,/
- TRUE = /true/
- FALSE = /false/
- NULL = /null/
- IGNORE = %r(
- (?:
- //[^\n\r]*[\n\r]| # line comments
- /\* # c-style comments
- (?:
- [^*/]| # normal chars
- /[^*]| # slashes that do not start a nested comment
- \*[^/]| # asterisks that do not end this comment
- /(?=\*/) # single slash before this comment's end
- )*
- \*/ # the End of this comment
- |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
- )+
- )mx
-
- UNPARSED = Object.new
-
- # Creates a new JSON::Pure::Parser instance for the string _source_.
- #
- # It will be configured by the _opts_ hash. _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|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.
- # * *create_additions*: If set to false, the Parser doesn't create
- # additions even if a matchin class and create_id was found. This option
- # defaults to true.
- def initialize(source, opts = {})
- super
- if !opts.key?(:max_nesting) # defaults to 19
- @max_nesting = 19
- elsif opts[:max_nesting]
- @max_nesting = opts[:max_nesting]
- else
- @max_nesting = 0
- end
- @allow_nan = !!opts[:allow_nan]
- ca = true
- ca = opts[:create_additions] if opts.key?(:create_additions)
- @create_id = ca ? JSON.create_id : nil
- end
-
- alias source string
-
- # Parses the current JSON string _source_ and returns the complete data
- # structure as a result.
- def parse
- reset
- obj = nil
- until eos?
- case
- when scan(OBJECT_OPEN)
- obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
- @current_nesting = 1
- obj = parse_object
- when scan(ARRAY_OPEN)
- obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
- @current_nesting = 1
- obj = parse_array
- when skip(IGNORE)
- ;
- else
- raise ParserError, "source '#{peek(20)}' not in JSON!"
- end
- end
- obj or raise ParserError, "source did not contain any JSON!"
- obj
- end
-
- private
-
- # Unescape characters in strings.
- UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
- UNESCAPE_MAP.update({
- ?" => '"',
- ?\\ => '\\',
- ?/ => '/',
- ?b => "\b",
- ?f => "\f",
- ?n => "\n",
- ?r => "\r",
- ?t => "\t",
- ?u => nil,
- })
-
- def parse_string
- if scan(STRING)
- return '' if self[1].empty?
- self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
- if u = UNESCAPE_MAP[$&[1]]
- u
- else # \uXXXX
- res = []
- stack = nil
- [c.delete!('\\\\u')].pack("H*").unpack("n*").each do |c|
- case c
- when 0xD800..0xDBFF
- raise JSON::ParserError, "partial character in source" if stack
- stack = c
- when 0xDC00..0xDFFF
- raise JSON::ParserError,
- "partial character in source" unless (0xD800..0xDBFF).include?(stack)
- res << (stack << 10) - 0x35fdc00 + c
- stack = nil
- else
- raise JSON::ParserError, "partial character in source" if stack
- res << c
- end
- end
- raise JSON::ParserError, "partial character in source" if stack
- res.pack("U*")
- end
- end.force_encoding("UTF-8")
- else
- UNPARSED
- end
- end
-
- def parse_value
- case
- when scan(FLOAT)
- Float(self[1])
- when scan(INTEGER)
- Integer(self[1])
- when scan(TRUE)
- true
- when scan(FALSE)
- false
- when scan(NULL)
- nil
- when (string = parse_string) != UNPARSED
- string
- when scan(ARRAY_OPEN)
- @current_nesting += 1
- ary = parse_array
- @current_nesting -= 1
- ary
- when scan(OBJECT_OPEN)
- @current_nesting += 1
- obj = parse_object
- @current_nesting -= 1
- obj
- when @allow_nan && scan(NAN)
- NaN
- when @allow_nan && scan(INFINITY)
- Infinity
- when @allow_nan && scan(MINUS_INFINITY)
- MinusInfinity
- else
- UNPARSED
- end
- end
-
- def parse_array
- raise NestingError, "nesting of #@current_nesting is to deep" if
- @max_nesting.nonzero? && @current_nesting > @max_nesting
- result = []
- delim = false
- until eos?
- case
- when (value = parse_value) != UNPARSED
- delim = false
- result << value
- skip(IGNORE)
- if scan(COLLECTION_DELIMITER)
- delim = true
- elsif match?(ARRAY_CLOSE)
- ;
- else
- raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
- end
- when scan(ARRAY_CLOSE)
- if delim
- raise ParserError, "expected next element in array at '#{peek(20)}'!"
- end
- break
- when skip(IGNORE)
- ;
- else
- raise ParserError, "unexpected token in array at '#{peek(20)}'!"
- end
- end
- result
- end
-
- def parse_object
- raise NestingError, "nesting of #@current_nesting is to deep" if
- @max_nesting.nonzero? && @current_nesting > @max_nesting
- result = {}
- delim = false
- until eos?
- case
- when (string = parse_string) != UNPARSED
- skip(IGNORE)
- unless scan(PAIR_DELIMITER)
- raise ParserError, "expected ':' in object at '#{peek(20)}'!"
- end
- skip(IGNORE)
- unless (value = parse_value).equal? UNPARSED
- result[string] = value
- delim = false
- skip(IGNORE)
- if scan(COLLECTION_DELIMITER)
- delim = true
- elsif match?(OBJECT_CLOSE)
- ;
- else
- raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
- end
- else
- raise ParserError, "expected value in object at '#{peek(20)}'!"
- end
- when scan(OBJECT_CLOSE)
- if delim
- raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
- end
- if @create_id and klassname = result[@create_id]
- klass = JSON.deep_const_get klassname
- break unless klass and klass.json_creatable?
- result = klass.json_create(result)
- end
- break
- when skip(IGNORE)
- ;
- else
- raise ParserError, "unexpected token in object at '#{peek(20)}'!"
- end
- end
- result
- end
- end
- end
-end
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index acf8217048..e2764b0c6e 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,6 +1,6 @@
module JSON
# JSON version
- VERSION = '1.1.3'
+ VERSION = '1.1.4'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: