mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Recommit of JSON; fix mixed declarations.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27501 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
27be3056dc
commit
3642494ce5
20 changed files with 2238 additions and 1766 deletions
|
@ -1,7 +1,3 @@
|
|||
Mon Apr 26 13:33:39 2010 NAKAMURA Usaku <usa@ruby-lang.org>
|
||||
|
||||
* ext/jason: came again after canceling gcc-ism.
|
||||
|
||||
Mon Apr 26 13:11:57 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* parse.y (ripper_get_value): escape Qundef.
|
||||
|
|
|
@ -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
|
4
ext/json/generator/extconf.rb
Normal file
4
ext/json/generator/extconf.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'mkmf'
|
||||
require 'rbconfig'
|
||||
|
||||
create_makefile 'json/ext/generator'
|
1341
ext/json/generator/generator.c
Normal file
1341
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:
|
||||
|
|
4
ext/json/parser/extconf.rb
Normal file
4
ext/json/parser/extconf.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'mkmf'
|
||||
require 'rbconfig'
|
||||
|
||||
create_makefile 'json/ext/parser'
|
File diff suppressed because it is too large
Load diff
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,10 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
|
|||
}
|
||||
|
||||
action parse_name {
|
||||
char *np = JSON_parse_string(json, fpc, pe, &last_name);
|
||||
char *np;
|
||||
json->parsing_name = 1;
|
||||
np = JSON_parse_string(json, fpc, pe, &last_name);
|
||||
json->parsing_name = 0;
|
||||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
|
@ -135,7 +162,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 +226,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 +234,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 +338,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 +374,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 +455,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 +474,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 +517,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 +529,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 +594,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 +608,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 +631,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 +656,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 +676,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 +693,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 +704,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 +742,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 +770,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
|
||||
}
|
Loading…
Reference in a new issue