1#include "../fbuffer/fbuffer.h"
4#ifdef HAVE_RUBY_ENCODING_H
5static VALUE CEncoding_UTF_8;
6static ID i_encoding, i_encode;
9static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
11#ifdef RUBY_INTEGER_UNIFICATION
16 mFloat, mString, mString_Extend,
17 mTrueClass, mFalseClass, mNilClass, eGeneratorError,
20static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
21 i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
22 i_pack, i_unpack, i_create_id, i_extend, i_key_p,
23 i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
24 i_buffer_initial_length, i_dup, i_escape_slash;
55static const char trailingBytesForUTF8[256] = {
56 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
57 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
58 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
59 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
60 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
61 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
62 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
63 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
71static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
72 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
84static unsigned char isLegalUTF8(
const UTF8 *source,
unsigned long length)
87 const UTF8 *srcptr = source+length;
91 case 4:
if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
return 0;
92 case 3:
if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
return 0;
93 case 2:
if ((a = (*--srcptr)) > 0xBF)
return 0;
97 case 0xE0:
if (a < 0xA0)
return 0;
break;
98 case 0xED:
if (a > 0x9F)
return 0;
break;
99 case 0xF0:
if (a < 0x90)
return 0;
break;
100 case 0xF4:
if (a > 0x8F)
return 0;
break;
101 default:
if (a < 0x80)
return 0;
104 case 1:
if (*source >= 0x80 && *source < 0xC2)
return 0;
106 if (*source > 0xF4)
return 0;
111static void unicode_escape(
char *
buf,
UTF16 character)
113 const char *digits =
"0123456789abcdef";
115 buf[2] = digits[character >> 12];
116 buf[3] = digits[(character >> 8) & 0xf];
117 buf[4] = digits[(character >> 4) & 0xf];
118 buf[5] = digits[character & 0xf];
123static void unicode_escape_to_buffer(
FBuffer *buffer,
char buf[6],
UTF16
126 unicode_escape(
buf, character);
127 fbuffer_append(buffer,
buf, 6);
132static void convert_UTF8_to_JSON_ASCII(
FBuffer *buffer,
VALUE string,
char escape_slash)
136 char buf[6] = {
'\\',
'u' };
138 while (source < sourceEnd) {
140 unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
141 if (source + extraBytesToRead >= sourceEnd) {
143 "partial character in source, but hit end");
145 if (!isLegalUTF8(source, extraBytesToRead+1)) {
147 "source sequence is illegal/malformed utf-8");
152 switch (extraBytesToRead) {
153 case 5: ch += *source++; ch <<= 6;
154 case 4: ch += *source++; ch <<= 6;
155 case 3: ch += *source++; ch <<= 6;
156 case 2: ch += *source++; ch <<= 6;
157 case 1: ch += *source++; ch <<= 6;
158 case 0: ch += *source++;
160 ch -= offsetsFromUTF8[extraBytesToRead];
165#if UNI_STRICT_CONVERSION
166 source -= (extraBytesToRead+1);
168 "source sequence is illegal/malformed utf-8");
174 if (ch >= 0x20 && ch <= 0x7f) {
177 fbuffer_append(buffer,
"\\\\", 2);
180 fbuffer_append(buffer,
"\\\"", 2);
184 fbuffer_append(buffer,
"\\/", 2);
188 fbuffer_append_char(buffer, (
char)ch);
194 fbuffer_append(buffer,
"\\n", 2);
197 fbuffer_append(buffer,
"\\r", 2);
200 fbuffer_append(buffer,
"\\t", 2);
203 fbuffer_append(buffer,
"\\f", 2);
206 fbuffer_append(buffer,
"\\b", 2);
209 unicode_escape_to_buffer(buffer,
buf, (
UTF16) ch);
215#if UNI_STRICT_CONVERSION
216 source -= (extraBytesToRead+1);
218 "source sequence is illegal/malformed utf8");
236static void convert_UTF8_to_JSON(
FBuffer *buffer,
VALUE string,
char escape_slash)
240 const char *escape =
NULL;
243 char buf[6] = {
'\\',
'u' };
246 for (start = 0, end = 0; end <
len;) {
248 c = (
unsigned char) *p;
295 unsigned short clen = 1;
297 clen += trailingBytesForUTF8[c];
298 if (end + clen >
len) {
300 "partial character in source, but hit end");
302 if (!isLegalUTF8((
UTF8 *) p, clen)) {
304 "source sequence is illegal/malformed utf-8");
313 fbuffer_append(buffer,
ptr + start, end - start);
314 fbuffer_append(buffer, escape, escape_len);
318 fbuffer_append(buffer,
ptr + start, end - start);
321static char *fstrndup(
const char *
ptr,
unsigned long len) {
436#ifdef RUBY_INTEGER_UNIFICATION
509static VALUE mString_to_json_raw_object(
VALUE self)
527 VALUE obj = mString_to_json_raw_object(self);
529 return mHash_to_json(
argc,
argv, obj);
590 return cState_partial_generate(
state,
string);
593static void State_free(
void *
ptr)
601 if (
state->array_delim) fbuffer_free(
state->array_delim);
602 if (
state->object_delim) fbuffer_free(
state->object_delim);
603 if (
state->object_delim2) fbuffer_free(
state->object_delim2);
607static size_t State_memsize(
const void *
ptr)
610 size_t size =
sizeof(*state);
622#ifndef HAVE_RB_EXT_RACTOR_SAFE
623# undef RUBY_TYPED_FROZEN_SHAREABLE
624# define RUBY_TYPED_FROZEN_SHAREABLE 0
627#ifdef NEW_TYPEDDATA_WRAPPER
629 "JSON/Generator/State",
630 {
NULL, State_free, State_memsize,},
631#ifdef RUBY_TYPED_FREE_IMMEDIATELY
642 &JSON_Generator_State_type,
state);
698 tmp =
ID2SYM(i_max_nesting);
699 state->max_nesting = 100;
702 if (
RTEST(max_nesting)) {
706 state->max_nesting = 0;
720 tmp =
ID2SYM(i_buffer_initial_length);
723 if (
RTEST(buffer_initial_length)) {
725 Check_Type(buffer_initial_length,
T_FIXNUM);
726 initial_length =
FIX2LONG(buffer_initial_length);
727 if (initial_length > 0)
state->buffer_initial_length = initial_length;
761 set_state_ivars(result, self);
803 return rb_funcall(self, i_send, 2, name_writer, value);
825 char *object_nl =
state->object_nl;
826 long object_nl_len =
state->object_nl_len;
827 char *indent =
state->indent;
828 long indent_len =
state->indent_len;
833 long depth =
state->depth;
835 VALUE klass, key_to_s;
837 if (
arg->iter > 0) fbuffer_append(
buffer, delim, delim_len);
839 fbuffer_append(
buffer, object_nl, object_nl_len);
842 for (j = 0; j < depth; j++) {
843 fbuffer_append(
buffer, indent, indent_len);
857 fbuffer_append(
buffer, delim2, delim2_len);
866 char *object_nl =
state->object_nl;
867 long object_nl_len =
state->object_nl_len;
868 char *indent =
state->indent;
869 long indent_len =
state->indent_len;
870 long max_nesting =
state->max_nesting;
871 long depth = ++
state->depth;
875 if (max_nesting != 0 && depth > max_nesting) {
877 rb_raise(eNestingError,
"nesting of %ld is too deep", --
state->depth);
879 fbuffer_append_char(
buffer,
'{');
887 depth = --
state->depth;
889 fbuffer_append(
buffer, object_nl, object_nl_len);
891 for (j = 0; j < depth; j++) {
892 fbuffer_append(
buffer, indent, indent_len);
896 fbuffer_append_char(
buffer,
'}');
901 char *array_nl =
state->array_nl;
902 long array_nl_len =
state->array_nl_len;
903 char *indent =
state->indent;
904 long indent_len =
state->indent_len;
905 long max_nesting =
state->max_nesting;
908 long depth = ++
state->depth;
910 if (max_nesting != 0 && depth > max_nesting) {
912 rb_raise(eNestingError,
"nesting of %ld is too deep", --
state->depth);
914 fbuffer_append_char(
buffer,
'[');
915 if (array_nl) fbuffer_append(
buffer, array_nl, array_nl_len);
917 if (i > 0) fbuffer_append(
buffer, delim, delim_len);
919 for (j = 0; j < depth; j++) {
920 fbuffer_append(
buffer, indent, indent_len);
925 state->depth = --depth;
927 fbuffer_append(
buffer, array_nl, array_nl_len);
929 for (j = 0; j < depth; j++) {
930 fbuffer_append(
buffer, indent, indent_len);
934 fbuffer_append_char(
buffer,
']');
937#ifdef HAVE_RUBY_ENCODING_H
948 fbuffer_append_char(
buffer,
'"');
949#ifdef HAVE_RUBY_ENCODING_H
950 if (!enc_utf8_compatible_p(
rb_enc_get(obj))) {
954 if (
state->ascii_only) {
955 convert_UTF8_to_JSON_ASCII(
buffer, obj,
state->escape_slash);
957 convert_UTF8_to_JSON(
buffer, obj,
state->escape_slash);
959 fbuffer_append_char(
buffer,
'"');
964 fbuffer_append(
buffer,
"null", 4);
969 fbuffer_append(
buffer,
"false", 5);
974 fbuffer_append(
buffer,
"true", 4);
985 fbuffer_append_str(
buffer, tmp);
988#ifdef RUBY_INTEGER_UNIFICATION
1000 char allow_nan =
state->allow_nan;
1006 }
else if (
isnan(value)) {
1011 fbuffer_append_str(
buffer, tmp);
1024 }
else if (obj ==
Qnil) {
1026 }
else if (obj ==
Qfalse) {
1028 }
else if (obj ==
Qtrue) {
1032 }
else if (RB_TYPE_P(obj,
T_BIGNUM)) {
1039 fbuffer_append_str(
buffer, tmp);
1051 buffer = fbuffer_alloc(
state->buffer_initial_length);
1053 if (
state->object_delim) {
1054 fbuffer_clear(
state->object_delim);
1056 state->object_delim = fbuffer_alloc(16);
1058 fbuffer_append_char(
state->object_delim,
',');
1059 if (
state->object_delim2) {
1060 fbuffer_clear(
state->object_delim2);
1062 state->object_delim2 = fbuffer_alloc(16);
1064 if (
state->space_before) fbuffer_append(
state->object_delim2,
state->space_before,
state->space_before_len);
1065 fbuffer_append_char(
state->object_delim2,
':');
1068 if (
state->array_delim) {
1069 fbuffer_clear(
state->array_delim);
1071 state->array_delim = fbuffer_alloc(16);
1073 fbuffer_append_char(
state->array_delim,
',');
1083 return fbuffer_to_s(
buffer);
1095 VALUE result = cState_partial_generate(self, obj);
1125 state->max_nesting = 100;
1128 if (!
NIL_P(opts)) cState_configure(self, opts);
1142 if (obj == orig)
return obj;
1200 if (
state->indent) {
1203 state->indent_len = 0;
1241 state->space_len = 0;
1271 Check_Type(space_before,
T_STRING);
1274 if (
state->space_before) {
1277 state->space_before_len = 0;
1312 if (
state->object_nl) {
1347 if (
state->array_nl) {
1366static VALUE cState_check_circular_p(
VALUE self)
1476static VALUE cState_buffer_initial_length(
VALUE self)
1488static VALUE cState_buffer_initial_length_set(
VALUE self,
VALUE buffer_initial_length)
1490 long initial_length;
1492 Check_Type(buffer_initial_length,
T_FIXNUM);
1493 initial_length =
FIX2LONG(buffer_initial_length);
1494 if (initial_length > 0) {
1495 state->buffer_initial_length = initial_length;
1505#ifdef HAVE_RB_EXT_RACTOR_SAFE
1546 rb_define_method(cState,
"buffer_initial_length", cState_buffer_initial_length, 0);
1547 rb_define_method(cState,
"buffer_initial_length=", cState_buffer_initial_length_set, 1);
1563#ifdef RUBY_INTEGER_UNIFICATION
1578 rb_define_method(mString,
"to_json_raw_object", mString_to_json_raw_object, 0);
1580 rb_define_method(mString_Extend,
"json_create", mString_Extend_json_create, 1);
1593 i_space_before =
rb_intern(
"space_before");
1596 i_max_nesting =
rb_intern(
"max_nesting");
1597 i_escape_slash =
rb_intern(
"escape_slash");
1601 i_buffer_initial_length =
rb_intern(
"buffer_initial_length");
1609 i_respond_to_p =
rb_intern(
"respond_to?");
1613#ifdef HAVE_RUBY_ENCODING_H
VALUE rb_ary_entry(VALUE ary, long offset)
#define RB_OBJ_STRING(obj)
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
rb_encoding * rb_utf8_encoding(void)
rb_encoding * rb_enc_get(VALUE obj)
rb_encoding * rb_usascii_encoding(void)
#define RSTRING_LEN(string)
#define RSTRING_PTR(string)
#define FBUFFER_INITIAL_LENGTH_DEFAULT
void ruby_xfree(void *x)
Deallocates a storage instance.
void rb_gc_register_mark_object(VALUE obj)
Inform the garbage collector that object is a live Ruby object that should not be moved.
void Init_generator(void)
#define GET_STATE_TO(self, state)
#define rb_obj_instance_variables(object)
#define UNI_SUR_LOW_START
#define UNI_REPLACEMENT_CHAR
#define UNI_SUR_HIGH_START
#define rb_intern_str(string)
#define option_given_p(opts, key)
#define GENERATE_JSON(type)
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
VALUE rb_define_module(const char *name)
VALUE rb_define_module_under(VALUE outer, const char *name)
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
void rb_raise(VALUE exc, const char *fmt,...)
VALUE rb_convert_type(VALUE, int, const char *, const char *)
Converts an object into another type.
VALUE rb_check_convert_type(VALUE, int, const char *, const char *)
Tries to convert an object into another type.
VALUE rb_cObject
Object class.
VALUE rb_class_new_instance(int, const VALUE *, VALUE)
Allocates and initializes an instance of klass.
VALUE rb_obj_class(VALUE)
VALUE rb_obj_is_kind_of(VALUE, VALUE)
Determines if obj is a kind of c.
void rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
VALUE rb_hash_aref(VALUE hash, VALUE key)
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
VALUE rb_str_encode(VALUE str, VALUE to, int ecflags, VALUE ecopts)
int rb_enc_str_asciionly_p(VALUE)
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
void rb_ext_ractor_safe(bool flag)
VALUE rb_str_concat(VALUE, VALUE)
#define rb_str_new(str, len)
VALUE rb_str_substr(VALUE, long, long)
VALUE rb_str_intern(VALUE)
VALUE rb_class_name(VALUE)
VALUE rb_path2class(const char *)
VALUE rb_attr_get(VALUE, ID)
VALUE rb_ivar_set(VALUE, ID, VALUE)
int rb_respond_to(VALUE, ID)
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
ID rb_intern(const char *)
VALUE rb_iv_get(VALUE, const char *)
#define MEMCPY(p1, p2, type, n)
#define StringValueCStr(v)
@ RUBY_TYPED_FREE_IMMEDIATELY
@ RUBY_TYPED_FROZEN_SHAREABLE
#define TypedData_Make_Struct(klass, type, data_type, sval)
VALUE rb_require(const char *)
JSON_Generator_State * state