json 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +11 -0
- data/LEGAL +0 -52
- data/README.md +75 -2
- data/ext/json/ext/fbuffer/fbuffer.h +1 -10
- data/ext/json/ext/generator/generator.c +394 -263
- data/ext/json/ext/parser/extconf.rb +1 -0
- data/ext/json/ext/parser/parser.c +769 -2521
- data/json.gemspec +3 -4
- data/lib/json/add/symbol.rb +7 -2
- data/lib/json/common.rb +107 -10
- data/lib/json/ext/generator/state.rb +1 -11
- data/lib/json/ext.rb +26 -4
- data/lib/json/truffle_ruby/generator.rb +111 -50
- data/lib/json/version.rb +1 -1
- metadata +6 -11
- data/ext/json/ext/parser/parser.rl +0 -1457
@@ -12,6 +12,7 @@ typedef struct JSON_Generator_StateStruct {
|
|
12
12
|
VALUE space_before;
|
13
13
|
VALUE object_nl;
|
14
14
|
VALUE array_nl;
|
15
|
+
VALUE as_json;
|
15
16
|
|
16
17
|
long max_nesting;
|
17
18
|
long depth;
|
@@ -27,11 +28,11 @@ typedef struct JSON_Generator_StateStruct {
|
|
27
28
|
#define RB_UNLIKELY(cond) (cond)
|
28
29
|
#endif
|
29
30
|
|
30
|
-
static VALUE mJSON, cState, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
|
31
|
+
static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
|
31
32
|
|
32
33
|
static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
|
33
|
-
static
|
34
|
-
|
34
|
+
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
|
35
|
+
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
35
36
|
|
36
37
|
|
37
38
|
#define GET_STATE_TO(self, state) \
|
@@ -68,6 +69,7 @@ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *da
|
|
68
69
|
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
|
69
70
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
|
70
71
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
|
72
|
+
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
|
71
73
|
|
72
74
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
73
75
|
|
@@ -96,6 +98,75 @@ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
96
98
|
raise_generator_error_str(invalid_object, str);
|
97
99
|
}
|
98
100
|
|
101
|
+
// 0 - single byte char that don't need to be escaped.
|
102
|
+
// (x | 8) - char that needs to be escaped.
|
103
|
+
static const unsigned char CHAR_LENGTH_MASK = 7;
|
104
|
+
static const unsigned char ESCAPE_MASK = 8;
|
105
|
+
|
106
|
+
typedef struct _search_state {
|
107
|
+
const char *ptr;
|
108
|
+
const char *end;
|
109
|
+
const char *cursor;
|
110
|
+
FBuffer *buffer;
|
111
|
+
} search_state;
|
112
|
+
|
113
|
+
static inline void search_flush(search_state *search)
|
114
|
+
{
|
115
|
+
fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor);
|
116
|
+
search->cursor = search->ptr;
|
117
|
+
}
|
118
|
+
|
119
|
+
static const unsigned char escape_table_basic[256] = {
|
120
|
+
// ASCII Control Characters
|
121
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
122
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
123
|
+
// ASCII Characters
|
124
|
+
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
|
125
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
126
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
127
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
|
128
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
129
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
130
|
+
};
|
131
|
+
|
132
|
+
static inline unsigned char search_escape_basic(search_state *search)
|
133
|
+
{
|
134
|
+
while (search->ptr < search->end) {
|
135
|
+
if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) {
|
136
|
+
search_flush(search);
|
137
|
+
return 1;
|
138
|
+
} else {
|
139
|
+
search->ptr++;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
search_flush(search);
|
143
|
+
return 0;
|
144
|
+
}
|
145
|
+
|
146
|
+
static inline void escape_UTF8_char_basic(search_state *search) {
|
147
|
+
const unsigned char ch = (unsigned char)*search->ptr;
|
148
|
+
switch (ch) {
|
149
|
+
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
|
150
|
+
case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
|
151
|
+
case '/': fbuffer_append(search->buffer, "\\/", 2); break;
|
152
|
+
case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
|
153
|
+
case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
|
154
|
+
case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
|
155
|
+
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
|
156
|
+
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
|
157
|
+
default: {
|
158
|
+
const char *hexdig = "0123456789abcdef";
|
159
|
+
char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
|
160
|
+
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
161
|
+
scratch[5] = hexdig[ch & 0xf];
|
162
|
+
fbuffer_append(search->buffer, scratch, 6);
|
163
|
+
break;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
search->ptr++;
|
167
|
+
search->cursor = search->ptr;
|
168
|
+
}
|
169
|
+
|
99
170
|
/* Converts in_string to a JSON string (without the wrapping '"'
|
100
171
|
* characters) in FBuffer out_buffer.
|
101
172
|
*
|
@@ -106,282 +177,241 @@ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
106
177
|
*
|
107
178
|
* - If out_ascii_only: non-ASCII characters (>0x7F)
|
108
179
|
*
|
109
|
-
* - If
|
180
|
+
* - If script_safe: forwardslash (/), line separator (U+2028), and
|
110
181
|
* paragraph separator (U+2029)
|
111
182
|
*
|
112
183
|
* Everything else (should be UTF-8) is just passed through and
|
113
184
|
* appended to the result.
|
114
185
|
*/
|
115
|
-
static void convert_UTF8_to_JSON(
|
186
|
+
static inline void convert_UTF8_to_JSON(search_state *search)
|
116
187
|
{
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
unsigned long len = RSTRING_LEN(str);
|
122
|
-
|
123
|
-
unsigned long beg = 0, pos = 0;
|
124
|
-
|
125
|
-
#define FLUSH_POS(bytes) if (pos > beg) { fbuffer_append(out_buffer, &ptr[beg], pos - beg); } pos += bytes; beg = pos;
|
126
|
-
|
127
|
-
while (pos < len) {
|
128
|
-
unsigned char ch = ptr[pos];
|
129
|
-
unsigned char ch_len = escape_table[ch];
|
130
|
-
/* JSON encoding */
|
188
|
+
while (search_escape_basic(search)) {
|
189
|
+
escape_UTF8_char_basic(search);
|
190
|
+
}
|
191
|
+
}
|
131
192
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
break;
|
152
|
-
}
|
153
|
-
}
|
193
|
+
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) {
|
194
|
+
const unsigned char ch = (unsigned char)*search->ptr;
|
195
|
+
switch (ch_len) {
|
196
|
+
case 1: {
|
197
|
+
switch (ch) {
|
198
|
+
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
|
199
|
+
case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
|
200
|
+
case '/': fbuffer_append(search->buffer, "\\/", 2); break;
|
201
|
+
case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
|
202
|
+
case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
|
203
|
+
case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
|
204
|
+
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
|
205
|
+
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
|
206
|
+
default: {
|
207
|
+
const char *hexdig = "0123456789abcdef";
|
208
|
+
char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
|
209
|
+
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
210
|
+
scratch[5] = hexdig[ch & 0xf];
|
211
|
+
fbuffer_append(search->buffer, scratch, 6);
|
154
212
|
break;
|
155
213
|
}
|
156
|
-
case 3: {
|
157
|
-
unsigned char b2 = ptr[pos + 1];
|
158
|
-
if (RB_UNLIKELY(out_script_safe && ch == 0xE2 && b2 == 0x80)) {
|
159
|
-
unsigned char b3 = ptr[pos + 2];
|
160
|
-
if (b3 == 0xA8) {
|
161
|
-
FLUSH_POS(3);
|
162
|
-
fbuffer_append(out_buffer, "\\u2028", 6);
|
163
|
-
break;
|
164
|
-
} else if (b3 == 0xA9) {
|
165
|
-
FLUSH_POS(3);
|
166
|
-
fbuffer_append(out_buffer, "\\u2029", 6);
|
167
|
-
break;
|
168
|
-
}
|
169
|
-
}
|
170
|
-
// fallthrough
|
171
|
-
}
|
172
|
-
default:
|
173
|
-
pos += ch_len;
|
174
|
-
break;
|
175
214
|
}
|
176
|
-
|
177
|
-
|
215
|
+
break;
|
216
|
+
}
|
217
|
+
case 3: {
|
218
|
+
if (search->ptr[2] & 1) {
|
219
|
+
fbuffer_append(search->buffer, "\\u2029", 6);
|
220
|
+
} else {
|
221
|
+
fbuffer_append(search->buffer, "\\u2028", 6);
|
222
|
+
}
|
223
|
+
break;
|
178
224
|
}
|
179
225
|
}
|
180
|
-
|
181
|
-
|
182
|
-
if (beg < len) {
|
183
|
-
fbuffer_append(out_buffer, &ptr[beg], len - beg);
|
184
|
-
}
|
185
|
-
|
186
|
-
RB_GC_GUARD(str);
|
226
|
+
search->cursor = (search->ptr += ch_len);
|
187
227
|
}
|
188
228
|
|
189
|
-
static const char
|
190
|
-
// ASCII Control Characters
|
191
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
192
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
193
|
-
// ASCII Characters
|
194
|
-
0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, // '"'
|
195
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
196
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
197
|
-
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, // '\\'
|
198
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
199
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
200
|
-
// Continuation byte
|
201
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
202
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
203
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
204
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
205
|
-
// First byte of a 2-byte code point
|
206
|
-
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
207
|
-
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
208
|
-
// First byte of a 4-byte code point
|
209
|
-
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
|
210
|
-
//First byte of a 4+byte code point
|
211
|
-
4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1,
|
212
|
-
};
|
213
|
-
|
214
|
-
static const char script_safe_escape_table[256] = {
|
229
|
+
static const unsigned char script_safe_escape_table[256] = {
|
215
230
|
// ASCII Control Characters
|
216
|
-
|
217
|
-
|
231
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
232
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
218
233
|
// ASCII Characters
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
234
|
+
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/'
|
235
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
236
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
237
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
|
238
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
239
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
225
240
|
// Continuation byte
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
241
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
242
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
243
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
244
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
230
245
|
// First byte of a 2-byte code point
|
231
|
-
|
232
|
-
|
233
|
-
// First byte of a
|
234
|
-
|
235
|
-
//First byte of a 4+byte code point
|
236
|
-
|
246
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
247
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
248
|
+
// First byte of a 3-byte code point
|
249
|
+
3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029
|
250
|
+
//First byte of a 4+ byte code point
|
251
|
+
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
|
237
252
|
};
|
238
253
|
|
239
|
-
static
|
254
|
+
static inline unsigned char search_script_safe_escape(search_state *search)
|
240
255
|
{
|
241
|
-
|
242
|
-
|
256
|
+
while (search->ptr < search->end) {
|
257
|
+
unsigned char ch = (unsigned char)*search->ptr;
|
258
|
+
unsigned char ch_len = script_safe_escape_table[ch];
|
243
259
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
}
|
256
|
-
|
257
|
-
beg = pos + 1;
|
258
|
-
switch (ch) {
|
259
|
-
case '"': fbuffer_append(out_buffer, "\\\"", 2); break;
|
260
|
-
case '\\': fbuffer_append(out_buffer, "\\\\", 2); break;
|
261
|
-
case '/': fbuffer_append(out_buffer, "\\/", 2); break;
|
262
|
-
case '\b': fbuffer_append(out_buffer, "\\b", 2); break;
|
263
|
-
case '\f': fbuffer_append(out_buffer, "\\f", 2); break;
|
264
|
-
case '\n': fbuffer_append(out_buffer, "\\n", 2); break;
|
265
|
-
case '\r': fbuffer_append(out_buffer, "\\r", 2); break;
|
266
|
-
case '\t': fbuffer_append(out_buffer, "\\t", 2); break;
|
267
|
-
default:
|
268
|
-
scratch[2] = '0';
|
269
|
-
scratch[3] = '0';
|
270
|
-
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
271
|
-
scratch[5] = hexdig[ch & 0xf];
|
272
|
-
fbuffer_append(out_buffer, scratch, 6);
|
260
|
+
if (RB_UNLIKELY(ch_len)) {
|
261
|
+
if (ch_len & ESCAPE_MASK) {
|
262
|
+
if (RB_UNLIKELY(ch_len == 11)) {
|
263
|
+
const unsigned char *uptr = (const unsigned char *)search->ptr;
|
264
|
+
if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) {
|
265
|
+
search->ptr += 3;
|
266
|
+
continue;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
search_flush(search);
|
270
|
+
return ch_len & CHAR_LENGTH_MASK;
|
271
|
+
} else {
|
272
|
+
search->ptr += ch_len;
|
273
273
|
}
|
274
|
+
} else {
|
275
|
+
search->ptr++;
|
274
276
|
}
|
275
|
-
|
276
|
-
pos++;
|
277
277
|
}
|
278
|
-
|
279
|
-
|
280
|
-
fbuffer_append(out_buffer, &ptr[beg], len - beg);
|
281
|
-
}
|
282
|
-
|
283
|
-
RB_GC_GUARD(str);
|
278
|
+
search_flush(search);
|
279
|
+
return 0;
|
284
280
|
}
|
285
281
|
|
286
|
-
static void
|
282
|
+
static void convert_UTF8_to_script_safe_JSON(search_state *search)
|
287
283
|
{
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
unsigned long beg = 0, pos = 0;
|
284
|
+
unsigned char ch_len;
|
285
|
+
while ((ch_len = search_script_safe_escape(search))) {
|
286
|
+
escape_UTF8_char(search, ch_len);
|
287
|
+
}
|
288
|
+
}
|
295
289
|
|
296
|
-
|
290
|
+
static const unsigned char ascii_only_escape_table[256] = {
|
291
|
+
// ASCII Control Characters
|
292
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
293
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
294
|
+
// ASCII Characters
|
295
|
+
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
|
296
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
297
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
298
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
|
299
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
300
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
301
|
+
// Continuation byte
|
302
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
303
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
304
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
305
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
306
|
+
// First byte of a 2-byte code point
|
307
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
308
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
309
|
+
// First byte of a 3-byte code point
|
310
|
+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
311
|
+
//First byte of a 4+ byte code point
|
312
|
+
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
|
313
|
+
};
|
297
314
|
|
298
|
-
|
299
|
-
|
315
|
+
static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256])
|
316
|
+
{
|
317
|
+
while (search->ptr < search->end) {
|
318
|
+
unsigned char ch = (unsigned char)*search->ptr;
|
300
319
|
unsigned char ch_len = escape_table[ch];
|
301
320
|
|
302
321
|
if (RB_UNLIKELY(ch_len)) {
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
322
|
+
search_flush(search);
|
323
|
+
return ch_len & CHAR_LENGTH_MASK;
|
324
|
+
} else {
|
325
|
+
search->ptr++;
|
326
|
+
}
|
327
|
+
}
|
328
|
+
search_flush(search);
|
329
|
+
return 0;
|
330
|
+
}
|
331
|
+
|
332
|
+
static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len) {
|
333
|
+
const unsigned char ch = (unsigned char)*search->ptr;
|
334
|
+
switch (ch_len) {
|
335
|
+
case 1: {
|
336
|
+
switch (ch) {
|
337
|
+
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
|
338
|
+
case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
|
339
|
+
case '/': fbuffer_append(search->buffer, "\\/", 2); break;
|
340
|
+
case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
|
341
|
+
case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
|
342
|
+
case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
|
343
|
+
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
|
344
|
+
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
|
345
|
+
default: {
|
346
|
+
const char *hexdig = "0123456789abcdef";
|
347
|
+
char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
|
348
|
+
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
349
|
+
scratch[5] = hexdig[ch & 0xf];
|
350
|
+
fbuffer_append(search->buffer, scratch, 6);
|
324
351
|
break;
|
325
352
|
}
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
case 3:
|
333
|
-
wchar = ptr[pos] & 0x0F;
|
334
|
-
break;
|
335
|
-
case 4:
|
336
|
-
wchar = ptr[pos] & 0x07;
|
337
|
-
break;
|
338
|
-
}
|
353
|
+
}
|
354
|
+
break;
|
355
|
+
}
|
356
|
+
default: {
|
357
|
+
const char *hexdig = "0123456789abcdef";
|
358
|
+
char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
|
339
359
|
|
340
|
-
|
341
|
-
wchar = (wchar << 6) | (ptr[pos+i] & 0x3F);
|
342
|
-
}
|
360
|
+
uint32_t wchar = 0;
|
343
361
|
|
344
|
-
|
362
|
+
switch(ch_len) {
|
363
|
+
case 2:
|
364
|
+
wchar = ch & 0x1F;
|
365
|
+
break;
|
366
|
+
case 3:
|
367
|
+
wchar = ch & 0x0F;
|
368
|
+
break;
|
369
|
+
case 4:
|
370
|
+
wchar = ch & 0x07;
|
371
|
+
break;
|
372
|
+
}
|
345
373
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
scratch[4] = hexdig[(wchar >> 4) & 0xf];
|
350
|
-
scratch[5] = hexdig[wchar & 0xf];
|
351
|
-
fbuffer_append(out_buffer, scratch, 6);
|
352
|
-
} else {
|
353
|
-
uint16_t hi, lo;
|
354
|
-
wchar -= 0x10000;
|
355
|
-
hi = 0xD800 + (uint16_t)(wchar >> 10);
|
356
|
-
lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
|
357
|
-
|
358
|
-
scratch[2] = hexdig[hi >> 12];
|
359
|
-
scratch[3] = hexdig[(hi >> 8) & 0xf];
|
360
|
-
scratch[4] = hexdig[(hi >> 4) & 0xf];
|
361
|
-
scratch[5] = hexdig[hi & 0xf];
|
362
|
-
|
363
|
-
scratch[8] = hexdig[lo >> 12];
|
364
|
-
scratch[9] = hexdig[(lo >> 8) & 0xf];
|
365
|
-
scratch[10] = hexdig[(lo >> 4) & 0xf];
|
366
|
-
scratch[11] = hexdig[lo & 0xf];
|
367
|
-
|
368
|
-
fbuffer_append(out_buffer, scratch, 12);
|
369
|
-
}
|
374
|
+
for (short i = 1; i < ch_len; i++) {
|
375
|
+
wchar = (wchar << 6) | (search->ptr[i] & 0x3F);
|
376
|
+
}
|
370
377
|
|
371
|
-
|
372
|
-
|
378
|
+
if (wchar <= 0xFFFF) {
|
379
|
+
scratch[2] = hexdig[wchar >> 12];
|
380
|
+
scratch[3] = hexdig[(wchar >> 8) & 0xf];
|
381
|
+
scratch[4] = hexdig[(wchar >> 4) & 0xf];
|
382
|
+
scratch[5] = hexdig[wchar & 0xf];
|
383
|
+
fbuffer_append(search->buffer, scratch, 6);
|
384
|
+
} else {
|
385
|
+
uint16_t hi, lo;
|
386
|
+
wchar -= 0x10000;
|
387
|
+
hi = 0xD800 + (uint16_t)(wchar >> 10);
|
388
|
+
lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
|
389
|
+
|
390
|
+
scratch[2] = hexdig[hi >> 12];
|
391
|
+
scratch[3] = hexdig[(hi >> 8) & 0xf];
|
392
|
+
scratch[4] = hexdig[(hi >> 4) & 0xf];
|
393
|
+
scratch[5] = hexdig[hi & 0xf];
|
394
|
+
|
395
|
+
scratch[8] = hexdig[lo >> 12];
|
396
|
+
scratch[9] = hexdig[(lo >> 8) & 0xf];
|
397
|
+
scratch[10] = hexdig[(lo >> 4) & 0xf];
|
398
|
+
scratch[11] = hexdig[lo & 0xf];
|
399
|
+
|
400
|
+
fbuffer_append(search->buffer, scratch, 12);
|
373
401
|
}
|
374
|
-
|
375
|
-
|
402
|
+
|
403
|
+
break;
|
376
404
|
}
|
377
405
|
}
|
378
|
-
|
406
|
+
search->cursor = (search->ptr += ch_len);
|
407
|
+
}
|
379
408
|
|
380
|
-
|
381
|
-
|
409
|
+
static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256])
|
410
|
+
{
|
411
|
+
unsigned char ch_len;
|
412
|
+
while ((ch_len = search_ascii_only_escape(search, escape_table))) {
|
413
|
+
full_escape_UTF8_char(search, ch_len);
|
382
414
|
}
|
383
|
-
|
384
|
-
RB_GC_GUARD(str);
|
385
415
|
}
|
386
416
|
|
387
417
|
/*
|
@@ -674,6 +704,7 @@ static void State_mark(void *ptr)
|
|
674
704
|
rb_gc_mark_movable(state->space_before);
|
675
705
|
rb_gc_mark_movable(state->object_nl);
|
676
706
|
rb_gc_mark_movable(state->array_nl);
|
707
|
+
rb_gc_mark_movable(state->as_json);
|
677
708
|
}
|
678
709
|
|
679
710
|
static void State_compact(void *ptr)
|
@@ -684,6 +715,7 @@ static void State_compact(void *ptr)
|
|
684
715
|
state->space_before = rb_gc_location(state->space_before);
|
685
716
|
state->object_nl = rb_gc_location(state->object_nl);
|
686
717
|
state->array_nl = rb_gc_location(state->array_nl);
|
718
|
+
state->as_json = rb_gc_location(state->as_json);
|
687
719
|
}
|
688
720
|
|
689
721
|
static void State_free(void *ptr)
|
@@ -740,6 +772,7 @@ static void vstate_spill(struct generate_json_data *data)
|
|
740
772
|
RB_OBJ_WRITTEN(vstate, Qundef, state->space_before);
|
741
773
|
RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
|
742
774
|
RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
|
775
|
+
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
743
776
|
}
|
744
777
|
|
745
778
|
static inline VALUE vstate_get(struct generate_json_data *data)
|
@@ -808,15 +841,19 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
808
841
|
return ST_CONTINUE;
|
809
842
|
}
|
810
843
|
|
811
|
-
static
|
844
|
+
static inline long increase_depth(JSON_Generator_State *state)
|
812
845
|
{
|
813
|
-
long max_nesting = state->max_nesting;
|
814
846
|
long depth = ++state->depth;
|
815
|
-
|
816
|
-
|
817
|
-
if (max_nesting != 0 && depth > max_nesting) {
|
847
|
+
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
818
848
|
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
819
849
|
}
|
850
|
+
return depth;
|
851
|
+
}
|
852
|
+
|
853
|
+
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
854
|
+
{
|
855
|
+
int j;
|
856
|
+
long depth = increase_depth(state);
|
820
857
|
|
821
858
|
if (RHASH_SIZE(obj) == 0) {
|
822
859
|
fbuffer_append(buffer, "{}", 2);
|
@@ -846,12 +883,8 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
846
883
|
|
847
884
|
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
848
885
|
{
|
849
|
-
long max_nesting = state->max_nesting;
|
850
|
-
long depth = ++state->depth;
|
851
886
|
int i, j;
|
852
|
-
|
853
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
854
|
-
}
|
887
|
+
long depth = increase_depth(state);
|
855
888
|
|
856
889
|
if (RARRAY_LEN(obj) == 0) {
|
857
890
|
fbuffer_append(buffer, "[]", 2);
|
@@ -933,15 +966,22 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
933
966
|
|
934
967
|
fbuffer_append_char(buffer, '"');
|
935
968
|
|
969
|
+
long len;
|
970
|
+
search_state search;
|
971
|
+
search.buffer = buffer;
|
972
|
+
RSTRING_GETMEM(obj, search.ptr, len);
|
973
|
+
search.cursor = search.ptr;
|
974
|
+
search.end = search.ptr + len;
|
975
|
+
|
936
976
|
switch(rb_enc_str_coderange(obj)) {
|
937
977
|
case ENC_CODERANGE_7BIT:
|
938
|
-
convert_ASCII_to_JSON(buffer, obj, state->script_safe ? script_safe_escape_table : escape_table);
|
939
|
-
break;
|
940
978
|
case ENC_CODERANGE_VALID:
|
941
979
|
if (RB_UNLIKELY(state->ascii_only)) {
|
942
|
-
convert_UTF8_to_ASCII_only_JSON(
|
980
|
+
convert_UTF8_to_ASCII_only_JSON(&search, state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
981
|
+
} else if (RB_UNLIKELY(state->script_safe)) {
|
982
|
+
convert_UTF8_to_script_safe_JSON(&search);
|
943
983
|
} else {
|
944
|
-
convert_UTF8_to_JSON(
|
984
|
+
convert_UTF8_to_JSON(&search);
|
945
985
|
}
|
946
986
|
break;
|
947
987
|
default:
|
@@ -951,6 +991,29 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
951
991
|
fbuffer_append_char(buffer, '"');
|
952
992
|
}
|
953
993
|
|
994
|
+
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
995
|
+
{
|
996
|
+
VALUE tmp;
|
997
|
+
if (rb_respond_to(obj, i_to_json)) {
|
998
|
+
tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
|
999
|
+
Check_Type(tmp, T_STRING);
|
1000
|
+
fbuffer_append_str(buffer, tmp);
|
1001
|
+
} else {
|
1002
|
+
tmp = rb_funcall(obj, i_to_s, 0);
|
1003
|
+
Check_Type(tmp, T_STRING);
|
1004
|
+
generate_json_string(buffer, data, state, tmp);
|
1005
|
+
}
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
1009
|
+
{
|
1010
|
+
if (state->strict) {
|
1011
|
+
generate_json_string(buffer, data, state, rb_sym2str(obj));
|
1012
|
+
} else {
|
1013
|
+
generate_json_fallback(buffer, data, state, obj);
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
|
954
1017
|
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
955
1018
|
{
|
956
1019
|
fbuffer_append(buffer, "null", 4);
|
@@ -991,18 +1054,34 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
991
1054
|
{
|
992
1055
|
double value = RFLOAT_VALUE(obj);
|
993
1056
|
char allow_nan = state->allow_nan;
|
994
|
-
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
995
1057
|
if (!allow_nan) {
|
996
1058
|
if (isinf(value) || isnan(value)) {
|
997
|
-
|
1059
|
+
if (state->strict && state->as_json) {
|
1060
|
+
VALUE casted_obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
|
1061
|
+
if (casted_obj != obj) {
|
1062
|
+
increase_depth(state);
|
1063
|
+
generate_json(buffer, data, state, casted_obj);
|
1064
|
+
state->depth--;
|
1065
|
+
return;
|
1066
|
+
}
|
1067
|
+
}
|
1068
|
+
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
|
998
1069
|
}
|
999
1070
|
}
|
1000
|
-
fbuffer_append_str(buffer,
|
1071
|
+
fbuffer_append_str(buffer, rb_funcall(obj, i_to_s, 0));
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
1075
|
+
{
|
1076
|
+
VALUE fragment = RSTRUCT_GET(obj, 0);
|
1077
|
+
Check_Type(fragment, T_STRING);
|
1078
|
+
fbuffer_append_str(buffer, fragment);
|
1001
1079
|
}
|
1002
1080
|
|
1003
1081
|
static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
1004
1082
|
{
|
1005
|
-
|
1083
|
+
bool as_json_called = false;
|
1084
|
+
start:
|
1006
1085
|
if (obj == Qnil) {
|
1007
1086
|
generate_json_null(buffer, data, state, obj);
|
1008
1087
|
} else if (obj == Qfalse) {
|
@@ -1014,6 +1093,8 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
|
|
1014
1093
|
generate_json_fixnum(buffer, data, state, obj);
|
1015
1094
|
} else if (RB_FLONUM_P(obj)) {
|
1016
1095
|
generate_json_float(buffer, data, state, obj);
|
1096
|
+
} else if (RB_STATIC_SYM_P(obj)) {
|
1097
|
+
generate_json_symbol(buffer, data, state, obj);
|
1017
1098
|
} else {
|
1018
1099
|
goto general;
|
1019
1100
|
}
|
@@ -1035,22 +1116,29 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
|
|
1035
1116
|
if (klass != rb_cString) goto general;
|
1036
1117
|
generate_json_string(buffer, data, state, obj);
|
1037
1118
|
break;
|
1119
|
+
case T_SYMBOL:
|
1120
|
+
generate_json_symbol(buffer, data, state, obj);
|
1121
|
+
break;
|
1038
1122
|
case T_FLOAT:
|
1039
1123
|
if (klass != rb_cFloat) goto general;
|
1040
1124
|
generate_json_float(buffer, data, state, obj);
|
1041
1125
|
break;
|
1126
|
+
case T_STRUCT:
|
1127
|
+
if (klass != cFragment) goto general;
|
1128
|
+
generate_json_fragment(buffer, data, state, obj);
|
1129
|
+
break;
|
1042
1130
|
default:
|
1043
1131
|
general:
|
1044
1132
|
if (state->strict) {
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1133
|
+
if (RTEST(state->as_json) && !as_json_called) {
|
1134
|
+
obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
|
1135
|
+
as_json_called = true;
|
1136
|
+
goto start;
|
1137
|
+
} else {
|
1138
|
+
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
|
1139
|
+
}
|
1050
1140
|
} else {
|
1051
|
-
|
1052
|
-
Check_Type(tmp, T_STRING);
|
1053
|
-
generate_json_string(buffer, data, state, tmp);
|
1141
|
+
generate_json_fallback(buffer, data, state, obj);
|
1054
1142
|
}
|
1055
1143
|
}
|
1056
1144
|
}
|
@@ -1097,8 +1185,19 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
1097
1185
|
return fbuffer_finalize(&buffer);
|
1098
1186
|
}
|
1099
1187
|
|
1100
|
-
|
1188
|
+
/* call-seq:
|
1189
|
+
* generate(obj) -> String
|
1190
|
+
* generate(obj, anIO) -> anIO
|
1191
|
+
*
|
1192
|
+
* Generates a valid JSON document from object +obj+ and returns the
|
1193
|
+
* result. If no valid JSON document can be created this method raises a
|
1194
|
+
* GeneratorError exception.
|
1195
|
+
*/
|
1196
|
+
static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
1101
1197
|
{
|
1198
|
+
rb_check_arity(argc, 1, 2);
|
1199
|
+
VALUE obj = argv[0];
|
1200
|
+
VALUE io = argc > 1 ? argv[1] : Qnil;
|
1102
1201
|
VALUE result = cState_partial_generate(self, obj, generate_json, io);
|
1103
1202
|
GET_STATE(self);
|
1104
1203
|
(void)state;
|
@@ -1132,6 +1231,7 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
1132
1231
|
objState->space_before = origState->space_before;
|
1133
1232
|
objState->object_nl = origState->object_nl;
|
1134
1233
|
objState->array_nl = origState->array_nl;
|
1234
|
+
objState->as_json = origState->as_json;
|
1135
1235
|
return obj;
|
1136
1236
|
}
|
1137
1237
|
|
@@ -1283,6 +1383,28 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
1283
1383
|
return Qnil;
|
1284
1384
|
}
|
1285
1385
|
|
1386
|
+
/*
|
1387
|
+
* call-seq: as_json()
|
1388
|
+
*
|
1389
|
+
* This string is put at the end of a line that holds a JSON array.
|
1390
|
+
*/
|
1391
|
+
static VALUE cState_as_json(VALUE self)
|
1392
|
+
{
|
1393
|
+
GET_STATE(self);
|
1394
|
+
return state->as_json;
|
1395
|
+
}
|
1396
|
+
|
1397
|
+
/*
|
1398
|
+
* call-seq: as_json=(as_json)
|
1399
|
+
*
|
1400
|
+
* This string is put at the end of a line that holds a JSON array.
|
1401
|
+
*/
|
1402
|
+
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
1403
|
+
{
|
1404
|
+
GET_STATE(self);
|
1405
|
+
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
1406
|
+
return Qnil;
|
1407
|
+
}
|
1286
1408
|
|
1287
1409
|
/*
|
1288
1410
|
* call-seq: check_circular?
|
@@ -1504,6 +1626,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
1504
1626
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
1505
1627
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
1506
1628
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
1629
|
+
else if (key == sym_as_json) { state->as_json = rb_convert_type(val, T_DATA, "Proc", "to_proc"); }
|
1507
1630
|
return ST_CONTINUE;
|
1508
1631
|
}
|
1509
1632
|
|
@@ -1564,6 +1687,10 @@ void Init_generator(void)
|
|
1564
1687
|
rb_require("json/common");
|
1565
1688
|
|
1566
1689
|
mJSON = rb_define_module("JSON");
|
1690
|
+
|
1691
|
+
rb_global_variable(&cFragment);
|
1692
|
+
cFragment = rb_const_get(mJSON, rb_intern("Fragment"));
|
1693
|
+
|
1567
1694
|
VALUE mExt = rb_define_module_under(mJSON, "Ext");
|
1568
1695
|
VALUE mGenerator = rb_define_module_under(mExt, "Generator");
|
1569
1696
|
|
@@ -1591,6 +1718,8 @@ void Init_generator(void)
|
|
1591
1718
|
rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
|
1592
1719
|
rb_define_method(cState, "array_nl", cState_array_nl, 0);
|
1593
1720
|
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
|
1721
|
+
rb_define_method(cState, "as_json", cState_as_json, 0);
|
1722
|
+
rb_define_method(cState, "as_json=", cState_as_json_set, 1);
|
1594
1723
|
rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
|
1595
1724
|
rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
|
1596
1725
|
rb_define_method(cState, "script_safe", cState_script_safe, 0);
|
@@ -1611,7 +1740,8 @@ void Init_generator(void)
|
|
1611
1740
|
rb_define_method(cState, "depth=", cState_depth_set, 1);
|
1612
1741
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
1613
1742
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
1614
|
-
|
1743
|
+
rb_define_method(cState, "generate", cState_generate, -1);
|
1744
|
+
rb_define_alias(cState, "generate_new", "generate"); // :nodoc:
|
1615
1745
|
|
1616
1746
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
1617
1747
|
|
@@ -1682,6 +1812,7 @@ void Init_generator(void)
|
|
1682
1812
|
sym_script_safe = ID2SYM(rb_intern("script_safe"));
|
1683
1813
|
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
1684
1814
|
sym_strict = ID2SYM(rb_intern("strict"));
|
1815
|
+
sym_as_json = ID2SYM(rb_intern("as_json"));
|
1685
1816
|
|
1686
1817
|
usascii_encindex = rb_usascii_encindex();
|
1687
1818
|
utf8_encindex = rb_utf8_encindex();
|