mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/json: Update to JSON 1.4.1.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27493 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
9372cdb80a
commit
54592ad627
26 changed files with 2434 additions and 1814 deletions
|
@ -1,3 +1,7 @@
|
|||
Mon Apr 26 09:05:30 2010 NARUSE, Yui <naruse@ruby-lang.org>
|
||||
|
||||
* ext/json: Update to JSON 1.4.1.
|
||||
|
||||
Sun Apr 25 23:50:08 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* io.c (ruby_dup): update max fd.
|
||||
|
@ -156,7 +160,7 @@ Thu Apr 22 16:43:31 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
|||
|
||||
* parse.y (parser_yylex): skip whitespaces after method name.
|
||||
|
||||
Wed Apr 22 02:16:00 2010 Kenta Murata <mrkn@mrkn.jp>
|
||||
Wed Apr 22 02:16:00 2010 Kenta Murata <mrkn@mrkn.jp>
|
||||
|
||||
* ext/bigdecimal/bigdecimal.[ch]: replace _DEBUG with BIGDECIMAL_DEBUG
|
||||
to prevent activating debugging code when compiled with -DEBUG flag
|
||||
|
@ -1619,7 +1623,7 @@ Thu Mar 18 21:24:21 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
|||
Thu Mar 18 19:52:27 2010 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* tool/transcode-tblgen.rb: scan singleton mappings sequentially.
|
||||
|
||||
|
||||
Thu Mar 18 06:28:32 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* configure.in (RUBY_EXEC_PREFIX): added to config.h.
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
require 'mkmf'
|
||||
require 'rbconfig'
|
||||
|
||||
if CONFIG['GCC'] == 'yes'
|
||||
$CFLAGS += ' -Wall'
|
||||
#$CFLAGS += ' -O0 -ggdb'
|
||||
end
|
||||
|
||||
create_makefile 'json/ext/generator'
|
|
@ -1,919 +0,0 @@
|
|||
#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) \
|
||||
rb_raise(eNestingError, "nesting of %ld is too deep", current_nesting); \
|
||||
} while (0);
|
||||
|
||||
static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
|
||||
mHash, mArray, mInteger, mFloat, mString, mString_Extend,
|
||||
mTrueClass, mFalseClass, mNilClass, eGeneratorError,
|
||||
eCircularDatastructure, eNestingError;
|
||||
|
||||
static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
|
||||
i_object_nl, i_array_nl, i_check_circular, i_max_nesting,
|
||||
i_allow_nan, i_pack, i_unpack, i_create_id, i_extend;
|
||||
|
||||
typedef struct JSON_Generator_StateStruct {
|
||||
VALUE indent;
|
||||
VALUE space;
|
||||
VALUE space_before;
|
||||
VALUE object_nl;
|
||||
VALUE array_nl;
|
||||
int check_circular;
|
||||
VALUE seen;
|
||||
VALUE memo;
|
||||
VALUE depth;
|
||||
long max_nesting;
|
||||
int flag;
|
||||
int allow_nan;
|
||||
} JSON_Generator_State;
|
||||
|
||||
#define GET_STATE(self) \
|
||||
JSON_Generator_State *state; \
|
||||
Data_Get_Struct(self, JSON_Generator_State, state);
|
||||
|
||||
/*
|
||||
* Document-module: JSON::Ext::Generator
|
||||
*
|
||||
* This is the JSON generator implemented as a C extension. It can be
|
||||
* configured to be used by setting
|
||||
*
|
||||
* JSON.generator = JSON::Ext::Generator
|
||||
*
|
||||
* with the method generator= in JSON.
|
||||
*
|
||||
*/
|
||||
|
||||
static int hash_to_json_state_i(VALUE key, VALUE value, VALUE Vstate)
|
||||
{
|
||||
VALUE json, buf, Vdepth;
|
||||
GET_STATE(Vstate);
|
||||
buf = state->memo;
|
||||
Vdepth = state->depth;
|
||||
|
||||
if (key == Qundef) return ST_CONTINUE;
|
||||
if (state->flag) {
|
||||
state->flag = 0;
|
||||
rb_str_buf_cat2(buf, ",");
|
||||
if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(buf, state->object_nl);
|
||||
}
|
||||
if (RSTRING_LEN(state->object_nl)) {
|
||||
rb_str_buf_append(buf, rb_str_times(state->indent, Vdepth));
|
||||
}
|
||||
json = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 2, Vstate, Vdepth);
|
||||
Check_Type(json, T_STRING);
|
||||
rb_str_buf_append(buf, json);
|
||||
OBJ_INFECT(buf, json);
|
||||
if (RSTRING_LEN(state->space_before)) {
|
||||
rb_str_buf_append(buf, state->space_before);
|
||||
}
|
||||
rb_str_buf_cat2(buf, ":");
|
||||
if (RSTRING_LEN(state->space)) rb_str_buf_append(buf, state->space);
|
||||
json = rb_funcall(value, i_to_json, 2, Vstate, Vdepth);
|
||||
Check_Type(json, T_STRING);
|
||||
state->flag = 1;
|
||||
rb_str_buf_append(buf, json);
|
||||
OBJ_INFECT(buf, json);
|
||||
state->depth = Vdepth;
|
||||
state->memo = buf;
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
inline static VALUE mHash_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
|
||||
long depth, len = RHASH_SIZE(self);
|
||||
VALUE result;
|
||||
GET_STATE(Vstate);
|
||||
|
||||
depth = 1 + FIX2LONG(Vdepth);
|
||||
result = rb_str_buf_new(len);
|
||||
state->memo = result;
|
||||
state->depth = LONG2FIX(depth);
|
||||
state->flag = 0;
|
||||
rb_str_buf_cat2(result, "{");
|
||||
if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
|
||||
rb_hash_foreach(self, hash_to_json_state_i, Vstate);
|
||||
if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
|
||||
if (RSTRING_LEN(state->object_nl)) {
|
||||
rb_str_buf_append(result, rb_str_times(state->indent, Vdepth));
|
||||
}
|
||||
rb_str_buf_cat2(result, "}");
|
||||
return result;
|
||||
}
|
||||
|
||||
static int hash_to_json_i(VALUE key, VALUE value, VALUE buf)
|
||||
{
|
||||
VALUE tmp;
|
||||
|
||||
if (key == Qundef) return ST_CONTINUE;
|
||||
if (RSTRING_LEN(buf) > 1) rb_str_buf_cat2(buf, ",");
|
||||
tmp = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 0);
|
||||
Check_Type(tmp, T_STRING);
|
||||
rb_str_buf_append(buf, tmp);
|
||||
OBJ_INFECT(buf, tmp);
|
||||
rb_str_buf_cat2(buf, ":");
|
||||
tmp = rb_funcall(value, i_to_json, 0);
|
||||
Check_Type(tmp, T_STRING);
|
||||
rb_str_buf_append(buf, tmp);
|
||||
OBJ_INFECT(buf, tmp);
|
||||
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(state = nil, depth = 0)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE Vstate, Vdepth, result;
|
||||
long depth;
|
||||
|
||||
rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
|
||||
depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
|
||||
if (NIL_P(Vstate)) {
|
||||
long len = RHASH_SIZE(self);
|
||||
result = rb_str_buf_new(len);
|
||||
rb_str_buf_cat2(result, "{");
|
||||
rb_hash_foreach(self, hash_to_json_i, result);
|
||||
rb_str_buf_cat2(result, "}");
|
||||
} else {
|
||||
GET_STATE(Vstate);
|
||||
check_max_nesting(state, depth);
|
||||
if (state->check_circular) {
|
||||
VALUE self_id = rb_obj_id(self);
|
||||
if (RTEST(rb_hash_aref(state->seen, self_id))) {
|
||||
rb_raise(eCircularDatastructure,
|
||||
"circular data structures not supported!");
|
||||
}
|
||||
rb_hash_aset(state->seen, self_id, Qtrue);
|
||||
result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
|
||||
rb_hash_delete(state->seen, self_id);
|
||||
} else {
|
||||
result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
|
||||
}
|
||||
}
|
||||
OBJ_INFECT(result, self);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
|
||||
long i, len = RARRAY_LEN(self);
|
||||
VALUE shift, result;
|
||||
long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
|
||||
VALUE delim = rb_str_new2(",");
|
||||
GET_STATE(Vstate);
|
||||
|
||||
check_max_nesting(state, depth);
|
||||
if (state->check_circular) {
|
||||
VALUE self_id = rb_obj_id(self);
|
||||
rb_hash_aset(state->seen, self_id, Qtrue);
|
||||
result = rb_str_buf_new(len);
|
||||
if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
|
||||
shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
|
||||
|
||||
rb_str_buf_cat2(result, "[");
|
||||
OBJ_INFECT(result, self);
|
||||
rb_str_buf_append(result, state->array_nl);
|
||||
for (i = 0; i < len; i++) {
|
||||
VALUE element = RARRAY_PTR(self)[i];
|
||||
if (RTEST(rb_hash_aref(state->seen, rb_obj_id(element)))) {
|
||||
rb_raise(eCircularDatastructure,
|
||||
"circular data structures not supported!");
|
||||
}
|
||||
OBJ_INFECT(result, element);
|
||||
if (i > 0) rb_str_buf_append(result, delim);
|
||||
rb_str_buf_append(result, shift);
|
||||
element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
|
||||
Check_Type(element, T_STRING);
|
||||
rb_str_buf_append(result, element);
|
||||
}
|
||||
if (RSTRING_LEN(state->array_nl)) {
|
||||
rb_str_buf_append(result, state->array_nl);
|
||||
rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
|
||||
}
|
||||
rb_str_buf_cat2(result, "]");
|
||||
rb_hash_delete(state->seen, self_id);
|
||||
} else {
|
||||
result = rb_str_buf_new(len);
|
||||
OBJ_INFECT(result, self);
|
||||
if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
|
||||
shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
|
||||
|
||||
rb_str_buf_cat2(result, "[");
|
||||
rb_str_buf_append(result, state->array_nl);
|
||||
for (i = 0; i < len; i++) {
|
||||
VALUE element = RARRAY_PTR(self)[i];
|
||||
OBJ_INFECT(result, element);
|
||||
if (i > 0) rb_str_buf_append(result, delim);
|
||||
rb_str_buf_append(result, shift);
|
||||
element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
|
||||
Check_Type(element, T_STRING);
|
||||
rb_str_buf_append(result, element);
|
||||
}
|
||||
rb_str_buf_append(result, state->array_nl);
|
||||
if (RSTRING_LEN(state->array_nl)) {
|
||||
rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
|
||||
}
|
||||
rb_str_buf_cat2(result, "]");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(state = nil, depth = 0)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
|
||||
VALUE Vstate, Vdepth, result;
|
||||
|
||||
rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
|
||||
if (NIL_P(Vstate)) {
|
||||
long i, len = RARRAY_LEN(self);
|
||||
result = rb_str_buf_new(2 + 2 * len);
|
||||
rb_str_buf_cat2(result, "[");
|
||||
OBJ_INFECT(result, self);
|
||||
for (i = 0; i < len; i++) {
|
||||
VALUE element = RARRAY_PTR(self)[i];
|
||||
OBJ_INFECT(result, element);
|
||||
if (i > 0) rb_str_buf_cat2(result, ",");
|
||||
element = rb_funcall(element, i_to_json, 0);
|
||||
Check_Type(element, T_STRING);
|
||||
rb_str_buf_append(result, element);
|
||||
}
|
||||
rb_str_buf_cat2(result, "]");
|
||||
} else {
|
||||
result = mArray_json_transfrom(self, Vstate, Vdepth);
|
||||
}
|
||||
OBJ_INFECT(result, self);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
* Returns a JSON string representation for this Integer number.
|
||||
*/
|
||||
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE result = rb_funcall(self, i_to_s, 0);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
* Returns a JSON string representation for this Float number.
|
||||
*/
|
||||
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
JSON_Generator_State *state = NULL;
|
||||
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) {
|
||||
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) {
|
||||
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 {
|
||||
result = rb_funcall(self, i_to_s, 0);
|
||||
}
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: String.included(modul)
|
||||
*
|
||||
* Extends _modul_ with the String::Extend module.
|
||||
*/
|
||||
static VALUE mString_included_s(VALUE self, VALUE modul) {
|
||||
VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
* 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????.
|
||||
*/
|
||||
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE result = rb_str_buf_new(RSTRING_LEN(self));
|
||||
rb_str_buf_cat2(result, "\"");
|
||||
JSON_convert_UTF8_to_JSON(result, self, strictConversion);
|
||||
rb_str_buf_cat2(result, "\"");
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json_raw_object()
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static VALUE mString_to_json_raw_object(VALUE self) {
|
||||
VALUE ary;
|
||||
VALUE result = rb_hash_new();
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json_raw(*args)
|
||||
*
|
||||
* This method creates a JSON text from the result of a call to
|
||||
* to_json_raw_object of this String.
|
||||
*/
|
||||
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
|
||||
VALUE result, obj = mString_to_json_raw_object(self);
|
||||
Check_Type(obj, T_HASH);
|
||||
result = mHash_to_json(argc, argv, obj);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: json_create(o)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
|
||||
VALUE ary;
|
||||
Check_Type(o, T_HASH);
|
||||
ary = rb_hash_aref(o, rb_str_new2("raw"));
|
||||
return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
* Returns a JSON string for true: 'true'.
|
||||
*/
|
||||
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE result = rb_str_new2("true");
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
* Returns a JSON string for false: 'false'.
|
||||
*/
|
||||
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE result = rb_str_new2("false");
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
*/
|
||||
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE result = rb_str_new2("null");
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_json(*)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE result, string = rb_funcall(self, i_to_s, 0);
|
||||
Check_Type(string, T_STRING);
|
||||
result = mString_to_json(argc, argv, string);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-class: JSON::Ext::Generator::State
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
static void State_mark(JSON_Generator_State *state)
|
||||
{
|
||||
rb_gc_mark_maybe(state->indent);
|
||||
rb_gc_mark_maybe(state->space);
|
||||
rb_gc_mark_maybe(state->space_before);
|
||||
rb_gc_mark_maybe(state->object_nl);
|
||||
rb_gc_mark_maybe(state->array_nl);
|
||||
rb_gc_mark_maybe(state->seen);
|
||||
rb_gc_mark_maybe(state->memo);
|
||||
rb_gc_mark_maybe(state->depth);
|
||||
}
|
||||
|
||||
static JSON_Generator_State *State_allocate()
|
||||
{
|
||||
JSON_Generator_State *state = ALLOC(JSON_Generator_State);
|
||||
return state;
|
||||
}
|
||||
|
||||
static VALUE cState_s_allocate(VALUE klass)
|
||||
{
|
||||
JSON_Generator_State *state = State_allocate();
|
||||
return Data_Wrap_Struct(klass, State_mark, -1, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: configure(opts)
|
||||
*
|
||||
* Configure this State instance with the Hash _opts_, and return
|
||||
* itself.
|
||||
*/
|
||||
static VALUE cState_configure(VALUE self, VALUE opts)
|
||||
{
|
||||
VALUE tmp;
|
||||
GET_STATE(self);
|
||||
tmp = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
|
||||
if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
|
||||
if (NIL_P(tmp)) {
|
||||
rb_raise(rb_eArgError, "opts has to be hash like or convertable into a hash");
|
||||
}
|
||||
opts = tmp;
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_indent));
|
||||
if (RTEST(tmp)) {
|
||||
Check_Type(tmp, T_STRING);
|
||||
state->indent = tmp;
|
||||
}
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_space));
|
||||
if (RTEST(tmp)) {
|
||||
Check_Type(tmp, T_STRING);
|
||||
state->space = tmp;
|
||||
}
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
|
||||
if (RTEST(tmp)) {
|
||||
Check_Type(tmp, T_STRING);
|
||||
state->space_before = tmp;
|
||||
}
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
|
||||
if (RTEST(tmp)) {
|
||||
Check_Type(tmp, T_STRING);
|
||||
state->array_nl = tmp;
|
||||
}
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
|
||||
if (RTEST(tmp)) {
|
||||
Check_Type(tmp, T_STRING);
|
||||
state->object_nl = tmp;
|
||||
}
|
||||
tmp = ID2SYM(i_check_circular);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
|
||||
state->check_circular = RTEST(tmp);
|
||||
} else {
|
||||
state->check_circular = 1;
|
||||
}
|
||||
tmp = ID2SYM(i_max_nesting);
|
||||
state->max_nesting = 19;
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
VALUE max_nesting = rb_hash_aref(opts, tmp);
|
||||
if (RTEST(max_nesting)) {
|
||||
Check_Type(max_nesting, T_FIXNUM);
|
||||
state->max_nesting = FIX2LONG(max_nesting);
|
||||
} else {
|
||||
state->max_nesting = 0;
|
||||
}
|
||||
}
|
||||
tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
|
||||
state->allow_nan = RTEST(tmp);
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: to_h
|
||||
*
|
||||
* Returns the configuration instance variables as a hash, that can be
|
||||
* passed to the configure method.
|
||||
*/
|
||||
static VALUE cState_to_h(VALUE self)
|
||||
{
|
||||
VALUE result = rb_hash_new();
|
||||
GET_STATE(self);
|
||||
rb_hash_aset(result, ID2SYM(i_indent), state->indent);
|
||||
rb_hash_aset(result, ID2SYM(i_space), state->space);
|
||||
rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
|
||||
rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
|
||||
rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
|
||||
rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
|
||||
rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
|
||||
rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call-seq: new(opts = {})
|
||||
*
|
||||
* 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, 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.
|
||||
*/
|
||||
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE opts;
|
||||
GET_STATE(self);
|
||||
|
||||
rb_scan_args(argc, argv, "01", &opts);
|
||||
state->indent = rb_str_new2("");
|
||||
state->space = rb_str_new2("");
|
||||
state->space_before = rb_str_new2("");
|
||||
state->array_nl = rb_str_new2("");
|
||||
state->object_nl = rb_str_new2("");
|
||||
if (NIL_P(opts)) {
|
||||
state->check_circular = 1;
|
||||
state->allow_nan = 0;
|
||||
state->max_nesting = 19;
|
||||
} else {
|
||||
cState_configure(self, opts);
|
||||
}
|
||||
state->seen = rb_hash_new();
|
||||
state->memo = Qnil;
|
||||
state->depth = INT2FIX(0);
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: from_state(opts)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static VALUE cState_from_state_s(VALUE self, VALUE opts)
|
||||
{
|
||||
if (rb_obj_is_kind_of(opts, self)) {
|
||||
return opts;
|
||||
} else if (rb_obj_is_kind_of(opts, rb_cHash)) {
|
||||
return rb_funcall(self, i_new, 1, opts);
|
||||
} else {
|
||||
return rb_funcall(self, i_new, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: indent()
|
||||
*
|
||||
* This string is used to indent levels in the JSON text.
|
||||
*/
|
||||
static VALUE cState_indent(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->indent;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: indent=(indent)
|
||||
*
|
||||
* This string is used to indent levels in the JSON text.
|
||||
*/
|
||||
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
||||
{
|
||||
GET_STATE(self);
|
||||
Check_Type(indent, T_STRING);
|
||||
return state->indent = indent;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: space()
|
||||
*
|
||||
* This string is used to insert a space between the tokens in a JSON
|
||||
* string.
|
||||
*/
|
||||
static VALUE cState_space(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->space;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: space=(space)
|
||||
*
|
||||
* This string is used to insert a space between the tokens in a JSON
|
||||
* string.
|
||||
*/
|
||||
static VALUE cState_space_set(VALUE self, VALUE space)
|
||||
{
|
||||
GET_STATE(self);
|
||||
Check_Type(space, T_STRING);
|
||||
return state->space = space;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: space_before()
|
||||
*
|
||||
* This string is used to insert a space before the ':' in JSON objects.
|
||||
*/
|
||||
static VALUE cState_space_before(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->space_before;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: space_before=(space_before)
|
||||
*
|
||||
* This string is used to insert a space before the ':' in JSON objects.
|
||||
*/
|
||||
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
||||
{
|
||||
GET_STATE(self);
|
||||
Check_Type(space_before, T_STRING);
|
||||
return state->space_before = space_before;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: object_nl()
|
||||
*
|
||||
* This string is put at the end of a line that holds a JSON object (or
|
||||
* Hash).
|
||||
*/
|
||||
static VALUE cState_object_nl(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->object_nl;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: object_nl=(object_nl)
|
||||
*
|
||||
* This string is put at the end of a line that holds a JSON object (or
|
||||
* Hash).
|
||||
*/
|
||||
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
||||
{
|
||||
GET_STATE(self);
|
||||
Check_Type(object_nl, T_STRING);
|
||||
return state->object_nl = object_nl;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: array_nl()
|
||||
*
|
||||
* This string is put at the end of a line that holds a JSON array.
|
||||
*/
|
||||
static VALUE cState_array_nl(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->array_nl;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: array_nl=(array_nl)
|
||||
*
|
||||
* This string is put at the end of a line that holds a JSON array.
|
||||
*/
|
||||
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
||||
{
|
||||
GET_STATE(self);
|
||||
Check_Type(array_nl, T_STRING);
|
||||
return state->array_nl = array_nl;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: check_circular?
|
||||
*
|
||||
* Returns true, if circular data structures should be checked,
|
||||
* otherwise returns false.
|
||||
*/
|
||||
static VALUE cState_check_circular_p(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->check_circular ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: max_nesting
|
||||
*
|
||||
* This integer returns the maximum level of data structure nesting in
|
||||
* the generated JSON, max_nesting = 0 if no maximum is checked.
|
||||
*/
|
||||
static VALUE cState_max_nesting(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return LONG2FIX(state->max_nesting);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: max_nesting=(depth)
|
||||
*
|
||||
* This sets the maximum level of data structure nesting in the generated JSON
|
||||
* to the integer depth, max_nesting = 0 if no maximum should be checked.
|
||||
*/
|
||||
static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
||||
{
|
||||
GET_STATE(self);
|
||||
Check_Type(depth, T_FIXNUM);
|
||||
state->max_nesting = FIX2LONG(depth);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: allow_nan?
|
||||
*
|
||||
* Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
|
||||
* returns false.
|
||||
*/
|
||||
static VALUE cState_allow_nan_p(VALUE self)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return state->allow_nan ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: seen?(object)
|
||||
*
|
||||
* Returns _true_, if _object_ was already seen during this generating run.
|
||||
*/
|
||||
static VALUE cState_seen_p(VALUE self, VALUE object)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return rb_hash_aref(state->seen, rb_obj_id(object));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: remember(object)
|
||||
*
|
||||
* Remember _object_, to find out if it was already encountered (if a cyclic
|
||||
* data structure is rendered).
|
||||
*/
|
||||
static VALUE cState_remember(VALUE self, VALUE object)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return rb_hash_aset(state->seen, rb_obj_id(object), Qtrue);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: forget(object)
|
||||
*
|
||||
* Forget _object_ for this generating run.
|
||||
*/
|
||||
static VALUE cState_forget(VALUE self, VALUE object)
|
||||
{
|
||||
GET_STATE(self);
|
||||
return rb_hash_delete(state->seen, rb_obj_id(object));
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void Init_generator()
|
||||
{
|
||||
rb_require("json/common");
|
||||
mJSON = rb_define_module("JSON");
|
||||
mExt = rb_define_module_under(mJSON, "Ext");
|
||||
mGenerator = rb_define_module_under(mExt, "Generator");
|
||||
eGeneratorError = rb_path2class("JSON::GeneratorError");
|
||||
eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
|
||||
eNestingError = rb_path2class("JSON::NestingError");
|
||||
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
|
||||
rb_define_alloc_func(cState, cState_s_allocate);
|
||||
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
|
||||
rb_define_method(cState, "initialize", cState_initialize, -1);
|
||||
|
||||
rb_define_method(cState, "indent", cState_indent, 0);
|
||||
rb_define_method(cState, "indent=", cState_indent_set, 1);
|
||||
rb_define_method(cState, "space", cState_space, 0);
|
||||
rb_define_method(cState, "space=", cState_space_set, 1);
|
||||
rb_define_method(cState, "space_before", cState_space_before, 0);
|
||||
rb_define_method(cState, "space_before=", cState_space_before_set, 1);
|
||||
rb_define_method(cState, "object_nl", cState_object_nl, 0);
|
||||
rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
|
||||
rb_define_method(cState, "array_nl", cState_array_nl, 0);
|
||||
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
|
||||
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
|
||||
rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
|
||||
rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
|
||||
rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
|
||||
rb_define_method(cState, "seen?", cState_seen_p, 1);
|
||||
rb_define_method(cState, "remember", cState_remember, 1);
|
||||
rb_define_method(cState, "forget", cState_forget, 1);
|
||||
rb_define_method(cState, "configure", cState_configure, 1);
|
||||
rb_define_method(cState, "to_h", cState_to_h, 0);
|
||||
|
||||
mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
|
||||
mObject = rb_define_module_under(mGeneratorMethods, "Object");
|
||||
rb_define_method(mObject, "to_json", mObject_to_json, -1);
|
||||
mHash = rb_define_module_under(mGeneratorMethods, "Hash");
|
||||
rb_define_method(mHash, "to_json", mHash_to_json, -1);
|
||||
mArray = rb_define_module_under(mGeneratorMethods, "Array");
|
||||
rb_define_method(mArray, "to_json", mArray_to_json, -1);
|
||||
mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
|
||||
rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
|
||||
mFloat = rb_define_module_under(mGeneratorMethods, "Float");
|
||||
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
||||
mString = rb_define_module_under(mGeneratorMethods, "String");
|
||||
rb_define_singleton_method(mString, "included", mString_included_s, 1);
|
||||
rb_define_method(mString, "to_json", mString_to_json, -1);
|
||||
rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
|
||||
rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
|
||||
mString_Extend = rb_define_module_under(mString, "Extend");
|
||||
rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
|
||||
mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
||||
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
||||
mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
|
||||
rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
|
||||
mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
|
||||
rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
|
||||
|
||||
i_to_s = rb_intern("to_s");
|
||||
i_to_json = rb_intern("to_json");
|
||||
i_new = rb_intern("new");
|
||||
i_indent = rb_intern("indent");
|
||||
i_space = rb_intern("space");
|
||||
i_space_before = rb_intern("space_before");
|
||||
i_object_nl = rb_intern("object_nl");
|
||||
i_array_nl = rb_intern("array_nl");
|
||||
i_check_circular = rb_intern("check_circular");
|
||||
i_max_nesting = rb_intern("max_nesting");
|
||||
i_allow_nan = rb_intern("allow_nan");
|
||||
i_pack = rb_intern("pack");
|
||||
i_unpack = rb_intern("unpack");
|
||||
i_create_id = rb_intern("create_id");
|
||||
i_extend = rb_intern("extend");
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
#include "unicode.h"
|
||||
|
||||
#define unicode_escape(buffer, character) \
|
||||
snprintf(buf, 7, "\\u%04x", (unsigned int) (character)); \
|
||||
rb_str_buf_cat(buffer, buf, 6);
|
||||
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Index into the table below with the first byte of a UTF-8 sequence to
|
||||
* get the number of trailing bytes that are supposed to follow it.
|
||||
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
|
||||
* left as-is for anyone who may want to do such conversion, which was
|
||||
* allowed in earlier algorithms.
|
||||
*/
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/*
|
||||
* Magic values subtracted from a buffer value during UTF8 conversion.
|
||||
* This table contains as many values as there might be trailing bytes
|
||||
* in a UTF-8 sequence.
|
||||
*/
|
||||
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
/*
|
||||
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
|
||||
* into the first byte, depending on how many bytes follow. There are
|
||||
* as many entries in this table as there are UTF-8 sequence types.
|
||||
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
|
||||
* for *legal* UTF-8 will be 4 or fewer bytes total.
|
||||
*/
|
||||
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
/*
|
||||
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
|
||||
* This must be called with the length pre-determined by the first byte.
|
||||
* If not calling this from ConvertUTF8to*, then the length can be set by:
|
||||
* length = trailingBytesForUTF8[*source]+1;
|
||||
* and the sequence is illegal right away if there aren't that many bytes
|
||||
* available.
|
||||
* If presented with a length > 4, this returns 0. The Unicode
|
||||
* definition of UTF-8 goes up to 4-byte sequences.
|
||||
*/
|
||||
|
||||
inline static unsigned char isLegalUTF8(const UTF8 *source, int length)
|
||||
{
|
||||
UTF8 a;
|
||||
const UTF8 *srcptr = source+length;
|
||||
switch (length) {
|
||||
default: return 0;
|
||||
/* Everything else falls through when "1"... */
|
||||
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
|
||||
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
|
||||
case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
|
||||
|
||||
switch (*source) {
|
||||
/* no fall-through in this inner switch */
|
||||
case 0xE0: if (a < 0xA0) return 0; break;
|
||||
case 0xED: if (a > 0x9F) return 0; break;
|
||||
case 0xF0: if (a < 0x90) return 0; break;
|
||||
case 0xF4: if (a > 0x8F) return 0; break;
|
||||
default: if (a < 0x80) return 0;
|
||||
}
|
||||
|
||||
case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
|
||||
}
|
||||
if (*source > 0xF4) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void JSON_convert_UTF8_to_JSON(VALUE buffer, VALUE string, ConversionFlags flags)
|
||||
{
|
||||
char buf[7];
|
||||
const UTF8* source = (UTF8 *) RSTRING_PTR(string);
|
||||
const UTF8* sourceEnd = source + RSTRING_LEN(string);
|
||||
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (source + extraBytesToRead >= sourceEnd) {
|
||||
rb_raise(rb_path2class("JSON::GeneratorError"),
|
||||
"partial character in source, but hit end");
|
||||
}
|
||||
if (!isLegalUTF8(source, extraBytesToRead+1)) {
|
||||
rb_raise(rb_path2class("JSON::GeneratorError"),
|
||||
"source sequence is illegal/malformed");
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead) {
|
||||
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
||||
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
||||
case 3: ch += *source++; ch <<= 6;
|
||||
case 2: ch += *source++; ch <<= 6;
|
||||
case 1: ch += *source++; ch <<= 6;
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
source -= (extraBytesToRead+1); /* return to the illegal value itself */
|
||||
rb_raise(rb_path2class("JSON::GeneratorError"),
|
||||
"source sequence is illegal/malformed");
|
||||
} else {
|
||||
unicode_escape(buffer, UNI_REPLACEMENT_CHAR);
|
||||
}
|
||||
} else {
|
||||
/* normal case */
|
||||
if (ch == '"') {
|
||||
rb_str_buf_cat2(buffer, "\\\"");
|
||||
} else if (ch == '\\') {
|
||||
rb_str_buf_cat2(buffer, "\\\\");
|
||||
} else if (ch >= 0x20 && ch <= 0x7f) {
|
||||
rb_str_buf_cat(buffer, (char *) source - 1, 1);
|
||||
} else if (ch == '\n') {
|
||||
rb_str_buf_cat2(buffer, "\\n");
|
||||
} else if (ch == '\r') {
|
||||
rb_str_buf_cat2(buffer, "\\r");
|
||||
} else if (ch == '\t') {
|
||||
rb_str_buf_cat2(buffer, "\\t");
|
||||
} else if (ch == '\f') {
|
||||
rb_str_buf_cat2(buffer, "\\f");
|
||||
} else if (ch == '\b') {
|
||||
rb_str_buf_cat2(buffer, "\\b");
|
||||
} else if (ch < 0x20) {
|
||||
unicode_escape(buffer, (UTF16) ch);
|
||||
} else {
|
||||
unicode_escape(buffer, (UTF16) ch);
|
||||
}
|
||||
}
|
||||
} else if (ch > UNI_MAX_UTF16) {
|
||||
if (flags == strictConversion) {
|
||||
source -= (extraBytesToRead+1); /* return to the start */
|
||||
rb_raise(rb_path2class("JSON::GeneratorError"),
|
||||
"source sequence is illegal/malformed");
|
||||
} else {
|
||||
unicode_escape(buffer, UNI_REPLACEMENT_CHAR);
|
||||
}
|
||||
} else {
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
ch -= halfBase;
|
||||
unicode_escape(buffer, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
|
||||
unicode_escape(buffer, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#include "ruby.h"
|
||||
|
||||
#ifndef _GENERATOR_UNICODE_H_
|
||||
#define _GENERATOR_UNICODE_H_
|
||||
|
||||
typedef enum {
|
||||
conversionOK = 0, /* conversion successful */
|
||||
sourceExhausted, /* partial character in source, but hit end */
|
||||
targetExhausted, /* insuff. room in target for conversion */
|
||||
sourceIllegal /* source sequence is illegal/malformed */
|
||||
} ConversionResult;
|
||||
|
||||
typedef enum {
|
||||
strictConversion = 0,
|
||||
lenientConversion
|
||||
} ConversionFlags;
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
#define UNI_MAX_BMP (UTF32)0x0000FFFF
|
||||
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
|
||||
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
|
||||
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
|
||||
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
static const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
static const UTF32 halfBase = 0x0010000UL;
|
||||
static const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
void JSON_convert_UTF8_to_JSON(VALUE buffer, VALUE string, ConversionFlags flags);
|
||||
|
||||
#ifndef RARRAY_PTR
|
||||
#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
|
||||
#endif
|
||||
#ifndef RARRAY_LEN
|
||||
#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
|
||||
#endif
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(string) RSTRING(string)->ptr
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(string) RSTRING(string)->len
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,9 +0,0 @@
|
|||
require 'mkmf'
|
||||
require 'rbconfig'
|
||||
|
||||
if CONFIG['GCC'] == 'yes'
|
||||
$CFLAGS += ' -Wall'
|
||||
#$CFLAGS += ' -O0 -ggdb'
|
||||
end
|
||||
|
||||
create_makefile 'json/ext/parser'
|
|
@ -1,154 +0,0 @@
|
|||
#include "unicode.h"
|
||||
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Index into the table below with the first byte of a UTF-8 sequence to
|
||||
* get the number of trailing bytes that are supposed to follow it.
|
||||
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
|
||||
* left as-is for anyone who may want to do such conversion, which was
|
||||
* allowed in earlier algorithms.
|
||||
*/
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/*
|
||||
* Magic values subtracted from a buffer value during UTF8 conversion.
|
||||
* This table contains as many values as there might be trailing bytes
|
||||
* in a UTF-8 sequence.
|
||||
*/
|
||||
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
/*
|
||||
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
|
||||
* into the first byte, depending on how many bytes follow. There are
|
||||
* as many entries in this table as there are UTF-8 sequence types.
|
||||
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
|
||||
* for *legal* UTF-8 will be 4 or fewer bytes total.
|
||||
*/
|
||||
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
char *JSON_convert_UTF16_to_UTF8 (
|
||||
VALUE buffer,
|
||||
char *source,
|
||||
char *sourceEnd,
|
||||
ConversionFlags flags)
|
||||
{
|
||||
UTF16 *tmp, *tmpPtr, *tmpEnd;
|
||||
char buf[5];
|
||||
long n = 0, i;
|
||||
char *p = source - 1;
|
||||
|
||||
while (p < sourceEnd && p[0] == '\\' && p[1] == 'u') {
|
||||
p += 6;
|
||||
n++;
|
||||
}
|
||||
p = source + 1;
|
||||
buf[4] = 0;
|
||||
tmpPtr = tmp = ALLOC_N(UTF16, n);
|
||||
tmpEnd = tmp + n;
|
||||
for (i = 0; i < n; i++) {
|
||||
buf[0] = *p++;
|
||||
buf[1] = *p++;
|
||||
buf[2] = *p++;
|
||||
buf[3] = *p++;
|
||||
tmpPtr[i] = (UTF16)strtol(buf, NULL, 16);
|
||||
p += 2;
|
||||
}
|
||||
|
||||
while (tmpPtr < tmpEnd) {
|
||||
UTF32 ch;
|
||||
unsigned short bytesToWrite = 0;
|
||||
const UTF32 byteMask = 0xBF;
|
||||
const UTF32 byteMark = 0x80;
|
||||
ch = *tmpPtr++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
|
||||
/* If the 16 bits following the high surrogate are in the source
|
||||
* buffer... */
|
||||
if (tmpPtr < tmpEnd) {
|
||||
UTF32 ch2 = *tmpPtr;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
|
||||
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++tmpPtr;
|
||||
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
|
||||
ruby_xfree(tmp);
|
||||
rb_raise(rb_path2class("JSON::ParserError"),
|
||||
"source sequence is illegal/malformed near %s", source);
|
||||
}
|
||||
} else { /* We don't have the 16 bits following the high surrogate. */
|
||||
ruby_xfree(tmp);
|
||||
rb_raise(rb_path2class("JSON::ParserError"),
|
||||
"partial character in source, but hit end near %s", source);
|
||||
break;
|
||||
}
|
||||
} else if (flags == strictConversion) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
|
||||
ruby_xfree(tmp);
|
||||
rb_raise(rb_path2class("JSON::ParserError"),
|
||||
"source sequence is illegal/malformed near %s", source);
|
||||
}
|
||||
}
|
||||
/* Figure out how many bytes the result will require */
|
||||
if (ch < (UTF32) 0x80) {
|
||||
bytesToWrite = 1;
|
||||
} else if (ch < (UTF32) 0x800) {
|
||||
bytesToWrite = 2;
|
||||
} else if (ch < (UTF32) 0x10000) {
|
||||
bytesToWrite = 3;
|
||||
} else if (ch < (UTF32) 0x110000) {
|
||||
bytesToWrite = 4;
|
||||
} else {
|
||||
bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = 0;
|
||||
buf[3] = 0;
|
||||
p = buf + bytesToWrite;
|
||||
switch (bytesToWrite) { /* note: everything falls through. */
|
||||
case 4: *--p = (UTF8) ((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 3: *--p = (UTF8) ((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 2: *--p = (UTF8) ((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 1: *--p = (UTF8) (ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
rb_str_buf_cat(buffer, p, bytesToWrite);
|
||||
}
|
||||
ruby_xfree(tmp);
|
||||
source += 5 + (n - 1) * 6;
|
||||
return source;
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
|
||||
#ifndef _PARSER_UNICODE_H_
|
||||
#define _PARSER_UNICODE_H_
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
#define UNI_MAX_BMP (UTF32)0x0000FFFF
|
||||
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
|
||||
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
|
||||
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
|
||||
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
static const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
static const UTF32 halfBase = 0x0010000UL;
|
||||
static const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
typedef enum {
|
||||
conversionOK = 0, /* conversion successful */
|
||||
sourceExhausted, /* partial character in source, but hit end */
|
||||
targetExhausted, /* insuff. room in target for conversion */
|
||||
sourceIllegal /* source sequence is illegal/malformed */
|
||||
} ConversionResult;
|
||||
|
||||
typedef enum {
|
||||
strictConversion = 0,
|
||||
lenientConversion
|
||||
} ConversionFlags;
|
||||
|
||||
char *JSON_convert_UTF16_to_UTF8 (
|
||||
VALUE buffer,
|
||||
char *source,
|
||||
char *sourceEnd,
|
||||
ConversionFlags flags);
|
||||
|
||||
#ifndef RARRAY_PTR
|
||||
#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
|
||||
#endif
|
||||
#ifndef RARRAY_LEN
|
||||
#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
|
||||
#endif
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(string) RSTRING(string)->ptr
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(string) RSTRING(string)->len
|
||||
#endif
|
||||
|
||||
#endif
|
14
ext/json/generator/extconf.rb
Normal file
14
ext/json/generator/extconf.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'mkmf'
|
||||
require 'rbconfig'
|
||||
|
||||
unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O3')
|
||||
$CFLAGS << ' -O3'
|
||||
end
|
||||
if CONFIG['CC'] =~ /gcc/
|
||||
$CFLAGS << ' -Wall'
|
||||
#unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O0 -ggdb')
|
||||
# $CFLAGS << ' -O0 -ggdb'
|
||||
#end
|
||||
end
|
||||
|
||||
create_makefile 'json/ext/generator'
|
1334
ext/json/generator/generator.c
Normal file
1334
ext/json/generator/generator.c
Normal file
File diff suppressed because it is too large
Load diff
170
ext/json/generator/generator.h
Normal file
170
ext/json/generator/generator.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
#ifndef _GENERATOR_H_
|
||||
#define _GENERATOR_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
#if HAVE_RUBY_RE_H
|
||||
#include "ruby/re.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_RE_H
|
||||
#include "re.h"
|
||||
#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 option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
|
||||
|
||||
#ifndef RHASH_SIZE
|
||||
#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
|
||||
#endif
|
||||
|
||||
#ifndef RFLOAT_VALUE
|
||||
#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
|
||||
#endif
|
||||
|
||||
#ifndef RARRAY_PTR
|
||||
#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
|
||||
#endif
|
||||
#ifndef RARRAY_LEN
|
||||
#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
|
||||
#endif
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(string) RSTRING(string)->ptr
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(string) RSTRING(string)->len
|
||||
#endif
|
||||
|
||||
#define RSTRING_PAIR(string) RSTRING_PTR(string), RSTRING_LEN(string)
|
||||
|
||||
/* fbuffer implementation */
|
||||
|
||||
typedef struct FBufferStruct {
|
||||
unsigned int initial_length;
|
||||
char *ptr;
|
||||
unsigned int len;
|
||||
unsigned int capa;
|
||||
} FBuffer;
|
||||
|
||||
#define FBUFFER_INITIAL_LENGTH 4096
|
||||
|
||||
#define FBUFFER_PTR(fb) (fb->ptr)
|
||||
#define FBUFFER_LEN(fb) (fb->len)
|
||||
#define FBUFFER_CAPA(fb) (fb->capa)
|
||||
#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
|
||||
|
||||
static char *fstrndup(const char *ptr, int len);
|
||||
static FBuffer *fbuffer_alloc();
|
||||
static FBuffer *fbuffer_alloc_with_length(unsigned initial_length);
|
||||
static void fbuffer_free(FBuffer *fb);
|
||||
static void fbuffer_free_only_buffer(FBuffer *fb);
|
||||
static void fbuffer_clear(FBuffer *fb);
|
||||
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len);
|
||||
static void fbuffer_append_long(FBuffer *fb, long number);
|
||||
static void fbuffer_append_char(FBuffer *fb, char newchr);
|
||||
static FBuffer *fbuffer_dup(FBuffer *fb);
|
||||
|
||||
/* unicode defintions */
|
||||
|
||||
#define UNI_STRICT_CONVERSION 1
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
#define UNI_MAX_BMP (UTF32)0x0000FFFF
|
||||
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
|
||||
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
|
||||
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
|
||||
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
static const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
static const UTF32 halfBase = 0x0010000UL;
|
||||
static const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
static unsigned char isLegalUTF8(const UTF8 *source, int length);
|
||||
static void unicode_escape(char *buf, UTF16 character);
|
||||
static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character);
|
||||
static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string);
|
||||
static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string);
|
||||
|
||||
/* ruby api and some helpers */
|
||||
|
||||
typedef struct JSON_Generator_StateStruct {
|
||||
char *indent;
|
||||
long indent_len;
|
||||
char *space;
|
||||
long space_len;
|
||||
char *space_before;
|
||||
long space_before_len;
|
||||
char *object_nl;
|
||||
long object_nl_len;
|
||||
char *array_nl;
|
||||
long array_nl_len;
|
||||
FBuffer *array_delim;
|
||||
FBuffer *object_delim;
|
||||
FBuffer *object_delim2;
|
||||
long max_nesting;
|
||||
char allow_nan;
|
||||
char ascii_only;
|
||||
} JSON_Generator_State;
|
||||
|
||||
#define GET_STATE(self) \
|
||||
JSON_Generator_State *state; \
|
||||
Data_Get_Struct(self, JSON_Generator_State, state)
|
||||
|
||||
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mString_included_s(VALUE self, VALUE modul);
|
||||
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mString_to_json_raw_object(VALUE self);
|
||||
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mString_Extend_json_create(VALUE self, VALUE o);
|
||||
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static void State_free(JSON_Generator_State *state);
|
||||
static JSON_Generator_State *State_allocate();
|
||||
static VALUE cState_s_allocate(VALUE klass);
|
||||
static VALUE cState_configure(VALUE self, VALUE opts);
|
||||
static VALUE cState_to_h(VALUE self);
|
||||
static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth);
|
||||
static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth);
|
||||
static VALUE cState_generate(VALUE self, VALUE obj);
|
||||
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
||||
static VALUE cState_indent(VALUE self);
|
||||
static VALUE cState_indent_set(VALUE self, VALUE indent);
|
||||
static VALUE cState_space(VALUE self);
|
||||
static VALUE cState_space_set(VALUE self, VALUE space);
|
||||
static VALUE cState_space_before(VALUE self);
|
||||
static VALUE cState_space_before_set(VALUE self, VALUE space_before);
|
||||
static VALUE cState_object_nl(VALUE self);
|
||||
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl);
|
||||
static VALUE cState_array_nl(VALUE self);
|
||||
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl);
|
||||
static VALUE cState_max_nesting(VALUE self);
|
||||
static VALUE cState_max_nesting_set(VALUE self, VALUE depth);
|
||||
static VALUE cState_allow_nan_p(VALUE self);
|
||||
static VALUE cState_ascii_only_p(VALUE self);
|
||||
|
||||
#endif
|
|
@ -7,6 +7,19 @@ unless Object.const_defined?(:JSON) and ::JSON.const_defined?(:JSON_LOADED) and
|
|||
end
|
||||
require 'date'
|
||||
|
||||
class Symbol
|
||||
def to_json(*a)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
's' => to_s,
|
||||
}.to_json(*a)
|
||||
end
|
||||
|
||||
def self.json_create(o)
|
||||
o['s'].to_sym
|
||||
end
|
||||
end
|
||||
|
||||
class Time
|
||||
def self.json_create(object)
|
||||
if usec = object.delete('u') # used to be tv_usec -> tv_nsec
|
||||
|
@ -21,7 +34,7 @@ class Time
|
|||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
JSON.create_id => self.class.name,
|
||||
's' => tv_sec,
|
||||
'n' => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000
|
||||
}.to_json(*args)
|
||||
|
@ -37,7 +50,7 @@ class Date
|
|||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
JSON.create_id => self.class.name,
|
||||
'y' => year,
|
||||
'm' => month,
|
||||
'd' => day,
|
||||
|
@ -63,7 +76,7 @@ class DateTime
|
|||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
JSON.create_id => self.class.name,
|
||||
'y' => year,
|
||||
'm' => month,
|
||||
'd' => day,
|
||||
|
@ -83,7 +96,7 @@ class Range
|
|||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
JSON.create_id => self.class.name,
|
||||
'a' => [ first, last, exclude_end? ]
|
||||
}.to_json(*args)
|
||||
end
|
||||
|
@ -98,7 +111,7 @@ class Struct
|
|||
klass = self.class.name
|
||||
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
|
||||
{
|
||||
'json_class' => klass,
|
||||
JSON.create_id => klass,
|
||||
'v' => values,
|
||||
}.to_json(*args)
|
||||
end
|
||||
|
@ -113,7 +126,7 @@ class Exception
|
|||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
JSON.create_id => self.class.name,
|
||||
'm' => message,
|
||||
'b' => backtrace,
|
||||
}.to_json(*args)
|
||||
|
@ -127,7 +140,7 @@ class Regexp
|
|||
|
||||
def to_json(*)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
JSON.create_id => self.class.name,
|
||||
'o' => options,
|
||||
's' => source,
|
||||
}.to_json
|
||||
|
|
|
@ -10,7 +10,7 @@ class Object
|
|||
def self.json_create(object)
|
||||
obj = new
|
||||
for key, value in object
|
||||
next if key == 'json_class'
|
||||
next if key == JSON.create_id
|
||||
instance_variable_set "@#{key}", value
|
||||
end
|
||||
obj
|
||||
|
@ -18,7 +18,7 @@ class Object
|
|||
|
||||
def to_json(*a)
|
||||
result = {
|
||||
'json_class' => self.class.name
|
||||
JSON.create_id => self.class.name
|
||||
}
|
||||
instance_variables.inject(result) do |r, name|
|
||||
r[name[1..-1]] = instance_variable_get name
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'json/version'
|
||||
require 'iconv'
|
||||
|
||||
module JSON
|
||||
class << self
|
||||
|
@ -32,12 +33,16 @@ module JSON
|
|||
# level (absolute namespace path?). If there doesn't exist a constant at
|
||||
# the given path, an ArgumentError is raised.
|
||||
def deep_const_get(path) # :nodoc:
|
||||
path = path.to_s
|
||||
path.split(/::/).inject(Object) do |p, c|
|
||||
path.to_s.split(/::/).inject(Object) do |p, c|
|
||||
case
|
||||
when c.empty? then p
|
||||
when p.const_defined?(c) then p.const_get(c)
|
||||
else raise ArgumentError, "can't find const #{path}"
|
||||
else
|
||||
begin
|
||||
p.const_missing(c)
|
||||
rescue NameError
|
||||
raise ArgumentError, "can't find const #{path}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -58,6 +63,20 @@ module JSON
|
|||
end
|
||||
self.state = generator::State
|
||||
const_set :State, self.state
|
||||
const_set :SAFE_STATE_PROTOTYPE, State.new.freeze
|
||||
const_set :FAST_STATE_PROTOTYPE, State.new(
|
||||
:indent => '',
|
||||
:space => '',
|
||||
:object_nl => "",
|
||||
:array_nl => "",
|
||||
:max_nesting => false
|
||||
).freeze
|
||||
const_set :PRETTY_STATE_PROTOTYPE, State.new(
|
||||
:indent => ' ',
|
||||
:space => ' ',
|
||||
:object_nl => "\n",
|
||||
:array_nl => "\n"
|
||||
).freeze
|
||||
end
|
||||
|
||||
# Returns the JSON generator modul, that is used by JSON. This might be
|
||||
|
@ -90,22 +109,22 @@ module JSON
|
|||
# deep.
|
||||
class NestingError < ParserError; end
|
||||
|
||||
# :stopdoc:
|
||||
class CircularDatastructure < NestingError; end
|
||||
# :startdoc:
|
||||
|
||||
# This exception is raised, if a generator or unparser error occurs.
|
||||
class GeneratorError < JSONError; end
|
||||
# For backwards compatibility
|
||||
UnparserError = GeneratorError
|
||||
|
||||
# If a circular data structure is encountered while unparsing
|
||||
# this exception is raised.
|
||||
class CircularDatastructure < GeneratorError; end
|
||||
|
||||
# This exception is raised, if the required unicode support is missing on the
|
||||
# system. Usually this means, that the iconv library is not installed.
|
||||
class MissingUnicodeSupport < JSONError; end
|
||||
|
||||
module_function
|
||||
|
||||
# Parse the JSON string _source_ into a Ruby data structure and return it.
|
||||
# Parse the JSON document _source_ into a Ruby data structure and return it.
|
||||
#
|
||||
# _opts_ can have the following
|
||||
# keys:
|
||||
|
@ -115,16 +134,21 @@ module JSON
|
|||
# * *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.
|
||||
# * *symbolize_names*: If set to true, returns symbols for the names
|
||||
# (keys) in a JSON object. Otherwise strings are returned, which is also
|
||||
# the default.
|
||||
# * *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.
|
||||
# * *object_class*: Defaults to Hash
|
||||
# * *array_class*: Defaults to Array
|
||||
def parse(source, opts = {})
|
||||
JSON.parser.new(source, opts).parse
|
||||
Parser.new(source, opts).parse
|
||||
end
|
||||
|
||||
# Parse the JSON string _source_ into a Ruby data structure and return it.
|
||||
# Parse the JSON document _source_ into a Ruby data structure and return it.
|
||||
# The bang version of the parse method, defaults to the more dangerous values
|
||||
# for the _opts_ hash, so be sure only to parse trusted _source_ strings.
|
||||
# for the _opts_ hash, so be sure only to parse trusted _source_ documents.
|
||||
#
|
||||
# _opts_ can have the following keys:
|
||||
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
|
||||
|
@ -139,15 +163,14 @@ module JSON
|
|||
# defaults to true.
|
||||
def parse!(source, opts = {})
|
||||
opts = {
|
||||
:max_nesting => false,
|
||||
:allow_nan => true
|
||||
:max_nesting => false,
|
||||
:allow_nan => true
|
||||
}.update(opts)
|
||||
JSON.parser.new(source, opts).parse
|
||||
Parser.new(source, opts).parse
|
||||
end
|
||||
|
||||
# Unparse the Ruby data structure _obj_ into a single line JSON string and
|
||||
# return it. _state_ is
|
||||
# * a JSON::State object,
|
||||
# Generate a JSON document from the Ruby data structure _obj_ and return
|
||||
# it. _state_ is * a JSON::State object,
|
||||
# * or a Hash like object (responding to to_hash),
|
||||
# * an object convertible into a hash by a to_h method,
|
||||
# that is used as or to configure a State object.
|
||||
|
@ -160,10 +183,8 @@ module JSON
|
|||
# * *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: ''),
|
||||
# * *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.
|
||||
# * *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.
|
||||
|
@ -174,13 +195,21 @@ module JSON
|
|||
# See also the fast_generate for the fastest creation method with the least
|
||||
# amount of sanity checks, and the pretty_generate method for some
|
||||
# defaults for a pretty output.
|
||||
def generate(obj, state = nil)
|
||||
if state
|
||||
state = State.from_state(state)
|
||||
def generate(obj, opts = nil)
|
||||
if opts
|
||||
if opts.respond_to? :to_hash
|
||||
opts = opts.to_hash
|
||||
elsif opts.respond_to? :to_h
|
||||
opts = opts.to_h
|
||||
else
|
||||
raise TypeError, "can't convert #{opts.class} into Hash"
|
||||
end
|
||||
state = SAFE_STATE_PROTOTYPE.dup
|
||||
state = state.configure(opts)
|
||||
else
|
||||
state = State.new
|
||||
state = SAFE_STATE_PROTOTYPE
|
||||
end
|
||||
obj.to_json(state)
|
||||
state.generate(obj)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
|
@ -190,35 +219,12 @@ module JSON
|
|||
module_function :unparse
|
||||
# :startdoc:
|
||||
|
||||
# Unparse the Ruby data structure _obj_ into a single line JSON string and
|
||||
# return it. This method disables the checks for circles in Ruby objects, and
|
||||
# also generates NaN, Infinity, and, -Infinity float values.
|
||||
# Generate a JSON document from the Ruby data structure _obj_ and return it.
|
||||
# This method disables the checks for circles in Ruby objects.
|
||||
#
|
||||
# *WARNING*: Be careful not to pass any Ruby data structures with circles as
|
||||
# _obj_ argument, because this will cause JSON to go into an infinite loop.
|
||||
def fast_generate(obj)
|
||||
obj.to_json(nil)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
|
||||
alias fast_unparse fast_generate
|
||||
module_function :fast_unparse
|
||||
# :startdoc:
|
||||
|
||||
# Unparse the Ruby data structure _obj_ into a JSON string and return it. The
|
||||
# returned string is a prettier form of the string returned by #unparse.
|
||||
#
|
||||
# The _opts_ argument can be used to configure the generator, see the
|
||||
# generate method for a more detailed explanation.
|
||||
def pretty_generate(obj, opts = nil)
|
||||
state = JSON.state.new(
|
||||
:indent => ' ',
|
||||
:space => ' ',
|
||||
:object_nl => "\n",
|
||||
:array_nl => "\n",
|
||||
:check_circular => true
|
||||
)
|
||||
def fast_generate(obj, opts = nil)
|
||||
if opts
|
||||
if opts.respond_to? :to_hash
|
||||
opts = opts.to_hash
|
||||
|
@ -227,9 +233,41 @@ module JSON
|
|||
else
|
||||
raise TypeError, "can't convert #{opts.class} into Hash"
|
||||
end
|
||||
state = FAST_STATE_PROTOTYPE.dup
|
||||
state.configure(opts)
|
||||
else
|
||||
state = FAST_STATE_PROTOTYPE
|
||||
end
|
||||
obj.to_json(state)
|
||||
state.generate(obj)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
|
||||
alias fast_unparse fast_generate
|
||||
module_function :fast_unparse
|
||||
# :startdoc:
|
||||
|
||||
# Generate a JSON document from the Ruby data structure _obj_ and return it.
|
||||
# The returned document is a prettier form of the document returned by
|
||||
# #unparse.
|
||||
#
|
||||
# The _opts_ argument can be used to configure the generator, see the
|
||||
# generate method for a more detailed explanation.
|
||||
def pretty_generate(obj, opts = nil)
|
||||
if opts
|
||||
if opts.respond_to? :to_hash
|
||||
opts = opts.to_hash
|
||||
elsif opts.respond_to? :to_h
|
||||
opts = opts.to_h
|
||||
else
|
||||
raise TypeError, "can't convert #{opts.class} into Hash"
|
||||
end
|
||||
state = PRETTY_STATE_PROTOTYPE.dup
|
||||
state.configure(opts)
|
||||
else
|
||||
state = PRETTY_STATE_PROTOTYPE
|
||||
end
|
||||
state.generate(obj)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
|
@ -305,6 +343,11 @@ module JSON
|
|||
rescue JSON::NestingError
|
||||
raise ArgumentError, "exceed depth limit"
|
||||
end
|
||||
|
||||
# Shortuct for iconv.
|
||||
def self.iconv(to, from, string)
|
||||
Iconv.iconv(to, from, string).first
|
||||
end
|
||||
end
|
||||
|
||||
module ::Kernel
|
||||
|
|
|
@ -48,14 +48,14 @@ module JSON
|
|||
# Opens an error dialog on top of _window_ showing the error message
|
||||
# _text_.
|
||||
def Editor.error_dialog(window, text)
|
||||
dialog = MessageDialog.new(window, Dialog::MODAL,
|
||||
MessageDialog::ERROR,
|
||||
dialog = MessageDialog.new(window, Dialog::MODAL,
|
||||
MessageDialog::ERROR,
|
||||
MessageDialog::BUTTONS_CLOSE, text)
|
||||
dialog.show_all
|
||||
dialog.run
|
||||
rescue TypeError
|
||||
dialog = MessageDialog.new(Editor.window, Dialog::MODAL,
|
||||
MessageDialog::ERROR,
|
||||
dialog = MessageDialog.new(Editor.window, Dialog::MODAL,
|
||||
MessageDialog::ERROR,
|
||||
MessageDialog::BUTTONS_CLOSE, text)
|
||||
dialog.show_all
|
||||
dialog.run
|
||||
|
@ -67,8 +67,8 @@ module JSON
|
|||
# message _text_. If yes was answered _true_ is returned, otherwise
|
||||
# _false_.
|
||||
def Editor.question_dialog(window, text)
|
||||
dialog = MessageDialog.new(window, Dialog::MODAL,
|
||||
MessageDialog::QUESTION,
|
||||
dialog = MessageDialog.new(window, Dialog::MODAL,
|
||||
MessageDialog::QUESTION,
|
||||
MessageDialog::BUTTONS_YES_NO, text)
|
||||
dialog.show_all
|
||||
dialog.run do |response|
|
||||
|
@ -465,7 +465,7 @@ module JSON
|
|||
add_separator
|
||||
add_item("Append new node", ?a, &method(:append_new_node))
|
||||
add_item("Insert new node before", ?i, &method(:insert_new_node))
|
||||
add_separator
|
||||
add_separator
|
||||
add_item("Collapse/Expand node (recursively)", ?e,
|
||||
&method(:collapse_expand))
|
||||
|
||||
|
@ -504,7 +504,7 @@ module JSON
|
|||
# Revert the current JSON document in the editor to the saved version.
|
||||
def revert(item)
|
||||
window.instance_eval do
|
||||
@filename and file_open(@filename)
|
||||
@filename and file_open(@filename)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -666,7 +666,7 @@ module JSON
|
|||
collapse_all
|
||||
else
|
||||
self.expanded = true
|
||||
expand_all
|
||||
expand_all
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -885,7 +885,7 @@ module JSON
|
|||
dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
|
||||
dialog.show_all
|
||||
self.focus = dialog
|
||||
dialog.run do |response|
|
||||
dialog.run do |response|
|
||||
if response == Dialog::RESPONSE_ACCEPT
|
||||
@key = key_input.text
|
||||
type = ALL_TYPES[@type = type_input.active]
|
||||
|
@ -937,7 +937,7 @@ module JSON
|
|||
dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
|
||||
dialog.show_all
|
||||
self.focus = dialog
|
||||
dialog.run do |response|
|
||||
dialog.run do |response|
|
||||
if response == Dialog::RESPONSE_ACCEPT
|
||||
type = types[type_input.active]
|
||||
@content = case type
|
||||
|
@ -982,7 +982,7 @@ module JSON
|
|||
dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
|
||||
dialog.show_all
|
||||
self.focus = dialog
|
||||
dialog.run do |response|
|
||||
dialog.run do |response|
|
||||
if response == Dialog::RESPONSE_ACCEPT
|
||||
return @order = order_input.text, reverse_checkbox.active?
|
||||
end
|
||||
|
@ -1017,7 +1017,7 @@ module JSON
|
|||
dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
|
||||
dialog.show_all
|
||||
self.focus = dialog
|
||||
dialog.run do |response|
|
||||
dialog.run do |response|
|
||||
if response == Dialog::RESPONSE_ACCEPT
|
||||
begin
|
||||
return Regexp.new(regex_input.text, icase_checkbox.active? ? Regexp::IGNORECASE : 0)
|
||||
|
@ -1216,7 +1216,7 @@ module JSON
|
|||
end
|
||||
end
|
||||
|
||||
# Save the current file as the filename
|
||||
# Save the current file as the filename
|
||||
def file_save_as
|
||||
filename = select_file('Save as a JSON file')
|
||||
store_file(filename)
|
||||
|
@ -1242,7 +1242,7 @@ module JSON
|
|||
rescue SystemCallError => e
|
||||
Editor.error_dialog(self, "Failed to store JSON file: #{e}!")
|
||||
end
|
||||
|
||||
|
||||
# Load the file named _filename_ into the editor as a JSON document.
|
||||
def load_file(filename)
|
||||
if filename
|
||||
|
@ -1335,7 +1335,7 @@ module JSON
|
|||
|
||||
dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
|
||||
dialog.show_all
|
||||
dialog.run do |response|
|
||||
dialog.run do |response|
|
||||
if response == Dialog::RESPONSE_ACCEPT
|
||||
return @location = location_input.text
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module JSON
|
||||
# JSON version
|
||||
VERSION = '1.1.8'
|
||||
VERSION = '1.4.1'
|
||||
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
|
||||
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
||||
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
||||
|
|
14
ext/json/parser/extconf.rb
Normal file
14
ext/json/parser/extconf.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'mkmf'
|
||||
require 'rbconfig'
|
||||
|
||||
unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O3')
|
||||
$CFLAGS << ' -O3'
|
||||
end
|
||||
if CONFIG['CC'] =~ /gcc/
|
||||
$CFLAGS << ' -Wall'
|
||||
#unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O0 -ggdb')
|
||||
# $CFLAGS << ' -O0 -ggdb'
|
||||
#end
|
||||
end
|
||||
|
||||
create_makefile 'json/ext/parser'
|
|
@ -1,68 +1,92 @@
|
|||
|
||||
#line 1 "parser.rl"
|
||||
#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
|
||||
#include "parser.h"
|
||||
|
||||
#define EVIL 0x666
|
||||
/* unicode */
|
||||
|
||||
#ifndef RHASH_TBL
|
||||
#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
|
||||
#endif
|
||||
static const char digit_values[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
|
||||
-1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
static UTF32 unescape_unicode(const unsigned char *p)
|
||||
{
|
||||
char b;
|
||||
UTF32 result = 0;
|
||||
b = digit_values[p[0]];
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
result = (result << 4) | b;
|
||||
b = digit_values[p[1]];
|
||||
result = (result << 4) | b;
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
b = digit_values[p[2]];
|
||||
result = (result << 4) | b;
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
b = digit_values[p[3]];
|
||||
result = (result << 4) | b;
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
|
||||
{
|
||||
int len = 1;
|
||||
if (ch <= 0x7F) {
|
||||
buf[0] = (char) ch;
|
||||
} else if (ch <= 0x07FF) {
|
||||
buf[0] = (char) ((ch >> 6) | 0xC0);
|
||||
buf[1] = (char) ((ch & 0x3F) | 0x80);
|
||||
len++;
|
||||
} else if (ch <= 0xFFFF) {
|
||||
buf[0] = (char) ((ch >> 12) | 0xE0);
|
||||
buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
|
||||
buf[2] = (char) ((ch & 0x3F) | 0x80);
|
||||
len += 2;
|
||||
} else if (ch <= 0x1fffff) {
|
||||
buf[0] =(char) ((ch >> 18) | 0xF0);
|
||||
buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
|
||||
buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
|
||||
buf[3] =(char) ((ch & 0x3F) | 0x80);
|
||||
len += 3;
|
||||
} else {
|
||||
buf[0] = '?';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
#include "ruby/encoding.h"
|
||||
#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
|
||||
static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE,
|
||||
CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE;
|
||||
static ID i_encoding, i_encode, i_encode_bang, i_force_encoding;
|
||||
#else
|
||||
#define FORCE_UTF8(obj)
|
||||
static ID i_iconv;
|
||||
#endif
|
||||
|
||||
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
|
||||
static VALUE CNaN, CInfinity, CMinusInfinity;
|
||||
|
||||
static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
|
||||
i_chr, i_max_nesting, i_allow_nan, i_object_class, i_array_class;
|
||||
|
||||
#define MinusInfinity "-Infinity"
|
||||
|
||||
typedef struct JSON_ParserStruct {
|
||||
VALUE Vsource;
|
||||
char *source;
|
||||
long len;
|
||||
char *memo;
|
||||
VALUE create_id;
|
||||
int max_nesting;
|
||||
int current_nesting;
|
||||
int allow_nan;
|
||||
VALUE object_class;
|
||||
VALUE array_class;
|
||||
} JSON_Parser;
|
||||
|
||||
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
|
||||
#define GET_STRUCT \
|
||||
JSON_Parser *json; \
|
||||
Data_Get_Struct(self, JSON_Parser, json);
|
||||
i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class,
|
||||
i_array_class, i_key_p, i_deep_const_get;
|
||||
|
||||
|
||||
#line 84 "parser.rl"
|
||||
#line 108 "parser.rl"
|
||||
|
||||
|
||||
|
||||
#line 66 "parser.c"
|
||||
#line 90 "parser.c"
|
||||
static const int JSON_object_start = 1;
|
||||
static const int JSON_object_first_final = 27;
|
||||
static const int JSON_object_error = 0;
|
||||
|
@ -70,7 +94,7 @@ static const int JSON_object_error = 0;
|
|||
static const int JSON_object_en_main = 1;
|
||||
|
||||
|
||||
#line 117 "parser.rl"
|
||||
#line 143 "parser.rl"
|
||||
|
||||
|
||||
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
|
@ -86,14 +110,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
*result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
|
||||
|
||||
|
||||
#line 90 "parser.c"
|
||||
#line 114 "parser.c"
|
||||
{
|
||||
cs = JSON_object_start;
|
||||
}
|
||||
|
||||
#line 132 "parser.rl"
|
||||
#line 158 "parser.rl"
|
||||
|
||||
#line 97 "parser.c"
|
||||
#line 121 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -121,9 +145,11 @@ case 2:
|
|||
goto st2;
|
||||
goto st0;
|
||||
tr2:
|
||||
#line 103 "parser.rl"
|
||||
#line 127 "parser.rl"
|
||||
{
|
||||
json->parsing_name = 1;
|
||||
char *np = JSON_parse_string(json, p, pe, &last_name);
|
||||
json->parsing_name = 0;
|
||||
if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;}
|
||||
}
|
||||
goto st3;
|
||||
|
@ -131,7 +157,7 @@ st3:
|
|||
if ( ++p == pe )
|
||||
goto _test_eof3;
|
||||
case 3:
|
||||
#line 135 "parser.c"
|
||||
#line 161 "parser.c"
|
||||
switch( (*p) ) {
|
||||
case 13: goto st3;
|
||||
case 32: goto st3;
|
||||
|
@ -198,10 +224,10 @@ case 8:
|
|||
goto st8;
|
||||
goto st0;
|
||||
tr11:
|
||||
#line 92 "parser.rl"
|
||||
#line 116 "parser.rl"
|
||||
{
|
||||
VALUE v = Qnil;
|
||||
char *np = JSON_parse_value(json, p, pe, &v);
|
||||
char *np = JSON_parse_value(json, p, pe, &v);
|
||||
if (np == NULL) {
|
||||
p--; {p++; cs = 9; goto _out;}
|
||||
} else {
|
||||
|
@ -214,7 +240,7 @@ st9:
|
|||
if ( ++p == pe )
|
||||
goto _test_eof9;
|
||||
case 9:
|
||||
#line 218 "parser.c"
|
||||
#line 244 "parser.c"
|
||||
switch( (*p) ) {
|
||||
case 13: goto st9;
|
||||
case 32: goto st9;
|
||||
|
@ -303,14 +329,14 @@ case 18:
|
|||
goto st9;
|
||||
goto st18;
|
||||
tr4:
|
||||
#line 108 "parser.rl"
|
||||
#line 134 "parser.rl"
|
||||
{ p--; {p++; cs = 27; goto _out;} }
|
||||
goto st27;
|
||||
st27:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof27;
|
||||
case 27:
|
||||
#line 314 "parser.c"
|
||||
#line 340 "parser.c"
|
||||
goto st0;
|
||||
st19:
|
||||
if ( ++p == pe )
|
||||
|
@ -408,13 +434,13 @@ case 26:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 133 "parser.rl"
|
||||
#line 159 "parser.rl"
|
||||
|
||||
if (cs >= JSON_object_first_final) {
|
||||
if (RTEST(json->create_id)) {
|
||||
VALUE klassname = rb_hash_aref(*result, json->create_id);
|
||||
if (!NIL_P(klassname)) {
|
||||
VALUE klass = rb_path2class(StringValueCStr(klassname));
|
||||
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
|
||||
if RTEST(rb_funcall(klass, i_json_creatable_p, 0)) {
|
||||
*result = rb_funcall(klass, i_json_create, 1, *result);
|
||||
}
|
||||
|
@ -427,7 +453,7 @@ case 26:
|
|||
}
|
||||
|
||||
|
||||
#line 431 "parser.c"
|
||||
#line 457 "parser.c"
|
||||
static const int JSON_value_start = 1;
|
||||
static const int JSON_value_first_final = 21;
|
||||
static const int JSON_value_error = 0;
|
||||
|
@ -435,7 +461,7 @@ static const int JSON_value_error = 0;
|
|||
static const int JSON_value_en_main = 1;
|
||||
|
||||
|
||||
#line 231 "parser.rl"
|
||||
#line 257 "parser.rl"
|
||||
|
||||
|
||||
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
|
@ -443,14 +469,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
int cs = EVIL;
|
||||
|
||||
|
||||
#line 447 "parser.c"
|
||||
#line 473 "parser.c"
|
||||
{
|
||||
cs = JSON_value_start;
|
||||
}
|
||||
|
||||
#line 238 "parser.rl"
|
||||
#line 264 "parser.rl"
|
||||
|
||||
#line 454 "parser.c"
|
||||
#line 480 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -475,14 +501,14 @@ st0:
|
|||
cs = 0;
|
||||
goto _out;
|
||||
tr0:
|
||||
#line 179 "parser.rl"
|
||||
#line 205 "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 184 "parser.rl"
|
||||
#line 210 "parser.rl"
|
||||
{
|
||||
char *np;
|
||||
if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
|
||||
|
@ -502,8 +528,8 @@ tr2:
|
|||
}
|
||||
goto st21;
|
||||
tr5:
|
||||
#line 202 "parser.rl"
|
||||
{
|
||||
#line 228 "parser.rl"
|
||||
{
|
||||
char *np;
|
||||
json->current_nesting++;
|
||||
np = JSON_parse_array(json, p, pe, result);
|
||||
|
@ -512,8 +538,8 @@ tr5:
|
|||
}
|
||||
goto st21;
|
||||
tr9:
|
||||
#line 210 "parser.rl"
|
||||
{
|
||||
#line 236 "parser.rl"
|
||||
{
|
||||
char *np;
|
||||
json->current_nesting++;
|
||||
np = JSON_parse_object(json, p, pe, result);
|
||||
|
@ -522,7 +548,7 @@ tr9:
|
|||
}
|
||||
goto st21;
|
||||
tr16:
|
||||
#line 172 "parser.rl"
|
||||
#line 198 "parser.rl"
|
||||
{
|
||||
if (json->allow_nan) {
|
||||
*result = CInfinity;
|
||||
|
@ -532,7 +558,7 @@ tr16:
|
|||
}
|
||||
goto st21;
|
||||
tr18:
|
||||
#line 165 "parser.rl"
|
||||
#line 191 "parser.rl"
|
||||
{
|
||||
if (json->allow_nan) {
|
||||
*result = CNaN;
|
||||
|
@ -542,19 +568,19 @@ tr18:
|
|||
}
|
||||
goto st21;
|
||||
tr22:
|
||||
#line 159 "parser.rl"
|
||||
#line 185 "parser.rl"
|
||||
{
|
||||
*result = Qfalse;
|
||||
}
|
||||
goto st21;
|
||||
tr25:
|
||||
#line 156 "parser.rl"
|
||||
#line 182 "parser.rl"
|
||||
{
|
||||
*result = Qnil;
|
||||
}
|
||||
goto st21;
|
||||
tr28:
|
||||
#line 162 "parser.rl"
|
||||
#line 188 "parser.rl"
|
||||
{
|
||||
*result = Qtrue;
|
||||
}
|
||||
|
@ -563,9 +589,9 @@ st21:
|
|||
if ( ++p == pe )
|
||||
goto _test_eof21;
|
||||
case 21:
|
||||
#line 218 "parser.rl"
|
||||
#line 244 "parser.rl"
|
||||
{ p--; {p++; cs = 21; goto _out;} }
|
||||
#line 569 "parser.c"
|
||||
#line 595 "parser.c"
|
||||
goto st0;
|
||||
st2:
|
||||
if ( ++p == pe )
|
||||
|
@ -726,7 +752,7 @@ case 20:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 239 "parser.rl"
|
||||
#line 265 "parser.rl"
|
||||
|
||||
if (cs >= JSON_value_first_final) {
|
||||
return p;
|
||||
|
@ -736,7 +762,7 @@ case 20:
|
|||
}
|
||||
|
||||
|
||||
#line 740 "parser.c"
|
||||
#line 766 "parser.c"
|
||||
static const int JSON_integer_start = 1;
|
||||
static const int JSON_integer_first_final = 5;
|
||||
static const int JSON_integer_error = 0;
|
||||
|
@ -744,7 +770,7 @@ static const int JSON_integer_error = 0;
|
|||
static const int JSON_integer_en_main = 1;
|
||||
|
||||
|
||||
#line 255 "parser.rl"
|
||||
#line 281 "parser.rl"
|
||||
|
||||
|
||||
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
|
@ -752,15 +778,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
|
|||
int cs = EVIL;
|
||||
|
||||
|
||||
#line 756 "parser.c"
|
||||
#line 782 "parser.c"
|
||||
{
|
||||
cs = JSON_integer_start;
|
||||
}
|
||||
|
||||
#line 262 "parser.rl"
|
||||
#line 288 "parser.rl"
|
||||
json->memo = p;
|
||||
|
||||
#line 764 "parser.c"
|
||||
#line 790 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -794,14 +820,14 @@ case 3:
|
|||
goto st0;
|
||||
goto tr4;
|
||||
tr4:
|
||||
#line 252 "parser.rl"
|
||||
#line 278 "parser.rl"
|
||||
{ p--; {p++; cs = 5; goto _out;} }
|
||||
goto st5;
|
||||
st5:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof5;
|
||||
case 5:
|
||||
#line 805 "parser.c"
|
||||
#line 831 "parser.c"
|
||||
goto st0;
|
||||
st4:
|
||||
if ( ++p == pe )
|
||||
|
@ -820,7 +846,7 @@ case 4:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 264 "parser.rl"
|
||||
#line 290 "parser.rl"
|
||||
|
||||
if (cs >= JSON_integer_first_final) {
|
||||
long len = p - json->memo;
|
||||
|
@ -832,7 +858,7 @@ case 4:
|
|||
}
|
||||
|
||||
|
||||
#line 836 "parser.c"
|
||||
#line 862 "parser.c"
|
||||
static const int JSON_float_start = 1;
|
||||
static const int JSON_float_first_final = 10;
|
||||
static const int JSON_float_error = 0;
|
||||
|
@ -840,7 +866,7 @@ static const int JSON_float_error = 0;
|
|||
static const int JSON_float_en_main = 1;
|
||||
|
||||
|
||||
#line 286 "parser.rl"
|
||||
#line 312 "parser.rl"
|
||||
|
||||
|
||||
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
|
@ -848,15 +874,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
int cs = EVIL;
|
||||
|
||||
|
||||
#line 852 "parser.c"
|
||||
#line 878 "parser.c"
|
||||
{
|
||||
cs = JSON_float_start;
|
||||
}
|
||||
|
||||
#line 293 "parser.rl"
|
||||
#line 319 "parser.rl"
|
||||
json->memo = p;
|
||||
|
||||
#line 860 "parser.c"
|
||||
#line 886 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -914,14 +940,14 @@ case 5:
|
|||
goto st0;
|
||||
goto tr7;
|
||||
tr7:
|
||||
#line 280 "parser.rl"
|
||||
#line 306 "parser.rl"
|
||||
{ p--; {p++; cs = 10; goto _out;} }
|
||||
goto st10;
|
||||
st10:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof10;
|
||||
case 10:
|
||||
#line 925 "parser.c"
|
||||
#line 951 "parser.c"
|
||||
goto st0;
|
||||
st6:
|
||||
if ( ++p == pe )
|
||||
|
@ -982,7 +1008,7 @@ case 9:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 295 "parser.rl"
|
||||
#line 321 "parser.rl"
|
||||
|
||||
if (cs >= JSON_float_first_final) {
|
||||
long len = p - json->memo;
|
||||
|
@ -995,7 +1021,7 @@ case 9:
|
|||
|
||||
|
||||
|
||||
#line 999 "parser.c"
|
||||
#line 1025 "parser.c"
|
||||
static const int JSON_array_start = 1;
|
||||
static const int JSON_array_first_final = 17;
|
||||
static const int JSON_array_error = 0;
|
||||
|
@ -1003,7 +1029,7 @@ static const int JSON_array_error = 0;
|
|||
static const int JSON_array_en_main = 1;
|
||||
|
||||
|
||||
#line 331 "parser.rl"
|
||||
#line 357 "parser.rl"
|
||||
|
||||
|
||||
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
|
@ -1017,14 +1043,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
*result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
|
||||
|
||||
|
||||
#line 1021 "parser.c"
|
||||
#line 1047 "parser.c"
|
||||
{
|
||||
cs = JSON_array_start;
|
||||
}
|
||||
|
||||
#line 344 "parser.rl"
|
||||
#line 370 "parser.rl"
|
||||
|
||||
#line 1028 "parser.c"
|
||||
#line 1054 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -1063,10 +1089,10 @@ case 2:
|
|||
goto st2;
|
||||
goto st0;
|
||||
tr2:
|
||||
#line 312 "parser.rl"
|
||||
#line 338 "parser.rl"
|
||||
{
|
||||
VALUE v = Qnil;
|
||||
char *np = JSON_parse_value(json, p, pe, &v);
|
||||
char *np = JSON_parse_value(json, p, pe, &v);
|
||||
if (np == NULL) {
|
||||
p--; {p++; cs = 3; goto _out;}
|
||||
} else {
|
||||
|
@ -1079,7 +1105,7 @@ st3:
|
|||
if ( ++p == pe )
|
||||
goto _test_eof3;
|
||||
case 3:
|
||||
#line 1083 "parser.c"
|
||||
#line 1109 "parser.c"
|
||||
switch( (*p) ) {
|
||||
case 13: goto st3;
|
||||
case 32: goto st3;
|
||||
|
@ -1179,14 +1205,14 @@ case 12:
|
|||
goto st3;
|
||||
goto st12;
|
||||
tr4:
|
||||
#line 323 "parser.rl"
|
||||
#line 349 "parser.rl"
|
||||
{ p--; {p++; cs = 17; goto _out;} }
|
||||
goto st17;
|
||||
st17:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof17;
|
||||
case 17:
|
||||
#line 1190 "parser.c"
|
||||
#line 1216 "parser.c"
|
||||
goto st0;
|
||||
st13:
|
||||
if ( ++p == pe )
|
||||
|
@ -1242,73 +1268,88 @@ case 16:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 345 "parser.rl"
|
||||
#line 371 "parser.rl"
|
||||
|
||||
if(cs >= JSON_array_first_final) {
|
||||
return p + 1;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE json_string_unescape(char *p, char *pe)
|
||||
static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
|
||||
{
|
||||
VALUE result = rb_str_buf_new(pe - p + 1);
|
||||
char *p = string, *pe = string, *unescape;
|
||||
int unescape_len;
|
||||
|
||||
while (p < pe) {
|
||||
if (*p == '\\') {
|
||||
p++;
|
||||
if (p >= pe) return Qnil; /* raise an exception later, \ at end */
|
||||
switch (*p) {
|
||||
case '"':
|
||||
case '\\':
|
||||
rb_str_buf_cat(result, p, 1);
|
||||
p++;
|
||||
break;
|
||||
case 'b':
|
||||
rb_str_buf_cat2(result, "\b");
|
||||
p++;
|
||||
break;
|
||||
case 'f':
|
||||
rb_str_buf_cat2(result, "\f");
|
||||
p++;
|
||||
break;
|
||||
while (pe < stringEnd) {
|
||||
if (*pe == '\\') {
|
||||
unescape = (char *) "?";
|
||||
unescape_len = 1;
|
||||
if (pe > p) rb_str_buf_cat(result, p, pe - p);
|
||||
switch (*++pe) {
|
||||
case 'n':
|
||||
rb_str_buf_cat2(result, "\n");
|
||||
p++;
|
||||
unescape = (char *) "\n";
|
||||
break;
|
||||
case 'r':
|
||||
rb_str_buf_cat2(result, "\r");
|
||||
p++;
|
||||
unescape = (char *) "\r";
|
||||
break;
|
||||
case 't':
|
||||
rb_str_buf_cat2(result, "\t");
|
||||
p++;
|
||||
unescape = (char *) "\t";
|
||||
break;
|
||||
case '"':
|
||||
unescape = (char *) "\"";
|
||||
break;
|
||||
case '\\':
|
||||
unescape = (char *) "\\";
|
||||
break;
|
||||
case 'b':
|
||||
unescape = (char *) "\b";
|
||||
break;
|
||||
case 'f':
|
||||
unescape = (char *) "\f";
|
||||
break;
|
||||
case 'u':
|
||||
if (p > pe - 4) {
|
||||
if (pe > stringEnd - 4) {
|
||||
return Qnil;
|
||||
} else {
|
||||
p = JSON_convert_UTF16_to_UTF8(result, p, pe, strictConversion);
|
||||
char buf[4];
|
||||
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
|
||||
pe += 3;
|
||||
if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
|
||||
pe++;
|
||||
if (pe > stringEnd - 6) return Qnil;
|
||||
if (pe[0] == '\\' && pe[1] == 'u') {
|
||||
UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
|
||||
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
||||
| (sur & 0x3FF));
|
||||
pe += 5;
|
||||
} else {
|
||||
unescape = (char *) "?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
unescape_len = convert_UTF32_to_UTF8(buf, ch);
|
||||
unescape = buf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rb_str_buf_cat(result, p, 1);
|
||||
p++;
|
||||
break;
|
||||
p = pe;
|
||||
continue;
|
||||
}
|
||||
rb_str_buf_cat(result, unescape, unescape_len);
|
||||
p = ++pe;
|
||||
} else {
|
||||
char *q = p;
|
||||
while (*q != '\\' && q < pe) q++;
|
||||
rb_str_buf_cat(result, p, q - p);
|
||||
p = q;
|
||||
pe++;
|
||||
}
|
||||
}
|
||||
rb_str_buf_cat(result, p, pe - p);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#line 1312 "parser.c"
|
||||
#line 1353 "parser.c"
|
||||
static const int JSON_string_start = 1;
|
||||
static const int JSON_string_first_final = 8;
|
||||
static const int JSON_string_error = 0;
|
||||
|
@ -1316,24 +1357,24 @@ static const int JSON_string_error = 0;
|
|||
static const int JSON_string_en_main = 1;
|
||||
|
||||
|
||||
#line 429 "parser.rl"
|
||||
#line 470 "parser.rl"
|
||||
|
||||
|
||||
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
|
||||
*result = rb_str_new("", 0);
|
||||
*result = rb_str_buf_new(0);
|
||||
|
||||
#line 1329 "parser.c"
|
||||
#line 1370 "parser.c"
|
||||
{
|
||||
cs = JSON_string_start;
|
||||
}
|
||||
|
||||
#line 437 "parser.rl"
|
||||
#line 478 "parser.rl"
|
||||
json->memo = p;
|
||||
|
||||
#line 1337 "parser.c"
|
||||
#line 1378 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -1358,9 +1399,9 @@ case 2:
|
|||
goto st0;
|
||||
goto st2;
|
||||
tr2:
|
||||
#line 415 "parser.rl"
|
||||
#line 456 "parser.rl"
|
||||
{
|
||||
*result = json_string_unescape(json->memo + 1, p);
|
||||
*result = json_string_unescape(*result, json->memo + 1, p);
|
||||
if (NIL_P(*result)) {
|
||||
p--;
|
||||
{p++; cs = 8; goto _out;}
|
||||
|
@ -1369,14 +1410,14 @@ tr2:
|
|||
{p = (( p + 1))-1;}
|
||||
}
|
||||
}
|
||||
#line 426 "parser.rl"
|
||||
#line 467 "parser.rl"
|
||||
{ p--; {p++; cs = 8; goto _out;} }
|
||||
goto st8;
|
||||
st8:
|
||||
if ( ++p == pe )
|
||||
goto _test_eof8;
|
||||
case 8:
|
||||
#line 1380 "parser.c"
|
||||
#line 1421 "parser.c"
|
||||
goto st0;
|
||||
st3:
|
||||
if ( ++p == pe )
|
||||
|
@ -1452,8 +1493,11 @@ case 7:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 439 "parser.rl"
|
||||
#line 480 "parser.rl"
|
||||
|
||||
if (json->symbolize_names && json->parsing_name) {
|
||||
*result = rb_str_intern(*result);
|
||||
}
|
||||
if (cs >= JSON_string_first_final) {
|
||||
return p + 1;
|
||||
} else {
|
||||
|
@ -1463,7 +1507,7 @@ case 7:
|
|||
|
||||
|
||||
|
||||
#line 1467 "parser.c"
|
||||
#line 1511 "parser.c"
|
||||
static const int JSON_start = 1;
|
||||
static const int JSON_first_final = 10;
|
||||
static const int JSON_error = 0;
|
||||
|
@ -1471,10 +1515,10 @@ static const int JSON_error = 0;
|
|||
static const int JSON_en_main = 1;
|
||||
|
||||
|
||||
#line 473 "parser.rl"
|
||||
#line 517 "parser.rl"
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* Document-class: JSON::Ext::Parser
|
||||
*
|
||||
* This is the JSON parser implemented as a C extension. It can be configured
|
||||
|
@ -1486,6 +1530,54 @@ static const int JSON_en_main = 1;
|
|||
*
|
||||
*/
|
||||
|
||||
static VALUE convert_encoding(VALUE source)
|
||||
{
|
||||
char *ptr = RSTRING_PTR(source);
|
||||
long len = RSTRING_LEN(source);
|
||||
if (len < 2) {
|
||||
rb_raise(eParserError, "A JSON text must at least contain two octets!");
|
||||
}
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
{
|
||||
VALUE encoding = rb_funcall(source, i_encoding, 0);
|
||||
if (encoding == CEncoding_ASCII_8BIT) {
|
||||
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32BE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16BE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32LE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16LE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else {
|
||||
FORCE_UTF8(source);
|
||||
}
|
||||
} else {
|
||||
source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source);
|
||||
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source);
|
||||
}
|
||||
#endif
|
||||
return source;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: new(source, opts => {})
|
||||
*
|
||||
|
@ -1503,6 +1595,9 @@ static const int JSON_en_main = 1;
|
|||
* * *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.
|
||||
* * *symbolize_names*: If set to true, returns symbols for the names
|
||||
* (keys) in a JSON object. Otherwise strings are returned, which is also
|
||||
* the default.
|
||||
* * *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.
|
||||
|
@ -1514,21 +1609,18 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
char *ptr;
|
||||
long len;
|
||||
VALUE source, opts;
|
||||
GET_STRUCT;
|
||||
GET_PARSER;
|
||||
rb_scan_args(argc, argv, "11", &source, &opts);
|
||||
source = StringValue(source);
|
||||
source = convert_encoding(StringValue(source));
|
||||
ptr = RSTRING_PTR(source);
|
||||
len = RSTRING_LEN(source);
|
||||
if (len < 2) {
|
||||
rb_raise(eParserError, "A JSON text must at least contain two octets!");
|
||||
}
|
||||
if (!NIL_P(opts)) {
|
||||
opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
|
||||
if (NIL_P(opts)) {
|
||||
rb_raise(rb_eArgError, "opts needs to be like a hash");
|
||||
} else {
|
||||
VALUE tmp = ID2SYM(i_max_nesting);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE max_nesting = rb_hash_aref(opts, tmp);
|
||||
if (RTEST(max_nesting)) {
|
||||
Check_Type(max_nesting, T_FIXNUM);
|
||||
|
@ -1540,14 +1632,21 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
json->max_nesting = 19;
|
||||
}
|
||||
tmp = ID2SYM(i_allow_nan);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE allow_nan = rb_hash_aref(opts, tmp);
|
||||
json->allow_nan = RTEST(allow_nan) ? 1 : 0;
|
||||
} else {
|
||||
json->allow_nan = 0;
|
||||
}
|
||||
tmp = ID2SYM(i_symbolize_names);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE symbolize_names = rb_hash_aref(opts, tmp);
|
||||
json->symbolize_names = RTEST(symbolize_names) ? 1 : 0;
|
||||
} else {
|
||||
json->symbolize_names = 0;
|
||||
}
|
||||
tmp = ID2SYM(i_create_additions);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE create_additions = rb_hash_aref(opts, tmp);
|
||||
if (RTEST(create_additions)) {
|
||||
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
||||
|
@ -1558,13 +1657,13 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
||||
}
|
||||
tmp = ID2SYM(i_object_class);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->object_class = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->object_class = Qnil;
|
||||
}
|
||||
tmp = ID2SYM(i_array_class);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->array_class = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->array_class = Qnil;
|
||||
|
@ -1578,18 +1677,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
json->array_class = Qnil;
|
||||
}
|
||||
json->current_nesting = 0;
|
||||
/*
|
||||
Convert these?
|
||||
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
}
|
||||
*/
|
||||
json->len = len;
|
||||
json->source = ptr;
|
||||
json->Vsource = source;
|
||||
|
@ -1607,19 +1694,19 @@ static VALUE cParser_parse(VALUE self)
|
|||
char *p, *pe;
|
||||
int cs = EVIL;
|
||||
VALUE result = Qnil;
|
||||
GET_STRUCT;
|
||||
GET_PARSER;
|
||||
|
||||
|
||||
#line 1614 "parser.c"
|
||||
#line 1701 "parser.c"
|
||||
{
|
||||
cs = JSON_start;
|
||||
}
|
||||
|
||||
#line 611 "parser.rl"
|
||||
#line 698 "parser.rl"
|
||||
p = json->source;
|
||||
pe = p + json->len;
|
||||
|
||||
#line 1623 "parser.c"
|
||||
#line 1710 "parser.c"
|
||||
{
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
|
@ -1675,7 +1762,7 @@ case 5:
|
|||
goto st1;
|
||||
goto st5;
|
||||
tr3:
|
||||
#line 462 "parser.rl"
|
||||
#line 506 "parser.rl"
|
||||
{
|
||||
char *np;
|
||||
json->current_nesting = 1;
|
||||
|
@ -1684,7 +1771,7 @@ tr3:
|
|||
}
|
||||
goto st10;
|
||||
tr4:
|
||||
#line 455 "parser.rl"
|
||||
#line 499 "parser.rl"
|
||||
{
|
||||
char *np;
|
||||
json->current_nesting = 1;
|
||||
|
@ -1696,7 +1783,7 @@ st10:
|
|||
if ( ++p == pe )
|
||||
goto _test_eof10;
|
||||
case 10:
|
||||
#line 1700 "parser.c"
|
||||
#line 1787 "parser.c"
|
||||
switch( (*p) ) {
|
||||
case 13: goto st10;
|
||||
case 32: goto st10;
|
||||
|
@ -1753,16 +1840,17 @@ case 9:
|
|||
_out: {}
|
||||
}
|
||||
|
||||
#line 614 "parser.rl"
|
||||
#line 701 "parser.rl"
|
||||
|
||||
if (cs >= JSON_first_final && p == pe) {
|
||||
return result;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
inline static JSON_Parser *JSON_allocate()
|
||||
static JSON_Parser *JSON_allocate()
|
||||
{
|
||||
JSON_Parser *json = ALLOC(JSON_Parser);
|
||||
MEMZERO(json, JSON_Parser, 1);
|
||||
|
@ -1796,7 +1884,7 @@ static VALUE cJSON_parser_s_allocate(VALUE klass)
|
|||
*/
|
||||
static VALUE cParser_source(VALUE self)
|
||||
{
|
||||
GET_STRUCT;
|
||||
GET_PARSER;
|
||||
return rb_str_dup(json->Vsource);
|
||||
}
|
||||
|
||||
|
@ -1824,6 +1912,23 @@ void Init_parser()
|
|||
i_chr = rb_intern("chr");
|
||||
i_max_nesting = rb_intern("max_nesting");
|
||||
i_allow_nan = rb_intern("allow_nan");
|
||||
i_symbolize_names = rb_intern("symbolize_names");
|
||||
i_object_class = rb_intern("object_class");
|
||||
i_array_class = rb_intern("array_class");
|
||||
i_key_p = rb_intern("key?");
|
||||
i_deep_const_get = rb_intern("deep_const_get");
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
|
||||
CEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
|
||||
CEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
|
||||
CEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
|
||||
CEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
|
||||
CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
|
||||
i_encoding = rb_intern("encoding");
|
||||
i_encode = rb_intern("encode");
|
||||
i_encode_bang = rb_intern("encode!");
|
||||
i_force_encoding = rb_intern("force_encoding");
|
||||
#else
|
||||
i_iconv = rb_intern("iconv");
|
||||
#endif
|
||||
}
|
71
ext/json/parser/parser.h
Normal file
71
ext/json/parser/parser.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#ifndef _PARSER_H_
|
||||
#define _PARSER_H_
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
#if HAVE_RE_H
|
||||
#include "re.h"
|
||||
#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 option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
|
||||
|
||||
/* unicode */
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
typedef struct JSON_ParserStruct {
|
||||
VALUE Vsource;
|
||||
char *source;
|
||||
long len;
|
||||
char *memo;
|
||||
VALUE create_id;
|
||||
int max_nesting;
|
||||
int current_nesting;
|
||||
int allow_nan;
|
||||
int parsing_name;
|
||||
int symbolize_names;
|
||||
VALUE object_class;
|
||||
VALUE array_class;
|
||||
} JSON_Parser;
|
||||
|
||||
#define GET_PARSER \
|
||||
JSON_Parser *json; \
|
||||
Data_Get_Struct(self, JSON_Parser, json)
|
||||
|
||||
#define MinusInfinity "-Infinity"
|
||||
#define EVIL 0x666
|
||||
|
||||
static UTF32 unescape_unicode(const unsigned char *p);
|
||||
static int convert_UTF32_to_UTF8(char *buf, UTF32 ch);
|
||||
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd);
|
||||
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static VALUE convert_encoding(VALUE source);
|
||||
static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE cParser_parse(VALUE self);
|
||||
static JSON_Parser *JSON_allocate();
|
||||
static void JSON_mark(JSON_Parser *json);
|
||||
static void JSON_free(JSON_Parser *json);
|
||||
static VALUE cJSON_parser_s_allocate(VALUE klass);
|
||||
static VALUE cParser_source(VALUE self);
|
||||
|
||||
#endif
|
|
@ -1,59 +1,83 @@
|
|||
#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
|
||||
#include "parser.h"
|
||||
|
||||
#define EVIL 0x666
|
||||
/* unicode */
|
||||
|
||||
#ifndef RHASH_TBL
|
||||
#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
|
||||
#endif
|
||||
static const char digit_values[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
|
||||
-1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
static UTF32 unescape_unicode(const unsigned char *p)
|
||||
{
|
||||
char b;
|
||||
UTF32 result = 0;
|
||||
b = digit_values[p[0]];
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
result = (result << 4) | b;
|
||||
b = digit_values[p[1]];
|
||||
result = (result << 4) | b;
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
b = digit_values[p[2]];
|
||||
result = (result << 4) | b;
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
b = digit_values[p[3]];
|
||||
result = (result << 4) | b;
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
|
||||
{
|
||||
int len = 1;
|
||||
if (ch <= 0x7F) {
|
||||
buf[0] = (char) ch;
|
||||
} else if (ch <= 0x07FF) {
|
||||
buf[0] = (char) ((ch >> 6) | 0xC0);
|
||||
buf[1] = (char) ((ch & 0x3F) | 0x80);
|
||||
len++;
|
||||
} else if (ch <= 0xFFFF) {
|
||||
buf[0] = (char) ((ch >> 12) | 0xE0);
|
||||
buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
|
||||
buf[2] = (char) ((ch & 0x3F) | 0x80);
|
||||
len += 2;
|
||||
} else if (ch <= 0x1fffff) {
|
||||
buf[0] =(char) ((ch >> 18) | 0xF0);
|
||||
buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
|
||||
buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
|
||||
buf[3] =(char) ((ch & 0x3F) | 0x80);
|
||||
len += 3;
|
||||
} else {
|
||||
buf[0] = '?';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
#include "ruby/encoding.h"
|
||||
#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
|
||||
static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE,
|
||||
CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE;
|
||||
static ID i_encoding, i_encode, i_encode_bang, i_force_encoding;
|
||||
#else
|
||||
#define FORCE_UTF8(obj)
|
||||
static ID i_iconv;
|
||||
#endif
|
||||
|
||||
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
|
||||
static VALUE CNaN, CInfinity, CMinusInfinity;
|
||||
|
||||
static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
|
||||
i_chr, i_max_nesting, i_allow_nan, i_object_class, i_array_class;
|
||||
|
||||
#define MinusInfinity "-Infinity"
|
||||
|
||||
typedef struct JSON_ParserStruct {
|
||||
VALUE Vsource;
|
||||
char *source;
|
||||
long len;
|
||||
char *memo;
|
||||
VALUE create_id;
|
||||
int max_nesting;
|
||||
int current_nesting;
|
||||
int allow_nan;
|
||||
VALUE object_class;
|
||||
VALUE array_class;
|
||||
} JSON_Parser;
|
||||
|
||||
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
|
||||
#define GET_STRUCT \
|
||||
JSON_Parser *json; \
|
||||
Data_Get_Struct(self, JSON_Parser, json);
|
||||
i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class,
|
||||
i_array_class, i_key_p, i_deep_const_get;
|
||||
|
||||
%%{
|
||||
machine JSON_common;
|
||||
|
@ -91,7 +115,7 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
|
||||
action parse_value {
|
||||
VALUE v = Qnil;
|
||||
char *np = JSON_parse_value(json, fpc, pe, &v);
|
||||
char *np = JSON_parse_value(json, fpc, pe, &v);
|
||||
if (np == NULL) {
|
||||
fhold; fbreak;
|
||||
} else {
|
||||
|
@ -101,7 +125,9 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
}
|
||||
|
||||
action parse_name {
|
||||
json->parsing_name = 1;
|
||||
char *np = JSON_parse_string(json, fpc, pe, &last_name);
|
||||
json->parsing_name = 0;
|
||||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
|
@ -135,7 +161,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
if (RTEST(json->create_id)) {
|
||||
VALUE klassname = rb_hash_aref(*result, json->create_id);
|
||||
if (!NIL_P(klassname)) {
|
||||
VALUE klass = rb_path2class(StringValueCStr(klassname));
|
||||
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
|
||||
if RTEST(rb_funcall(klass, i_json_creatable_p, 0)) {
|
||||
*result = rb_funcall(klass, i_json_create, 1, *result);
|
||||
}
|
||||
|
@ -199,7 +225,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
fhold; fbreak;
|
||||
}
|
||||
|
||||
action parse_array {
|
||||
action parse_array {
|
||||
char *np;
|
||||
json->current_nesting++;
|
||||
np = JSON_parse_array(json, fpc, pe, result);
|
||||
|
@ -207,7 +233,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
action parse_object {
|
||||
action parse_object {
|
||||
char *np;
|
||||
json->current_nesting++;
|
||||
np = JSON_parse_object(json, fpc, pe, result);
|
||||
|
@ -311,7 +337,7 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
|
||||
action parse_value {
|
||||
VALUE v = Qnil;
|
||||
char *np = JSON_parse_value(json, fpc, pe, &v);
|
||||
char *np = JSON_parse_value(json, fpc, pe, &v);
|
||||
if (np == NULL) {
|
||||
fhold; fbreak;
|
||||
} else {
|
||||
|
@ -347,62 +373,77 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
return p + 1;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE json_string_unescape(char *p, char *pe)
|
||||
static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
|
||||
{
|
||||
VALUE result = rb_str_buf_new(pe - p + 1);
|
||||
char *p = string, *pe = string, *unescape;
|
||||
int unescape_len;
|
||||
|
||||
while (p < pe) {
|
||||
if (*p == '\\') {
|
||||
p++;
|
||||
if (p >= pe) return Qnil; /* raise an exception later, \ at end */
|
||||
switch (*p) {
|
||||
case '"':
|
||||
case '\\':
|
||||
rb_str_buf_cat(result, p, 1);
|
||||
p++;
|
||||
break;
|
||||
case 'b':
|
||||
rb_str_buf_cat2(result, "\b");
|
||||
p++;
|
||||
break;
|
||||
case 'f':
|
||||
rb_str_buf_cat2(result, "\f");
|
||||
p++;
|
||||
break;
|
||||
while (pe < stringEnd) {
|
||||
if (*pe == '\\') {
|
||||
unescape = (char *) "?";
|
||||
unescape_len = 1;
|
||||
if (pe > p) rb_str_buf_cat(result, p, pe - p);
|
||||
switch (*++pe) {
|
||||
case 'n':
|
||||
rb_str_buf_cat2(result, "\n");
|
||||
p++;
|
||||
unescape = (char *) "\n";
|
||||
break;
|
||||
case 'r':
|
||||
rb_str_buf_cat2(result, "\r");
|
||||
p++;
|
||||
unescape = (char *) "\r";
|
||||
break;
|
||||
case 't':
|
||||
rb_str_buf_cat2(result, "\t");
|
||||
p++;
|
||||
unescape = (char *) "\t";
|
||||
break;
|
||||
case '"':
|
||||
unescape = (char *) "\"";
|
||||
break;
|
||||
case '\\':
|
||||
unescape = (char *) "\\";
|
||||
break;
|
||||
case 'b':
|
||||
unescape = (char *) "\b";
|
||||
break;
|
||||
case 'f':
|
||||
unescape = (char *) "\f";
|
||||
break;
|
||||
case 'u':
|
||||
if (p > pe - 4) {
|
||||
if (pe > stringEnd - 4) {
|
||||
return Qnil;
|
||||
} else {
|
||||
p = JSON_convert_UTF16_to_UTF8(result, p, pe, strictConversion);
|
||||
char buf[4];
|
||||
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
|
||||
pe += 3;
|
||||
if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
|
||||
pe++;
|
||||
if (pe > stringEnd - 6) return Qnil;
|
||||
if (pe[0] == '\\' && pe[1] == 'u') {
|
||||
UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
|
||||
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
||||
| (sur & 0x3FF));
|
||||
pe += 5;
|
||||
} else {
|
||||
unescape = (char *) "?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
unescape_len = convert_UTF32_to_UTF8(buf, ch);
|
||||
unescape = buf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rb_str_buf_cat(result, p, 1);
|
||||
p++;
|
||||
break;
|
||||
p = pe;
|
||||
continue;
|
||||
}
|
||||
rb_str_buf_cat(result, unescape, unescape_len);
|
||||
p = ++pe;
|
||||
} else {
|
||||
char *q = p;
|
||||
while (*q != '\\' && q < pe) q++;
|
||||
rb_str_buf_cat(result, p, q - p);
|
||||
p = q;
|
||||
pe++;
|
||||
}
|
||||
}
|
||||
rb_str_buf_cat(result, p, pe - p);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -413,7 +454,7 @@ static VALUE json_string_unescape(char *p, char *pe)
|
|||
write data;
|
||||
|
||||
action parse_string {
|
||||
*result = json_string_unescape(json->memo + 1, p);
|
||||
*result = json_string_unescape(*result, json->memo + 1, p);
|
||||
if (NIL_P(*result)) {
|
||||
fhold;
|
||||
fbreak;
|
||||
|
@ -432,11 +473,14 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
{
|
||||
int cs = EVIL;
|
||||
|
||||
*result = rb_str_new("", 0);
|
||||
*result = rb_str_buf_new(0);
|
||||
%% write init;
|
||||
json->memo = p;
|
||||
%% write exec;
|
||||
|
||||
if (json->symbolize_names && json->parsing_name) {
|
||||
*result = rb_str_intern(*result);
|
||||
}
|
||||
if (cs >= JSON_string_first_final) {
|
||||
return p + 1;
|
||||
} else {
|
||||
|
@ -472,7 +516,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
) ignore*;
|
||||
}%%
|
||||
|
||||
/*
|
||||
/*
|
||||
* Document-class: JSON::Ext::Parser
|
||||
*
|
||||
* This is the JSON parser implemented as a C extension. It can be configured
|
||||
|
@ -484,6 +528,54 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
*
|
||||
*/
|
||||
|
||||
static VALUE convert_encoding(VALUE source)
|
||||
{
|
||||
char *ptr = RSTRING_PTR(source);
|
||||
long len = RSTRING_LEN(source);
|
||||
if (len < 2) {
|
||||
rb_raise(eParserError, "A JSON text must at least contain two octets!");
|
||||
}
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
{
|
||||
VALUE encoding = rb_funcall(source, i_encoding, 0);
|
||||
if (encoding == CEncoding_ASCII_8BIT) {
|
||||
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32BE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16BE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32LE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
|
||||
source = rb_str_dup(source);
|
||||
rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16LE);
|
||||
source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
|
||||
} else {
|
||||
FORCE_UTF8(source);
|
||||
}
|
||||
} else {
|
||||
source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source);
|
||||
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source);
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
|
||||
source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source);
|
||||
}
|
||||
#endif
|
||||
return source;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: new(source, opts => {})
|
||||
*
|
||||
|
@ -501,6 +593,9 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|||
* * *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.
|
||||
* * *symbolize_names*: If set to true, returns symbols for the names
|
||||
* (keys) in a JSON object. Otherwise strings are returned, which is also
|
||||
* the default.
|
||||
* * *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.
|
||||
|
@ -512,21 +607,18 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
char *ptr;
|
||||
long len;
|
||||
VALUE source, opts;
|
||||
GET_STRUCT;
|
||||
GET_PARSER;
|
||||
rb_scan_args(argc, argv, "11", &source, &opts);
|
||||
source = StringValue(source);
|
||||
source = convert_encoding(StringValue(source));
|
||||
ptr = RSTRING_PTR(source);
|
||||
len = RSTRING_LEN(source);
|
||||
if (len < 2) {
|
||||
rb_raise(eParserError, "A JSON text must at least contain two octets!");
|
||||
}
|
||||
if (!NIL_P(opts)) {
|
||||
opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
|
||||
if (NIL_P(opts)) {
|
||||
rb_raise(rb_eArgError, "opts needs to be like a hash");
|
||||
} else {
|
||||
VALUE tmp = ID2SYM(i_max_nesting);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE max_nesting = rb_hash_aref(opts, tmp);
|
||||
if (RTEST(max_nesting)) {
|
||||
Check_Type(max_nesting, T_FIXNUM);
|
||||
|
@ -538,14 +630,21 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
json->max_nesting = 19;
|
||||
}
|
||||
tmp = ID2SYM(i_allow_nan);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE allow_nan = rb_hash_aref(opts, tmp);
|
||||
json->allow_nan = RTEST(allow_nan) ? 1 : 0;
|
||||
} else {
|
||||
json->allow_nan = 0;
|
||||
}
|
||||
tmp = ID2SYM(i_symbolize_names);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE symbolize_names = rb_hash_aref(opts, tmp);
|
||||
json->symbolize_names = RTEST(symbolize_names) ? 1 : 0;
|
||||
} else {
|
||||
json->symbolize_names = 0;
|
||||
}
|
||||
tmp = ID2SYM(i_create_additions);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE create_additions = rb_hash_aref(opts, tmp);
|
||||
if (RTEST(create_additions)) {
|
||||
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
||||
|
@ -556,13 +655,13 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
||||
}
|
||||
tmp = ID2SYM(i_object_class);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->object_class = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->object_class = Qnil;
|
||||
}
|
||||
tmp = ID2SYM(i_array_class);
|
||||
if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->array_class = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->array_class = Qnil;
|
||||
|
@ -576,18 +675,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|||
json->array_class = Qnil;
|
||||
}
|
||||
json->current_nesting = 0;
|
||||
/*
|
||||
Convert these?
|
||||
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
|
||||
rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
|
||||
}
|
||||
*/
|
||||
json->len = len;
|
||||
json->source = ptr;
|
||||
json->Vsource = source;
|
||||
|
@ -605,7 +692,7 @@ static VALUE cParser_parse(VALUE self)
|
|||
char *p, *pe;
|
||||
int cs = EVIL;
|
||||
VALUE result = Qnil;
|
||||
GET_STRUCT;
|
||||
GET_PARSER;
|
||||
|
||||
%% write init;
|
||||
p = json->source;
|
||||
|
@ -616,10 +703,11 @@ static VALUE cParser_parse(VALUE self)
|
|||
return result;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
inline static JSON_Parser *JSON_allocate()
|
||||
static JSON_Parser *JSON_allocate()
|
||||
{
|
||||
JSON_Parser *json = ALLOC(JSON_Parser);
|
||||
MEMZERO(json, JSON_Parser, 1);
|
||||
|
@ -653,7 +741,7 @@ static VALUE cJSON_parser_s_allocate(VALUE klass)
|
|||
*/
|
||||
static VALUE cParser_source(VALUE self)
|
||||
{
|
||||
GET_STRUCT;
|
||||
GET_PARSER;
|
||||
return rb_str_dup(json->Vsource);
|
||||
}
|
||||
|
||||
|
@ -681,6 +769,23 @@ void Init_parser()
|
|||
i_chr = rb_intern("chr");
|
||||
i_max_nesting = rb_intern("max_nesting");
|
||||
i_allow_nan = rb_intern("allow_nan");
|
||||
i_symbolize_names = rb_intern("symbolize_names");
|
||||
i_object_class = rb_intern("object_class");
|
||||
i_array_class = rb_intern("array_class");
|
||||
i_key_p = rb_intern("key?");
|
||||
i_deep_const_get = rb_intern("deep_const_get");
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
|
||||
CEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
|
||||
CEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
|
||||
CEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
|
||||
CEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
|
||||
CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
|
||||
i_encoding = rb_intern("encoding");
|
||||
i_encode = rb_intern("encode");
|
||||
i_encode_bang = rb_intern("encode!");
|
||||
i_force_encoding = rb_intern("force_encoding");
|
||||
#else
|
||||
i_iconv = rb_intern("iconv");
|
||||
#endif
|
||||
}
|
|
@ -9,6 +9,20 @@ else require 'json'
|
|||
end
|
||||
require 'stringio'
|
||||
|
||||
unless Array.method_defined?(:permutation)
|
||||
begin
|
||||
require 'enumerator'
|
||||
require 'permutation'
|
||||
class Array
|
||||
def permutation
|
||||
Permutation.for(self).to_enum.map { |x| x.project }
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
warn "Skipping permutation tests."
|
||||
end
|
||||
end
|
||||
|
||||
class TC_JSON < Test::Unit::TestCase
|
||||
include JSON
|
||||
|
||||
|
@ -94,22 +108,23 @@ class TC_JSON < Test::Unit::TestCase
|
|||
assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
|
||||
end
|
||||
|
||||
def test_parse_more_complex_arrays
|
||||
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
||||
a.permutation.each do |perm|
|
||||
orig_ary = a
|
||||
json = pretty_generate(orig_ary)
|
||||
assert_equal orig_ary, parse(json)
|
||||
if Array.method_defined?(:permutation)
|
||||
def test_parse_more_complex_arrays
|
||||
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
||||
a.permutation.each do |perm|
|
||||
json = pretty_generate(perm)
|
||||
assert_equal perm, parse(json)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_parse_complex_objects
|
||||
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
||||
a.permutation.each do |perm|
|
||||
s = "a"
|
||||
orig_obj = a.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
|
||||
json = pretty_generate(orig_obj)
|
||||
assert_equal orig_obj, parse(json)
|
||||
def test_parse_complex_objects
|
||||
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
||||
a.permutation.each do |perm|
|
||||
s = "a"
|
||||
orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
|
||||
json = pretty_generate(orig_obj)
|
||||
assert_equal orig_obj, parse(json)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -215,27 +230,27 @@ EOT
|
|||
def test_backslash
|
||||
data = [ '\\.(?i:gif|jpe?g|png)$' ]
|
||||
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
|
||||
assert_equal json, JSON.unparse(data)
|
||||
assert_equal json, JSON.generate(data)
|
||||
assert_equal data, JSON.parse(json)
|
||||
#
|
||||
data = [ '\\"' ]
|
||||
json = '["\\\\\""]'
|
||||
assert_equal json, JSON.unparse(data)
|
||||
assert_equal json, JSON.generate(data)
|
||||
assert_equal data, JSON.parse(json)
|
||||
#
|
||||
json = '["/"]'
|
||||
data = JSON.parse(json)
|
||||
assert_equal ['/'], data
|
||||
assert_equal json, JSON.unparse(data)
|
||||
assert_equal json, JSON.generate(data)
|
||||
#
|
||||
json = '["\""]'
|
||||
data = JSON.parse(json)
|
||||
assert_equal ['"'], data
|
||||
assert_equal json, JSON.unparse(data)
|
||||
assert_equal json, JSON.generate(data)
|
||||
json = '["\\\'"]'
|
||||
data = JSON.parse(json)
|
||||
assert_equal ["'"], data
|
||||
assert_equal '["\'"]', JSON.unparse(data)
|
||||
assert_equal '["\'"]', JSON.generate(data)
|
||||
end
|
||||
|
||||
def test_wrong_inputs
|
||||
|
@ -287,6 +302,13 @@ EOT
|
|||
assert_equal too_deep, ok
|
||||
end
|
||||
|
||||
def test_symbolize_names
|
||||
assert_equal({ "foo" => "bar", "baz" => "quux" },
|
||||
JSON.parse('{"foo":"bar", "baz":"quux"}'))
|
||||
assert_equal({ :foo => "bar", :baz => "quux" },
|
||||
JSON.parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
|
||||
end
|
||||
|
||||
def test_load_dump
|
||||
too_deep = '[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]'
|
||||
assert_equal too_deep, JSON.dump(eval(too_deep))
|
||||
|
@ -302,4 +324,17 @@ EOT
|
|||
JSON.dump(eval(too_deep), output, 20)
|
||||
assert_equal too_deep, output.string
|
||||
end
|
||||
|
||||
def test_big_integers
|
||||
json1 = JSON([orig = (1 << 31) - 1])
|
||||
assert_equal orig, JSON[json1][0]
|
||||
json2 = JSON([orig = 1 << 31])
|
||||
assert_equal orig, JSON[json2][0]
|
||||
json3 = JSON([orig = (1 << 62) - 1])
|
||||
assert_equal orig, JSON[json3][0]
|
||||
json4 = JSON([orig = 1 << 62])
|
||||
assert_equal orig, JSON[json4][0]
|
||||
json5 = JSON([orig = 1 << 64])
|
||||
assert_equal orig, JSON[json5][0]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -95,7 +95,7 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|||
c = C.new
|
||||
assert !C.json_creatable?
|
||||
json = generate(c)
|
||||
assert_raises(ArgumentError) { JSON.parse(json) }
|
||||
assert_raises(ArgumentError, NameError) { JSON.parse(json) }
|
||||
end
|
||||
|
||||
def test_raw_strings
|
||||
|
@ -110,11 +110,9 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|||
json_raw_object = raw.to_json_raw_object
|
||||
hash = { 'json_class' => 'String', 'raw'=> raw_array }
|
||||
assert_equal hash, json_raw_object
|
||||
json_raw = <<EOT.chomp
|
||||
{\"json_class\":\"String\",\"raw\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]}
|
||||
EOT
|
||||
# "
|
||||
assert_equal json_raw, json
|
||||
assert_match /\A\{.*\}\Z/, json
|
||||
assert_match /"json_class":"String"/, json
|
||||
assert_match /"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\]/, json
|
||||
raw_again = JSON.parse(json)
|
||||
assert_equal raw, raw_again
|
||||
end
|
||||
|
|
68
test/json/test_json_encoding.rb
Normal file
68
test/json/test_json_encoding.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env ruby
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
require 'test/unit'
|
||||
case ENV['JSON']
|
||||
when 'pure' then require 'json/pure'
|
||||
when 'ext' then require 'json/ext'
|
||||
else require 'json'
|
||||
end
|
||||
require 'iconv'
|
||||
|
||||
class TC_JSONEncoding < Test::Unit::TestCase
|
||||
include JSON
|
||||
|
||||
def setup
|
||||
@utf_8 = '["© ≠ €!"]'
|
||||
@parsed = [ "© ≠ €!" ]
|
||||
@utf_16_data = Iconv.iconv('utf-16be', 'utf-8', @parsed.first)
|
||||
@generated = '["\u00a9 \u2260 \u20ac!"]'
|
||||
if defined?(::Encoding)
|
||||
@utf_8_ascii_8bit = @utf_8.dup.force_encoding(Encoding::ASCII_8BIT)
|
||||
@utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8)
|
||||
@utf_16be_ascii_8bit = @utf_16be.dup.force_encoding(Encoding::ASCII_8BIT)
|
||||
@utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8)
|
||||
@utf_16le_ascii_8bit = @utf_16le.dup.force_encoding(Encoding::ASCII_8BIT)
|
||||
@utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8)
|
||||
@utf_32be_ascii_8bit = @utf_32be.dup.force_encoding(Encoding::ASCII_8BIT)
|
||||
@utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8)
|
||||
@utf_32le_ascii_8bit = @utf_32le.dup.force_encoding(Encoding::ASCII_8BIT)
|
||||
else
|
||||
@utf_8_ascii_8bit = @utf_8.dup
|
||||
@utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8)
|
||||
@utf_16be_ascii_8bit = @utf_16be.dup
|
||||
@utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8)
|
||||
@utf_16le_ascii_8bit = @utf_16le.dup
|
||||
@utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8)
|
||||
@utf_32be_ascii_8bit = @utf_32be.dup
|
||||
@utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8)
|
||||
@utf_32le_ascii_8bit = @utf_32le.dup
|
||||
end
|
||||
end
|
||||
|
||||
def test_parse
|
||||
assert_equal @parsed, JSON.parse(@utf_8)
|
||||
assert_equal @parsed, JSON.parse(@utf_16be)
|
||||
assert_equal @parsed, JSON.parse(@utf_16le)
|
||||
assert_equal @parsed, JSON.parse(@utf_32be)
|
||||
assert_equal @parsed, JSON.parse(@utf_32le)
|
||||
end
|
||||
|
||||
def test_parse_ascii_8bit
|
||||
assert_equal @parsed, JSON.parse(@utf_8_ascii_8bit)
|
||||
assert_equal @parsed, JSON.parse(@utf_16be_ascii_8bit)
|
||||
assert_equal @parsed, JSON.parse(@utf_16le_ascii_8bit)
|
||||
assert_equal @parsed, JSON.parse(@utf_32be_ascii_8bit)
|
||||
assert_equal @parsed, JSON.parse(@utf_32le_ascii_8bit)
|
||||
end
|
||||
|
||||
def test_generate
|
||||
assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
|
||||
if defined?(::Encoding)
|
||||
assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
|
||||
else
|
||||
# XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only
|
||||
assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) }
|
||||
end
|
||||
end
|
||||
end
|
44
test/json/test_json_generate.rb
Normal file → Executable file
44
test/json/test_json_generate.rb
Normal file → Executable file
|
@ -44,8 +44,8 @@ class TC_JSONGenerate < Test::Unit::TestCase
|
|||
EOT
|
||||
end
|
||||
|
||||
def test_unparse
|
||||
json = unparse(@hash)
|
||||
def test_generate
|
||||
json = generate(@hash)
|
||||
assert_equal(JSON.parse(@json2), JSON.parse(json))
|
||||
parsed_json = parse(json)
|
||||
assert_equal(@hash, parsed_json)
|
||||
|
@ -53,10 +53,11 @@ EOT
|
|||
assert_equal('{"1":2}', json)
|
||||
parsed_json = parse(json)
|
||||
assert_equal({"1"=>2}, parsed_json)
|
||||
assert_raise(GeneratorError) { generate(666) }
|
||||
end
|
||||
|
||||
def test_unparse_pretty
|
||||
json = pretty_unparse(@hash)
|
||||
def test_generate_pretty
|
||||
json = pretty_generate(@hash)
|
||||
assert_equal(JSON.parse(@json3), JSON.parse(json))
|
||||
parsed_json = parse(json)
|
||||
assert_equal(@hash, parsed_json)
|
||||
|
@ -68,38 +69,53 @@ EOT
|
|||
EOT
|
||||
parsed_json = parse(json)
|
||||
assert_equal({"1"=>2}, parsed_json)
|
||||
assert_raise(GeneratorError) { pretty_generate(666) }
|
||||
end
|
||||
|
||||
def test_fast_generate
|
||||
json = fast_generate(@hash)
|
||||
assert_equal(JSON.parse(@json2), JSON.parse(json))
|
||||
parsed_json = parse(json)
|
||||
assert_equal(@hash, parsed_json)
|
||||
json = fast_generate({1=>2})
|
||||
assert_equal('{"1":2}', json)
|
||||
parsed_json = parse(json)
|
||||
assert_equal({"1"=>2}, parsed_json)
|
||||
assert_raise(GeneratorError) { fast_generate(666) }
|
||||
end
|
||||
|
||||
def test_states
|
||||
json = generate({1=>2}, nil)
|
||||
assert_equal('{"1":2}', json)
|
||||
s = JSON.state.new(:check_circular => true)
|
||||
#assert s.check_circular
|
||||
s = JSON.state.new
|
||||
assert s.check_circular?
|
||||
assert s[:check_circular?]
|
||||
h = { 1=>2 }
|
||||
h[3] = h
|
||||
assert_raises(JSON::CircularDatastructure) { generate(h) }
|
||||
assert_raises(JSON::CircularDatastructure) { generate(h, s) }
|
||||
s = JSON.state.new(:check_circular => true)
|
||||
#assert s.check_circular
|
||||
assert_raises(JSON::NestingError) { generate(h) }
|
||||
assert_raises(JSON::NestingError) { generate(h, s) }
|
||||
s = JSON.state.new
|
||||
a = [ 1, 2 ]
|
||||
a << a
|
||||
assert_raises(JSON::CircularDatastructure) { generate(a, s) }
|
||||
assert_raises(JSON::NestingError) { generate(a, s) }
|
||||
assert s.check_circular?
|
||||
assert s[:check_circular?]
|
||||
end
|
||||
|
||||
def test_allow_nan
|
||||
assert_raises(GeneratorError) { generate([JSON::NaN]) }
|
||||
assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
|
||||
assert_equal '[NaN]', fast_generate([JSON::NaN])
|
||||
assert_raises(GeneratorError) { fast_generate([JSON::NaN]) }
|
||||
assert_raises(GeneratorError) { pretty_generate([JSON::NaN]) }
|
||||
assert_equal "[\n NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { generate([JSON::Infinity]) }
|
||||
assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
|
||||
assert_equal '[Infinity]', fast_generate([JSON::Infinity])
|
||||
assert_raises(GeneratorError) { fast_generate([JSON::Infinity]) }
|
||||
assert_raises(GeneratorError) { pretty_generate([JSON::Infinity]) }
|
||||
assert_equal "[\n Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { generate([JSON::MinusInfinity]) }
|
||||
assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
|
||||
assert_equal '[-Infinity]', fast_generate([JSON::MinusInfinity])
|
||||
assert_raises(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
|
||||
assert_raises(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
|
||||
assert_equal "[\n -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
|
||||
end
|
||||
|
|
12
test/json/test_json_rails.rb
Normal file → Executable file
12
test/json/test_json_rails.rb
Normal file → Executable file
|
@ -116,7 +116,7 @@ class TC_JSONRails < Test::Unit::TestCase
|
|||
c = C.new # with rails addition all objects are theoretically creatable
|
||||
assert C.json_creatable?
|
||||
json = generate(c)
|
||||
assert_raises(ArgumentError) { JSON.parse(json) }
|
||||
assert_raises(ArgumentError, NameError) { JSON.parse(json) }
|
||||
end
|
||||
|
||||
def test_raw_strings
|
||||
|
@ -131,16 +131,14 @@ class TC_JSONRails < Test::Unit::TestCase
|
|||
json_raw_object = raw.to_json_raw_object
|
||||
hash = { 'json_class' => 'String', 'raw'=> raw_array }
|
||||
assert_equal hash, json_raw_object
|
||||
json_raw = <<EOT.chomp
|
||||
{\"json_class\":\"String\",\"raw\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]}
|
||||
EOT
|
||||
# "
|
||||
assert_equal json_raw, json
|
||||
assert_match /\A\{.*\}\Z/, json
|
||||
assert_match /"json_class":"String"/, json
|
||||
assert_match /"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\]/, json
|
||||
raw_again = JSON.parse(json)
|
||||
assert_equal raw, raw_again
|
||||
end
|
||||
|
||||
def test_symbol
|
||||
assert_equal '"foo"', JSON(:foo) # we don't want an object here
|
||||
assert_equal '"foo"', :foo.to_json # we don't want an object here
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,22 +19,36 @@ class TC_JSONUnicode < Test::Unit::TestCase
|
|||
assert_equal '" "', ' '.to_json
|
||||
assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
|
||||
utf8 = [ "© ≠ €! \01" ]
|
||||
json = '["© ≠ €! \u0001"]'
|
||||
assert_equal json, utf8.to_json(:ascii_only => false)
|
||||
assert_equal utf8, parse(json)
|
||||
json = '["\u00a9 \u2260 \u20ac! \u0001"]'
|
||||
assert_equal json, utf8.to_json
|
||||
assert_equal json, utf8.to_json(:ascii_only => true)
|
||||
assert_equal utf8, parse(json)
|
||||
utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
|
||||
json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]"
|
||||
assert_equal utf8, parse(json)
|
||||
assert_equal json, utf8.to_json(:ascii_only => false)
|
||||
utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
|
||||
assert_equal utf8, parse(json)
|
||||
json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
|
||||
assert_equal json, utf8.to_json
|
||||
assert_equal json, utf8.to_json(:ascii_only => true)
|
||||
assert_equal utf8, parse(json)
|
||||
utf8 = ['საქართველო']
|
||||
json = '["საქართველო"]'
|
||||
assert_equal json, utf8.to_json(:ascii_only => false)
|
||||
json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
|
||||
assert_equal json, utf8.to_json
|
||||
assert_equal json, utf8.to_json(:ascii_only => true)
|
||||
assert_equal utf8, parse(json)
|
||||
assert_equal '["\\u00c3"]', JSON.generate(["Ã"])
|
||||
assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false)
|
||||
assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true)
|
||||
assert_equal ["€"], JSON.parse('["\u20ac"]')
|
||||
utf8 = ["\xf0\xa0\x80\x81"]
|
||||
json = "[\"\xf0\xa0\x80\x81\"]"
|
||||
assert_equal json, JSON.generate(utf8, :ascii_only => false)
|
||||
assert_equal utf8, JSON.parse(json)
|
||||
json = '["\ud840\udc01"]'
|
||||
assert_equal json, JSON.generate(utf8)
|
||||
assert_equal json, JSON.generate(utf8, :ascii_only => true)
|
||||
assert_equal utf8, JSON.parse(json)
|
||||
end
|
||||
|
||||
|
@ -55,7 +69,7 @@ class TC_JSONUnicode < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
assert_raise(JSON::GeneratorError) do
|
||||
JSON.generate(["\x80"])
|
||||
JSON.generate(["\x80"], :ascii_only => true)
|
||||
end
|
||||
assert_equal "\302\200", JSON.parse('["\u0080"]').first
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue