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:
parent
1735892962
commit
4f364c6bf7
@ -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>
|
Tue May 5 07:22:37 2009 NARUSE, Yui <naruse@ruby-lang.org>
|
||||||
|
|
||||||
* transcode.c: NOMAP is now multibyte direct map.
|
* transcode.c: NOMAP is now multibyte direct map.
|
||||||
|
@ -1,8 +1,33 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "ruby.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 "unicode.h"
|
||||||
#include <math.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 { \
|
#define check_max_nesting(state, depth) do { \
|
||||||
long current_nesting = 1 + depth; \
|
long current_nesting = 1 + depth; \
|
||||||
if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
|
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);
|
OBJ_INFECT(result, self);
|
||||||
|
FORCE_UTF8(result);
|
||||||
return 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);
|
result = mArray_json_transfrom(self, Vstate, Vdepth);
|
||||||
}
|
}
|
||||||
OBJ_INFECT(result, self);
|
OBJ_INFECT(result, self);
|
||||||
|
FORCE_UTF8(result);
|
||||||
return 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)
|
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)
|
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
||||||
{
|
{
|
||||||
JSON_Generator_State *state = NULL;
|
JSON_Generator_State *state = NULL;
|
||||||
VALUE Vstate, rest, tmp;
|
VALUE Vstate, rest, tmp, result;
|
||||||
double value = RFLOAT_VALUE(self);
|
double value = RFLOAT_VALUE(self);
|
||||||
rb_scan_args(argc, argv, "01*", &Vstate, &rest);
|
rb_scan_args(argc, argv, "01*", &Vstate, &rest);
|
||||||
if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
|
if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
|
||||||
if (isinf(value)) {
|
if (isinf(value)) {
|
||||||
if (!state || state->allow_nan) {
|
if (!state || state->allow_nan) {
|
||||||
return rb_funcall(self, i_to_s, 0);
|
result = rb_funcall(self, i_to_s, 0);
|
||||||
} else {
|
} else {
|
||||||
tmp = rb_funcall(self, i_to_s, 0);
|
tmp = rb_funcall(self, i_to_s, 0);
|
||||||
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
||||||
}
|
}
|
||||||
} else if (isnan(value)) {
|
} else if (isnan(value)) {
|
||||||
if (!state || state->allow_nan) {
|
if (!state || state->allow_nan) {
|
||||||
return rb_funcall(self, i_to_s, 0);
|
result = rb_funcall(self, i_to_s, 0);
|
||||||
} else {
|
} else {
|
||||||
tmp = rb_funcall(self, i_to_s, 0);
|
tmp = rb_funcall(self, i_to_s, 0);
|
||||||
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
* Extends _modul_ with the String::Extend module.
|
||||||
*/
|
*/
|
||||||
static VALUE mString_included_s(VALUE self, VALUE modul) {
|
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, "\"");
|
rb_str_buf_cat2(result, "\"");
|
||||||
JSON_convert_UTF8_to_JSON(result, self, strictConversion);
|
JSON_convert_UTF8_to_JSON(result, self, strictConversion);
|
||||||
rb_str_buf_cat2(result, "\"");
|
rb_str_buf_cat2(result, "\"");
|
||||||
|
FORCE_UTF8(result);
|
||||||
return 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)));
|
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*"));
|
ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
|
||||||
rb_hash_aset(result, rb_str_new2("raw"), ary);
|
rb_hash_aset(result, rb_str_new2("raw"), ary);
|
||||||
|
FORCE_UTF8(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,9 +388,11 @@ static VALUE mString_to_json_raw_object(VALUE self) {
|
|||||||
* to_json_raw_object of this String.
|
* to_json_raw_object of this String.
|
||||||
*/
|
*/
|
||||||
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
|
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);
|
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)
|
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)
|
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)
|
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)
|
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);
|
Check_Type(string, T_STRING);
|
||||||
return mString_to_json(argc, argv, string);
|
result = mString_to_json(argc, argv, string);
|
||||||
|
FORCE_UTF8(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,10 +1,30 @@
|
|||||||
|
|
||||||
#line 1 "parser.rl"
|
#line 1 "parser.rl"
|
||||||
#include "ruby.h"
|
#include "ruby.h"
|
||||||
#include "ruby/encoding.h"
|
|
||||||
#include "unicode.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
|
#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 mJSON, mExt, cParser, eParserError, eNestingError;
|
||||||
static VALUE CNaN, CInfinity, CMinusInfinity;
|
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; \
|
JSON_Parser *json; \
|
||||||
Data_Get_Struct(self, 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_start = 1;
|
||||||
static const int JSON_object_first_final = 27;
|
static const int JSON_object_first_final = 27;
|
||||||
static const int JSON_object_error = 0;
|
static const int JSON_object_error = 0;
|
||||||
|
|
||||||
static const int JSON_object_en_main = 1;
|
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)
|
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();
|
*result = rb_hash_new();
|
||||||
|
|
||||||
|
|
||||||
#line 66 "parser.c"
|
#line 87 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_object_start;
|
cs = JSON_object_start;
|
||||||
}
|
}
|
||||||
#line 111 "parser.rl"
|
|
||||||
|
#line 129 "parser.rl"
|
||||||
|
|
||||||
#line 72 "parser.c"
|
#line 94 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -95,7 +118,7 @@ case 2:
|
|||||||
goto st2;
|
goto st2;
|
||||||
goto st0;
|
goto st0;
|
||||||
tr2:
|
tr2:
|
||||||
#line 83 "parser.rl"
|
#line 101 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np = JSON_parse_string(json, p, pe, &last_name);
|
char *np = JSON_parse_string(json, p, pe, &last_name);
|
||||||
if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;}
|
if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;}
|
||||||
@ -105,7 +128,7 @@ st3:
|
|||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof3;
|
goto _test_eof3;
|
||||||
case 3:
|
case 3:
|
||||||
#line 110 "parser.c"
|
#line 132 "parser.c"
|
||||||
switch( (*p) ) {
|
switch( (*p) ) {
|
||||||
case 13: goto st3;
|
case 13: goto st3;
|
||||||
case 32: goto st3;
|
case 32: goto st3;
|
||||||
@ -172,7 +195,7 @@ case 8:
|
|||||||
goto st8;
|
goto st8;
|
||||||
goto st0;
|
goto st0;
|
||||||
tr11:
|
tr11:
|
||||||
#line 72 "parser.rl"
|
#line 90 "parser.rl"
|
||||||
{
|
{
|
||||||
VALUE v = Qnil;
|
VALUE v = Qnil;
|
||||||
char *np = JSON_parse_value(json, p, pe, &v);
|
char *np = JSON_parse_value(json, p, pe, &v);
|
||||||
@ -188,7 +211,7 @@ st9:
|
|||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof9;
|
goto _test_eof9;
|
||||||
case 9:
|
case 9:
|
||||||
#line 193 "parser.c"
|
#line 215 "parser.c"
|
||||||
switch( (*p) ) {
|
switch( (*p) ) {
|
||||||
case 13: goto st9;
|
case 13: goto st9;
|
||||||
case 32: goto st9;
|
case 32: goto st9;
|
||||||
@ -277,14 +300,14 @@ case 18:
|
|||||||
goto st9;
|
goto st9;
|
||||||
goto st18;
|
goto st18;
|
||||||
tr4:
|
tr4:
|
||||||
#line 88 "parser.rl"
|
#line 106 "parser.rl"
|
||||||
{ p--; {p++; cs = 27; goto _out;} }
|
{ p--; {p++; cs = 27; goto _out;} }
|
||||||
goto st27;
|
goto st27;
|
||||||
st27:
|
st27:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof27;
|
goto _test_eof27;
|
||||||
case 27:
|
case 27:
|
||||||
#line 289 "parser.c"
|
#line 311 "parser.c"
|
||||||
goto st0;
|
goto st0;
|
||||||
st19:
|
st19:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
@ -381,7 +404,8 @@ case 26:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 112 "parser.rl"
|
|
||||||
|
#line 130 "parser.rl"
|
||||||
|
|
||||||
if (cs >= JSON_object_first_final) {
|
if (cs >= JSON_object_first_final) {
|
||||||
if (RTEST(json->create_id)) {
|
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_start = 1;
|
||||||
static const int JSON_value_first_final = 21;
|
static const int JSON_value_first_final = 21;
|
||||||
static const int JSON_value_error = 0;
|
static const int JSON_value_error = 0;
|
||||||
|
|
||||||
static const int JSON_value_en_main = 1;
|
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)
|
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;
|
int cs = EVIL;
|
||||||
|
|
||||||
|
|
||||||
#line 420 "parser.c"
|
#line 444 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_value_start;
|
cs = JSON_value_start;
|
||||||
}
|
}
|
||||||
#line 217 "parser.rl"
|
|
||||||
|
#line 235 "parser.rl"
|
||||||
|
|
||||||
#line 426 "parser.c"
|
#line 451 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -446,14 +472,14 @@ st0:
|
|||||||
cs = 0;
|
cs = 0;
|
||||||
goto _out;
|
goto _out;
|
||||||
tr0:
|
tr0:
|
||||||
#line 158 "parser.rl"
|
#line 176 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np = JSON_parse_string(json, p, pe, result);
|
char *np = JSON_parse_string(json, p, pe, result);
|
||||||
if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
|
if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
|
||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr2:
|
tr2:
|
||||||
#line 163 "parser.rl"
|
#line 181 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np;
|
char *np;
|
||||||
if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
|
if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
|
||||||
@ -473,7 +499,7 @@ tr2:
|
|||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr5:
|
tr5:
|
||||||
#line 181 "parser.rl"
|
#line 199 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np;
|
char *np;
|
||||||
json->current_nesting++;
|
json->current_nesting++;
|
||||||
@ -483,7 +509,7 @@ tr5:
|
|||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr9:
|
tr9:
|
||||||
#line 189 "parser.rl"
|
#line 207 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np;
|
char *np;
|
||||||
json->current_nesting++;
|
json->current_nesting++;
|
||||||
@ -493,7 +519,7 @@ tr9:
|
|||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr16:
|
tr16:
|
||||||
#line 151 "parser.rl"
|
#line 169 "parser.rl"
|
||||||
{
|
{
|
||||||
if (json->allow_nan) {
|
if (json->allow_nan) {
|
||||||
*result = CInfinity;
|
*result = CInfinity;
|
||||||
@ -503,7 +529,7 @@ tr16:
|
|||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr18:
|
tr18:
|
||||||
#line 144 "parser.rl"
|
#line 162 "parser.rl"
|
||||||
{
|
{
|
||||||
if (json->allow_nan) {
|
if (json->allow_nan) {
|
||||||
*result = CNaN;
|
*result = CNaN;
|
||||||
@ -513,19 +539,19 @@ tr18:
|
|||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr22:
|
tr22:
|
||||||
#line 138 "parser.rl"
|
#line 156 "parser.rl"
|
||||||
{
|
{
|
||||||
*result = Qfalse;
|
*result = Qfalse;
|
||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr25:
|
tr25:
|
||||||
#line 135 "parser.rl"
|
#line 153 "parser.rl"
|
||||||
{
|
{
|
||||||
*result = Qnil;
|
*result = Qnil;
|
||||||
}
|
}
|
||||||
goto st21;
|
goto st21;
|
||||||
tr28:
|
tr28:
|
||||||
#line 141 "parser.rl"
|
#line 159 "parser.rl"
|
||||||
{
|
{
|
||||||
*result = Qtrue;
|
*result = Qtrue;
|
||||||
}
|
}
|
||||||
@ -534,9 +560,9 @@ st21:
|
|||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof21;
|
goto _test_eof21;
|
||||||
case 21:
|
case 21:
|
||||||
#line 197 "parser.rl"
|
#line 215 "parser.rl"
|
||||||
{ p--; {p++; cs = 21; goto _out;} }
|
{ p--; {p++; cs = 21; goto _out;} }
|
||||||
#line 541 "parser.c"
|
#line 566 "parser.c"
|
||||||
goto st0;
|
goto st0;
|
||||||
st2:
|
st2:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
@ -696,7 +722,8 @@ case 20:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 218 "parser.rl"
|
|
||||||
|
#line 236 "parser.rl"
|
||||||
|
|
||||||
if (cs >= JSON_value_first_final) {
|
if (cs >= JSON_value_first_final) {
|
||||||
return p;
|
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_start = 1;
|
||||||
static const int JSON_integer_first_final = 5;
|
static const int JSON_integer_first_final = 5;
|
||||||
static const int JSON_integer_error = 0;
|
static const int JSON_integer_error = 0;
|
||||||
|
|
||||||
static const int JSON_integer_en_main = 1;
|
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)
|
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;
|
int cs = EVIL;
|
||||||
|
|
||||||
|
|
||||||
#line 726 "parser.c"
|
#line 753 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_integer_start;
|
cs = JSON_integer_start;
|
||||||
}
|
}
|
||||||
#line 241 "parser.rl"
|
|
||||||
|
#line 259 "parser.rl"
|
||||||
json->memo = p;
|
json->memo = p;
|
||||||
|
|
||||||
#line 733 "parser.c"
|
#line 761 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -762,14 +791,14 @@ case 3:
|
|||||||
goto st0;
|
goto st0;
|
||||||
goto tr4;
|
goto tr4;
|
||||||
tr4:
|
tr4:
|
||||||
#line 231 "parser.rl"
|
#line 249 "parser.rl"
|
||||||
{ p--; {p++; cs = 5; goto _out;} }
|
{ p--; {p++; cs = 5; goto _out;} }
|
||||||
goto st5;
|
goto st5;
|
||||||
st5:
|
st5:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof5;
|
goto _test_eof5;
|
||||||
case 5:
|
case 5:
|
||||||
#line 774 "parser.c"
|
#line 802 "parser.c"
|
||||||
goto st0;
|
goto st0;
|
||||||
st4:
|
st4:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
@ -787,7 +816,8 @@ case 4:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 243 "parser.rl"
|
|
||||||
|
#line 261 "parser.rl"
|
||||||
|
|
||||||
if (cs >= JSON_integer_first_final) {
|
if (cs >= JSON_integer_first_final) {
|
||||||
long len = p - json->memo;
|
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_start = 1;
|
||||||
static const int JSON_float_first_final = 10;
|
static const int JSON_float_first_final = 10;
|
||||||
static const int JSON_float_error = 0;
|
static const int JSON_float_error = 0;
|
||||||
|
|
||||||
static const int JSON_float_en_main = 1;
|
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)
|
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;
|
int cs = EVIL;
|
||||||
|
|
||||||
|
|
||||||
#line 819 "parser.c"
|
#line 849 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_float_start;
|
cs = JSON_float_start;
|
||||||
}
|
}
|
||||||
#line 272 "parser.rl"
|
|
||||||
|
#line 290 "parser.rl"
|
||||||
json->memo = p;
|
json->memo = p;
|
||||||
|
|
||||||
#line 826 "parser.c"
|
#line 857 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -879,14 +911,14 @@ case 5:
|
|||||||
goto st0;
|
goto st0;
|
||||||
goto tr7;
|
goto tr7;
|
||||||
tr7:
|
tr7:
|
||||||
#line 259 "parser.rl"
|
#line 277 "parser.rl"
|
||||||
{ p--; {p++; cs = 10; goto _out;} }
|
{ p--; {p++; cs = 10; goto _out;} }
|
||||||
goto st10;
|
goto st10;
|
||||||
st10:
|
st10:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof10;
|
goto _test_eof10;
|
||||||
case 10:
|
case 10:
|
||||||
#line 891 "parser.c"
|
#line 922 "parser.c"
|
||||||
goto st0;
|
goto st0;
|
||||||
st6:
|
st6:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
@ -946,7 +978,8 @@ case 9:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 274 "parser.rl"
|
|
||||||
|
#line 292 "parser.rl"
|
||||||
|
|
||||||
if (cs >= JSON_float_first_final) {
|
if (cs >= JSON_float_first_final) {
|
||||||
long len = p - json->memo;
|
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_start = 1;
|
||||||
static const int JSON_array_first_final = 17;
|
static const int JSON_array_first_final = 17;
|
||||||
static const int JSON_array_error = 0;
|
static const int JSON_array_error = 0;
|
||||||
|
|
||||||
static const int JSON_array_en_main = 1;
|
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)
|
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();
|
*result = rb_ary_new();
|
||||||
|
|
||||||
|
|
||||||
#line 984 "parser.c"
|
#line 1017 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_array_start;
|
cs = JSON_array_start;
|
||||||
}
|
}
|
||||||
#line 322 "parser.rl"
|
|
||||||
|
#line 340 "parser.rl"
|
||||||
|
|
||||||
#line 990 "parser.c"
|
#line 1024 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -1024,7 +1059,7 @@ case 2:
|
|||||||
goto st2;
|
goto st2;
|
||||||
goto st0;
|
goto st0;
|
||||||
tr2:
|
tr2:
|
||||||
#line 291 "parser.rl"
|
#line 309 "parser.rl"
|
||||||
{
|
{
|
||||||
VALUE v = Qnil;
|
VALUE v = Qnil;
|
||||||
char *np = JSON_parse_value(json, p, pe, &v);
|
char *np = JSON_parse_value(json, p, pe, &v);
|
||||||
@ -1040,7 +1075,7 @@ st3:
|
|||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof3;
|
goto _test_eof3;
|
||||||
case 3:
|
case 3:
|
||||||
#line 1045 "parser.c"
|
#line 1079 "parser.c"
|
||||||
switch( (*p) ) {
|
switch( (*p) ) {
|
||||||
case 13: goto st3;
|
case 13: goto st3;
|
||||||
case 32: goto st3;
|
case 32: goto st3;
|
||||||
@ -1140,14 +1175,14 @@ case 12:
|
|||||||
goto st3;
|
goto st3;
|
||||||
goto st12;
|
goto st12;
|
||||||
tr4:
|
tr4:
|
||||||
#line 302 "parser.rl"
|
#line 320 "parser.rl"
|
||||||
{ p--; {p++; cs = 17; goto _out;} }
|
{ p--; {p++; cs = 17; goto _out;} }
|
||||||
goto st17;
|
goto st17;
|
||||||
st17:
|
st17:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof17;
|
goto _test_eof17;
|
||||||
case 17:
|
case 17:
|
||||||
#line 1152 "parser.c"
|
#line 1186 "parser.c"
|
||||||
goto st0;
|
goto st0;
|
||||||
st13:
|
st13:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
@ -1202,7 +1237,8 @@ case 16:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 323 "parser.rl"
|
|
||||||
|
#line 341 "parser.rl"
|
||||||
|
|
||||||
if(cs >= JSON_array_first_final) {
|
if(cs >= JSON_array_first_final) {
|
||||||
return p + 1;
|
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_start = 1;
|
||||||
static const int JSON_string_first_final = 8;
|
static const int JSON_string_first_final = 8;
|
||||||
static const int JSON_string_error = 0;
|
static const int JSON_string_error = 0;
|
||||||
|
|
||||||
static const int JSON_string_en_main = 1;
|
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)
|
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);
|
*result = rb_str_new("", 0);
|
||||||
|
|
||||||
#line 1289 "parser.c"
|
#line 1325 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_string_start;
|
cs = JSON_string_start;
|
||||||
}
|
}
|
||||||
#line 409 "parser.rl"
|
|
||||||
|
#line 433 "parser.rl"
|
||||||
json->memo = p;
|
json->memo = p;
|
||||||
|
|
||||||
#line 1296 "parser.c"
|
#line 1333 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -1316,19 +1354,25 @@ case 2:
|
|||||||
goto st0;
|
goto st0;
|
||||||
goto st2;
|
goto st2;
|
||||||
tr2:
|
tr2:
|
||||||
#line 393 "parser.rl"
|
#line 411 "parser.rl"
|
||||||
{
|
{
|
||||||
*result = json_string_unescape(json->memo + 1, p);
|
*result = json_string_unescape(json->memo + 1, p);
|
||||||
if (NIL_P(*result)) { p--; {p++; cs = 8; goto _out;} } else {p = (( p + 1))-1;}
|
if (NIL_P(*result)) {
|
||||||
}
|
p--;
|
||||||
#line 398 "parser.rl"
|
{p++; cs = 8; goto _out;}
|
||||||
|
} else {
|
||||||
|
FORCE_UTF8(*result);
|
||||||
|
{p = (( p + 1))-1;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#line 422 "parser.rl"
|
||||||
{ p--; {p++; cs = 8; goto _out;} }
|
{ p--; {p++; cs = 8; goto _out;} }
|
||||||
goto st8;
|
goto st8;
|
||||||
st8:
|
st8:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof8;
|
goto _test_eof8;
|
||||||
case 8:
|
case 8:
|
||||||
#line 1333 "parser.c"
|
#line 1376 "parser.c"
|
||||||
goto st0;
|
goto st0;
|
||||||
st3:
|
st3:
|
||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
@ -1403,11 +1447,11 @@ case 7:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 411 "parser.rl"
|
|
||||||
|
#line 435 "parser.rl"
|
||||||
|
|
||||||
if (cs >= JSON_string_first_final) {
|
if (cs >= JSON_string_first_final) {
|
||||||
rb_enc_associate(*result, rb_utf8_encoding());
|
return p + 1;
|
||||||
return p + 1;
|
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
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_start = 1;
|
||||||
static const int JSON_first_final = 10;
|
static const int JSON_first_final = 10;
|
||||||
static const int JSON_error = 0;
|
static const int JSON_error = 0;
|
||||||
|
|
||||||
static const int JSON_en_main = 1;
|
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;
|
GET_STRUCT;
|
||||||
|
|
||||||
|
|
||||||
#line 1549 "parser.c"
|
#line 1594 "parser.c"
|
||||||
{
|
{
|
||||||
cs = JSON_start;
|
cs = JSON_start;
|
||||||
}
|
}
|
||||||
#line 567 "parser.rl"
|
|
||||||
|
#line 591 "parser.rl"
|
||||||
p = json->source;
|
p = json->source;
|
||||||
pe = p + json->len;
|
pe = p + json->len;
|
||||||
|
|
||||||
#line 1557 "parser.c"
|
#line 1603 "parser.c"
|
||||||
{
|
{
|
||||||
if ( p == pe )
|
if ( p == pe )
|
||||||
goto _test_eof;
|
goto _test_eof;
|
||||||
@ -1609,7 +1655,7 @@ case 5:
|
|||||||
goto st1;
|
goto st1;
|
||||||
goto st5;
|
goto st5;
|
||||||
tr3:
|
tr3:
|
||||||
#line 434 "parser.rl"
|
#line 458 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np;
|
char *np;
|
||||||
json->current_nesting = 1;
|
json->current_nesting = 1;
|
||||||
@ -1618,7 +1664,7 @@ tr3:
|
|||||||
}
|
}
|
||||||
goto st10;
|
goto st10;
|
||||||
tr4:
|
tr4:
|
||||||
#line 427 "parser.rl"
|
#line 451 "parser.rl"
|
||||||
{
|
{
|
||||||
char *np;
|
char *np;
|
||||||
json->current_nesting = 1;
|
json->current_nesting = 1;
|
||||||
@ -1630,7 +1676,7 @@ st10:
|
|||||||
if ( ++p == pe )
|
if ( ++p == pe )
|
||||||
goto _test_eof10;
|
goto _test_eof10;
|
||||||
case 10:
|
case 10:
|
||||||
#line 1634 "parser.c"
|
#line 1680 "parser.c"
|
||||||
switch( (*p) ) {
|
switch( (*p) ) {
|
||||||
case 13: goto st10;
|
case 13: goto st10;
|
||||||
case 32: goto st10;
|
case 32: goto st10;
|
||||||
@ -1686,7 +1732,8 @@ case 9:
|
|||||||
_test_eof: {}
|
_test_eof: {}
|
||||||
_out: {}
|
_out: {}
|
||||||
}
|
}
|
||||||
#line 570 "parser.rl"
|
|
||||||
|
#line 594 "parser.rl"
|
||||||
|
|
||||||
if (cs >= JSON_first_final && p == pe) {
|
if (cs >= JSON_first_final && p == pe) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,8 +1,28 @@
|
|||||||
#include "ruby.h"
|
#include "ruby.h"
|
||||||
#include "unicode.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
|
#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 mJSON, mExt, cParser, eParserError, eNestingError;
|
||||||
static VALUE CNaN, CInfinity, CMinusInfinity;
|
static VALUE CNaN, CInfinity, CMinusInfinity;
|
||||||
|
|
||||||
@ -390,8 +410,14 @@ static VALUE json_string_unescape(char *p, char *pe)
|
|||||||
|
|
||||||
action parse_string {
|
action parse_string {
|
||||||
*result = json_string_unescape(json->memo + 1, p);
|
*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; }
|
action exit { fhold; fbreak; }
|
||||||
|
|
||||||
|
148
ext/json/lib/json.rb
Normal file → Executable file
148
ext/json/lib/json.rb
Normal file → Executable file
@ -77,57 +77,121 @@ require 'json/common'
|
|||||||
#
|
#
|
||||||
# == Speed Comparisons
|
# == Speed Comparisons
|
||||||
#
|
#
|
||||||
# I have created some benchmark results (see the benchmarks subdir of the
|
# I have created some benchmark results (see the benchmarks/data-p4-3Ghz
|
||||||
# package) for the JSON-Parser to estimate the speed up in the C extension:
|
# subdir of the package) for the JSON-parser to estimate the speed up in the C
|
||||||
|
# extension:
|
||||||
#
|
#
|
||||||
# JSON::Pure::Parser:: 28.90 calls/second
|
# Comparing times (call_time_mean):
|
||||||
# JSON::Ext::Parser:: 505.50 calls/second
|
# 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
|
# If you look at the benchmark data you can see that this is mostly caused by
|
||||||
# values, because there are different modes, that also influence the achieved
|
# 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:
|
# speed:
|
||||||
#
|
#
|
||||||
# * JSON::Pure::Generator:
|
# Comparing times (call_time_mean):
|
||||||
# generate:: 35.06 calls/second
|
# 1 GeneratorBenchmarkExt#generator_fast 1000 repeats:
|
||||||
# pretty_generate:: 34.00 calls/second
|
# 547.354332608 ( real) -> 15.090x
|
||||||
# fast_generate:: 41.06 calls/second
|
# 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:
|
# In the table above 1-3 are JSON::Ext::Generator methods. 4, 6, and 7 are
|
||||||
# generate:: 492.11 calls/second
|
# JSON::Pure::Generator methods and 5 is the Rails JSON generator. It is now a
|
||||||
# pretty_generate:: 348.85 calls/second
|
# bit faster than the generator_safe and generator_pretty methods of the pure
|
||||||
# fast_generate:: 541.60 calls/second
|
# variant but slower than the others.
|
||||||
#
|
#
|
||||||
# * Speedup Ext/Pure:
|
# To achieve the fastest JSON text output, you can use the fast_generate
|
||||||
# generate safe:: 14.0 times
|
# method. Beware, that this will disable the checking for circular Ruby data
|
||||||
# generate pretty:: 10.3 times
|
# structures, which may cause JSON to go into an infinite loop.
|
||||||
# generate fast:: 13.2 times
|
|
||||||
#
|
#
|
||||||
# The rails framework includes a generator as well, also it seems to be rather
|
# Here are the median comparisons for completeness' sake:
|
||||||
# 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:
|
# Comparing times (call_time_median):
|
||||||
# generate safe:: 1.5 times
|
# 1 GeneratorBenchmarkExt#generator_fast 1000 repeats:
|
||||||
# generate pretty:: 1.4 times
|
# 708.258020939 ( real) -> 16.547x
|
||||||
# generate fast:: 1.7 times
|
# 0.001411915
|
||||||
#
|
# 2 GeneratorBenchmarkExt#generator_safe 1000 repeats:
|
||||||
# * Speedup Ext/Rails:
|
# 569.105020353 ( real) -> 13.296x
|
||||||
# generate safe:: 20.6 times
|
# 0.001757145
|
||||||
# generate pretty:: 14.6 times
|
# 3 GeneratorBenchmarkExt#generator_pretty 900 repeats:
|
||||||
# generate fast:: 22.7 times
|
# 482.825371244 ( real) -> 11.280x
|
||||||
#
|
# 0.002071142
|
||||||
# To achieve the fastest JSON text output, you can use the
|
# 4 GeneratorBenchmarkPure#generator_fast 1000 repeats:
|
||||||
# fast_generate/fast_unparse methods. Beware, that this will disable the
|
# 62.717626652 ( real) -> 1.465x
|
||||||
# checking for circular Ruby data structures, which may cause JSON to go into
|
# 0.015944481
|
||||||
# an infinite loop.
|
# 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
|
# == Examples
|
||||||
#
|
#
|
||||||
# To create a JSON text from a ruby data structure, you
|
# To create a JSON text from a ruby data structure, you can call JSON.generate
|
||||||
# can call JSON.generate (or JSON.unparse) like that:
|
# like that:
|
||||||
#
|
#
|
||||||
# json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
# json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
||||||
# # => "[1,2,{\"a\":3.141},false,true,null,\"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
|
# There are also the methods Kernel#j for generate, and Kernel#jj for
|
||||||
# pretty_unparse output to the console, that work analogous to Core Ruby's p
|
# pretty_generate output to the console, that work analogous to Core Ruby's p
|
||||||
# and the pp library's pp methods.
|
# and the pp library's pp methods.
|
||||||
#
|
#
|
||||||
# The script tools/server.rb contains a small example if you want to test, how
|
# The script tools/server.rb contains a small example if you want to test, how
|
||||||
@ -230,6 +294,4 @@ module JSON
|
|||||||
require 'json/pure'
|
require 'json/pure'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
JSON_LOADED = true
|
|
||||||
end
|
end
|
||||||
|
@ -96,7 +96,7 @@ class Struct
|
|||||||
|
|
||||||
def to_json(*args)
|
def to_json(*args)
|
||||||
klass = self.class.name
|
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,
|
'json_class' => klass,
|
||||||
'v' => values,
|
'v' => values,
|
||||||
|
0
ext/json/lib/json/common.rb
Normal file → Executable file
0
ext/json/lib/json/common.rb
Normal file → Executable file
13
ext/json/lib/json/editor.rb
Normal file → Executable file
13
ext/json/lib/json/editor.rb
Normal file → Executable file
@ -769,7 +769,12 @@ module JSON
|
|||||||
iter.type, iter.content = 'FalseClass', 'false'
|
iter.type, iter.content = 'FalseClass', 'false'
|
||||||
end
|
end
|
||||||
when 'Numeric'
|
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'
|
when 'String'
|
||||||
iter.content = value
|
iter.content = value
|
||||||
when 'Hash', 'Array'
|
when 'Hash', 'Array'
|
||||||
@ -937,7 +942,11 @@ module JSON
|
|||||||
type = types[type_input.active]
|
type = types[type_input.active]
|
||||||
@content = case type
|
@content = case type
|
||||||
when 'Numeric'
|
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
|
else
|
||||||
value_input.text
|
value_input.text
|
||||||
end.to_s
|
end.to_s
|
||||||
|
@ -10,4 +10,6 @@ module JSON
|
|||||||
JSON.parser = Parser
|
JSON.parser = Parser
|
||||||
JSON.generator = Generator
|
JSON.generator = Generator
|
||||||
end
|
end
|
||||||
|
|
||||||
|
JSON_LOADED = true
|
||||||
end
|
end
|
||||||
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||||||
module JSON
|
module JSON
|
||||||
# JSON version
|
# JSON version
|
||||||
VERSION = '1.1.3'
|
VERSION = '1.1.4'
|
||||||
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
|
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
|
||||||
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
||||||
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user