1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Update to JSON 1.1.4.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@23346 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2009-05-05 02:32:49 +00:00
parent 1735892962
commit 4f364c6bf7
13 changed files with 336 additions and 826 deletions

View file

@ -1,3 +1,7 @@
Tue May 5 10:42:28 2009 NARUSE, Yui <naruse@ruby-lang.org>
* ext/json: Update to JSON 1.1.4.
Tue May 5 07:22:37 2009 NARUSE, Yui <naruse@ruby-lang.org>
* transcode.c: NOMAP is now multibyte direct map.

View file

@ -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;
}
/*

View file

@ -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;

View file

@ -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; }

148
ext/json/lib/json.rb Normal file → Executable file
View file

@ -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:
# 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:
#
# JSON::Pure::Parser:: 28.90 calls/second
# JSON::Ext::Parser:: 505.50 calls/second
# 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
#
# This is ca. <b>17.5</b> times the speed of the pure Ruby implementation.
# 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!
#
# I have benchmarked the JSON-Generator as well. This generates a few more
# values, because there are different modes, that also influence the achieved
# 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
# 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
#
# * JSON::Ext::Generator:
# generate:: 492.11 calls/second
# pretty_generate:: 348.85 calls/second
# fast_generate:: 541.60 calls/second
# 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.
#
# * Speedup Ext/Pure:
# generate safe:: 14.0 times
# generate pretty:: 10.3 times
# generate fast:: 13.2 times
# 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.
#
# 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:
# Here are the median comparisons for completeness' sake:
#
# * 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_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

View file

@ -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,

0
ext/json/lib/json/common.rb Normal file → Executable file
View file

13
ext/json/lib/json/editor.rb Normal file → Executable file
View file

@ -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

View file

@ -10,4 +10,6 @@ module JSON
JSON.parser = Parser
JSON.generator = Generator
end
JSON_LOADED = true
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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: