json 2.9.0 → 2.11.3

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.
@@ -1,5 +1,6 @@
1
1
  #include "ruby.h"
2
2
  #include "../fbuffer/fbuffer.h"
3
+ #include "../vendor/fpconv.c"
3
4
 
4
5
  #include <math.h>
5
6
  #include <ctype.h>
@@ -12,6 +13,7 @@ typedef struct JSON_Generator_StateStruct {
12
13
  VALUE space_before;
13
14
  VALUE object_nl;
14
15
  VALUE array_nl;
16
+ VALUE as_json;
15
17
 
16
18
  long max_nesting;
17
19
  long depth;
@@ -27,11 +29,11 @@ typedef struct JSON_Generator_StateStruct {
27
29
  #define RB_UNLIKELY(cond) (cond)
28
30
  #endif
29
31
 
30
- static VALUE mJSON, cState, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
32
+ static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
31
33
 
32
34
  static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
33
- static ID sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
34
- sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict;
35
+ static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
36
+ sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
35
37
 
36
38
 
37
39
  #define GET_STATE_TO(self, state) \
@@ -43,7 +45,7 @@ static ID sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl,
43
45
 
44
46
  struct generate_json_data;
45
47
 
46
- typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
48
+ typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
47
49
 
48
50
  struct generate_json_data {
49
51
  FBuffer *buffer;
@@ -55,19 +57,20 @@ struct generate_json_data {
55
57
 
56
58
  static VALUE cState_from_state_s(VALUE self, VALUE opts);
57
59
  static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
58
- static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
59
- static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
60
- static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
61
- static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
62
- static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
63
- static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
64
- static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
60
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
61
+ static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
62
+ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
63
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
64
+ static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
65
+ static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
66
+ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
65
67
  #ifdef RUBY_INTEGER_UNIFICATION
66
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
68
+ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
67
69
  #endif
68
- static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
69
- static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
70
- static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
70
+ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
71
+ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
72
+ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
73
+ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
71
74
 
72
75
  static int usascii_encindex, utf8_encindex, binary_encindex;
73
76
 
@@ -96,6 +99,75 @@ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
96
99
  raise_generator_error_str(invalid_object, str);
97
100
  }
98
101
 
102
+ // 0 - single byte char that don't need to be escaped.
103
+ // (x | 8) - char that needs to be escaped.
104
+ static const unsigned char CHAR_LENGTH_MASK = 7;
105
+ static const unsigned char ESCAPE_MASK = 8;
106
+
107
+ typedef struct _search_state {
108
+ const char *ptr;
109
+ const char *end;
110
+ const char *cursor;
111
+ FBuffer *buffer;
112
+ } search_state;
113
+
114
+ static inline void search_flush(search_state *search)
115
+ {
116
+ fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor);
117
+ search->cursor = search->ptr;
118
+ }
119
+
120
+ static const unsigned char escape_table_basic[256] = {
121
+ // ASCII Control Characters
122
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
123
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
124
+ // ASCII Characters
125
+ 0, 0, 9, 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, 0, 0, 0, 0,
128
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
129
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131
+ };
132
+
133
+ static inline unsigned char search_escape_basic(search_state *search)
134
+ {
135
+ while (search->ptr < search->end) {
136
+ if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) {
137
+ search_flush(search);
138
+ return 1;
139
+ } else {
140
+ search->ptr++;
141
+ }
142
+ }
143
+ search_flush(search);
144
+ return 0;
145
+ }
146
+
147
+ static inline void escape_UTF8_char_basic(search_state *search) {
148
+ const unsigned char ch = (unsigned char)*search->ptr;
149
+ switch (ch) {
150
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
151
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
152
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
153
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
154
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
155
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
156
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
157
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
158
+ default: {
159
+ const char *hexdig = "0123456789abcdef";
160
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
161
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
162
+ scratch[5] = hexdig[ch & 0xf];
163
+ fbuffer_append(search->buffer, scratch, 6);
164
+ break;
165
+ }
166
+ }
167
+ search->ptr++;
168
+ search->cursor = search->ptr;
169
+ }
170
+
99
171
  /* Converts in_string to a JSON string (without the wrapping '"'
100
172
  * characters) in FBuffer out_buffer.
101
173
  *
@@ -106,282 +178,241 @@ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
106
178
  *
107
179
  * - If out_ascii_only: non-ASCII characters (>0x7F)
108
180
  *
109
- * - If out_script_safe: forwardslash, line separator (U+2028), and
181
+ * - If script_safe: forwardslash (/), line separator (U+2028), and
110
182
  * paragraph separator (U+2029)
111
183
  *
112
184
  * Everything else (should be UTF-8) is just passed through and
113
185
  * appended to the result.
114
186
  */
115
- static void convert_UTF8_to_JSON(FBuffer *out_buffer, VALUE str, const char escape_table[256], bool out_script_safe)
187
+ static inline void convert_UTF8_to_JSON(search_state *search)
116
188
  {
117
- const char *hexdig = "0123456789abcdef";
118
- char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
119
-
120
- const char *ptr = RSTRING_PTR(str);
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 */
189
+ while (search_escape_basic(search)) {
190
+ escape_UTF8_char_basic(search);
191
+ }
192
+ }
131
193
 
132
- if (RB_UNLIKELY(ch_len)) {
133
- switch (ch_len) {
134
- case 1: {
135
- FLUSH_POS(1);
136
- switch (ch) {
137
- case '"': fbuffer_append(out_buffer, "\\\"", 2); break;
138
- case '\\': fbuffer_append(out_buffer, "\\\\", 2); break;
139
- case '/': fbuffer_append(out_buffer, "\\/", 2); break;
140
- case '\b': fbuffer_append(out_buffer, "\\b", 2); break;
141
- case '\f': fbuffer_append(out_buffer, "\\f", 2); break;
142
- case '\n': fbuffer_append(out_buffer, "\\n", 2); break;
143
- case '\r': fbuffer_append(out_buffer, "\\r", 2); break;
144
- case '\t': fbuffer_append(out_buffer, "\\t", 2); break;
145
- default: {
146
- scratch[2] = '0';
147
- scratch[3] = '0';
148
- scratch[4] = hexdig[(ch >> 4) & 0xf];
149
- scratch[5] = hexdig[ch & 0xf];
150
- fbuffer_append(out_buffer, scratch, 6);
151
- break;
152
- }
153
- }
194
+ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) {
195
+ const unsigned char ch = (unsigned char)*search->ptr;
196
+ switch (ch_len) {
197
+ case 1: {
198
+ switch (ch) {
199
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
200
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
201
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
202
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
203
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
204
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
205
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
206
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
207
+ default: {
208
+ const char *hexdig = "0123456789abcdef";
209
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
210
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
211
+ scratch[5] = hexdig[ch & 0xf];
212
+ fbuffer_append(search->buffer, scratch, 6);
154
213
  break;
155
214
  }
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
215
  }
176
- } else {
177
- pos++;
216
+ break;
217
+ }
218
+ case 3: {
219
+ if (search->ptr[2] & 1) {
220
+ fbuffer_append(search->buffer, "\\u2029", 6);
221
+ } else {
222
+ fbuffer_append(search->buffer, "\\u2028", 6);
223
+ }
224
+ break;
178
225
  }
179
226
  }
180
- #undef FLUSH_POS
181
-
182
- if (beg < len) {
183
- fbuffer_append(out_buffer, &ptr[beg], len - beg);
184
- }
185
-
186
- RB_GC_GUARD(str);
227
+ search->cursor = (search->ptr += ch_len);
187
228
  }
188
229
 
189
- static const char escape_table[256] = {
230
+ static const unsigned char script_safe_escape_table[256] = {
190
231
  // 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,
232
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
233
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
193
234
  // 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,
235
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/'
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, 0, 0, 0, 0,
238
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
239
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
240
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
200
241
  // 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,
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,
245
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
205
246
  // 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,
247
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
248
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
249
+ // First byte of a 3-byte code point
250
+ 3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029
251
+ //First byte of a 4+ byte code point
252
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
212
253
  };
213
254
 
214
- static const char script_safe_escape_table[256] = {
215
- // ASCII Control Characters
216
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
217
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
218
- // ASCII Characters
219
- 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, // '"' and '/'
220
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
221
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
222
- 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, // '\\'
223
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
224
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
225
- // Continuation byte
226
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
227
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
228
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
229
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
230
- // First byte of a 2-byte code point
231
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
232
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
233
- // First byte of a 4-byte code point
234
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
235
- //First byte of a 4+byte code point
236
- 4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1,
237
- };
238
-
239
- static void convert_ASCII_to_JSON(FBuffer *out_buffer, VALUE str, const char escape_table[256])
255
+ static inline unsigned char search_script_safe_escape(search_state *search)
240
256
  {
241
- const char *hexdig = "0123456789abcdef";
242
- char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
243
-
244
- const char *ptr = RSTRING_PTR(str);
245
- unsigned long len = RSTRING_LEN(str);
257
+ while (search->ptr < search->end) {
258
+ unsigned char ch = (unsigned char)*search->ptr;
259
+ unsigned char ch_len = script_safe_escape_table[ch];
246
260
 
247
- unsigned long beg = 0, pos;
248
-
249
- for (pos = 0; pos < len;) {
250
- unsigned char ch = ptr[pos];
251
- /* JSON encoding */
252
- if (escape_table[ch]) {
253
- if (pos > beg) {
254
- fbuffer_append(out_buffer, &ptr[beg], pos - beg);
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);
261
+ if (RB_UNLIKELY(ch_len)) {
262
+ if (ch_len & ESCAPE_MASK) {
263
+ if (RB_UNLIKELY(ch_len == 11)) {
264
+ const unsigned char *uptr = (const unsigned char *)search->ptr;
265
+ if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) {
266
+ search->ptr += 3;
267
+ continue;
268
+ }
269
+ }
270
+ search_flush(search);
271
+ return ch_len & CHAR_LENGTH_MASK;
272
+ } else {
273
+ search->ptr += ch_len;
273
274
  }
275
+ } else {
276
+ search->ptr++;
274
277
  }
275
-
276
- pos++;
277
- }
278
-
279
- if (beg < len) {
280
- fbuffer_append(out_buffer, &ptr[beg], len - beg);
281
278
  }
282
-
283
- RB_GC_GUARD(str);
279
+ search_flush(search);
280
+ return 0;
284
281
  }
285
282
 
286
- static void convert_UTF8_to_ASCII_only_JSON(FBuffer *out_buffer, VALUE str, const char escape_table[256], bool out_script_safe)
283
+ static void convert_UTF8_to_script_safe_JSON(search_state *search)
287
284
  {
288
- const char *hexdig = "0123456789abcdef";
289
- char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
290
-
291
- const char *ptr = RSTRING_PTR(str);
292
- unsigned long len = RSTRING_LEN(str);
293
-
294
- unsigned long beg = 0, pos = 0;
285
+ unsigned char ch_len;
286
+ while ((ch_len = search_script_safe_escape(search))) {
287
+ escape_UTF8_char(search, ch_len);
288
+ }
289
+ }
295
290
 
296
- #define FLUSH_POS(bytes) if (pos > beg) { fbuffer_append(out_buffer, &ptr[beg], pos - beg); } pos += bytes; beg = pos;
291
+ static const unsigned char ascii_only_escape_table[256] = {
292
+ // ASCII Control Characters
293
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
294
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
295
+ // ASCII Characters
296
+ 0, 0, 9, 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, 0, 0, 0, 0,
299
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
300
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
301
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
302
+ // Continuation byte
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
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
307
+ // First byte of a 2-byte code point
308
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
309
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
310
+ // First byte of a 3-byte code point
311
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
312
+ //First byte of a 4+ byte code point
313
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
314
+ };
297
315
 
298
- while (pos < len) {
299
- unsigned char ch = ptr[pos];
316
+ static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256])
317
+ {
318
+ while (search->ptr < search->end) {
319
+ unsigned char ch = (unsigned char)*search->ptr;
300
320
  unsigned char ch_len = escape_table[ch];
301
321
 
302
322
  if (RB_UNLIKELY(ch_len)) {
303
- switch (ch_len) {
304
- case 1: {
305
- FLUSH_POS(1);
306
- switch (ch) {
307
- case '"': fbuffer_append(out_buffer, "\\\"", 2); break;
308
- case '\\': fbuffer_append(out_buffer, "\\\\", 2); break;
309
- case '/': fbuffer_append(out_buffer, "\\/", 2); break;
310
- case '\b': fbuffer_append(out_buffer, "\\b", 2); break;
311
- case '\f': fbuffer_append(out_buffer, "\\f", 2); break;
312
- case '\n': fbuffer_append(out_buffer, "\\n", 2); break;
313
- case '\r': fbuffer_append(out_buffer, "\\r", 2); break;
314
- case '\t': fbuffer_append(out_buffer, "\\t", 2); break;
315
- default: {
316
- scratch[2] = '0';
317
- scratch[3] = '0';
318
- scratch[4] = hexdig[(ch >> 4) & 0xf];
319
- scratch[5] = hexdig[ch & 0xf];
320
- fbuffer_append(out_buffer, scratch, 6);
321
- break;
322
- }
323
- }
323
+ search_flush(search);
324
+ return ch_len & CHAR_LENGTH_MASK;
325
+ } else {
326
+ search->ptr++;
327
+ }
328
+ }
329
+ search_flush(search);
330
+ return 0;
331
+ }
332
+
333
+ static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len) {
334
+ const unsigned char ch = (unsigned char)*search->ptr;
335
+ switch (ch_len) {
336
+ case 1: {
337
+ switch (ch) {
338
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
339
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
340
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
341
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
342
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
343
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
344
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
345
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
346
+ default: {
347
+ const char *hexdig = "0123456789abcdef";
348
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
349
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
350
+ scratch[5] = hexdig[ch & 0xf];
351
+ fbuffer_append(search->buffer, scratch, 6);
324
352
  break;
325
353
  }
326
- default: {
327
- uint32_t wchar = 0;
328
- switch(ch_len) {
329
- case 2:
330
- wchar = ptr[pos] & 0x1F;
331
- break;
332
- case 3:
333
- wchar = ptr[pos] & 0x0F;
334
- break;
335
- case 4:
336
- wchar = ptr[pos] & 0x07;
337
- break;
338
- }
354
+ }
355
+ break;
356
+ }
357
+ default: {
358
+ const char *hexdig = "0123456789abcdef";
359
+ char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
339
360
 
340
- for (short i = 1; i < ch_len; i++) {
341
- wchar = (wchar << 6) | (ptr[pos+i] & 0x3F);
342
- }
361
+ uint32_t wchar = 0;
343
362
 
344
- FLUSH_POS(ch_len);
363
+ switch(ch_len) {
364
+ case 2:
365
+ wchar = ch & 0x1F;
366
+ break;
367
+ case 3:
368
+ wchar = ch & 0x0F;
369
+ break;
370
+ case 4:
371
+ wchar = ch & 0x07;
372
+ break;
373
+ }
345
374
 
346
- if (wchar <= 0xFFFF) {
347
- scratch[2] = hexdig[wchar >> 12];
348
- scratch[3] = hexdig[(wchar >> 8) & 0xf];
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
- }
375
+ for (short i = 1; i < ch_len; i++) {
376
+ wchar = (wchar << 6) | (search->ptr[i] & 0x3F);
377
+ }
370
378
 
371
- break;
372
- }
379
+ if (wchar <= 0xFFFF) {
380
+ scratch[2] = hexdig[wchar >> 12];
381
+ scratch[3] = hexdig[(wchar >> 8) & 0xf];
382
+ scratch[4] = hexdig[(wchar >> 4) & 0xf];
383
+ scratch[5] = hexdig[wchar & 0xf];
384
+ fbuffer_append(search->buffer, scratch, 6);
385
+ } else {
386
+ uint16_t hi, lo;
387
+ wchar -= 0x10000;
388
+ hi = 0xD800 + (uint16_t)(wchar >> 10);
389
+ lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
390
+
391
+ scratch[2] = hexdig[hi >> 12];
392
+ scratch[3] = hexdig[(hi >> 8) & 0xf];
393
+ scratch[4] = hexdig[(hi >> 4) & 0xf];
394
+ scratch[5] = hexdig[hi & 0xf];
395
+
396
+ scratch[8] = hexdig[lo >> 12];
397
+ scratch[9] = hexdig[(lo >> 8) & 0xf];
398
+ scratch[10] = hexdig[(lo >> 4) & 0xf];
399
+ scratch[11] = hexdig[lo & 0xf];
400
+
401
+ fbuffer_append(search->buffer, scratch, 12);
373
402
  }
374
- } else {
375
- pos++;
403
+
404
+ break;
376
405
  }
377
406
  }
378
- #undef FLUSH_POS
407
+ search->cursor = (search->ptr += ch_len);
408
+ }
379
409
 
380
- if (beg < len) {
381
- fbuffer_append(out_buffer, &ptr[beg], len - beg);
410
+ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256])
411
+ {
412
+ unsigned char ch_len;
413
+ while ((ch_len = search_ascii_only_escape(search, escape_table))) {
414
+ full_escape_UTF8_char(search, ch_len);
382
415
  }
383
-
384
- RB_GC_GUARD(str);
385
416
  }
386
417
 
387
418
  /*
@@ -674,6 +705,7 @@ static void State_mark(void *ptr)
674
705
  rb_gc_mark_movable(state->space_before);
675
706
  rb_gc_mark_movable(state->object_nl);
676
707
  rb_gc_mark_movable(state->array_nl);
708
+ rb_gc_mark_movable(state->as_json);
677
709
  }
678
710
 
679
711
  static void State_compact(void *ptr)
@@ -684,6 +716,7 @@ static void State_compact(void *ptr)
684
716
  state->space_before = rb_gc_location(state->space_before);
685
717
  state->object_nl = rb_gc_location(state->object_nl);
686
718
  state->array_nl = rb_gc_location(state->array_nl);
719
+ state->as_json = rb_gc_location(state->as_json);
687
720
  }
688
721
 
689
722
  static void State_free(void *ptr)
@@ -740,6 +773,7 @@ static void vstate_spill(struct generate_json_data *data)
740
773
  RB_OBJ_WRITTEN(vstate, Qundef, state->space_before);
741
774
  RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
742
775
  RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
776
+ RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
743
777
  }
744
778
 
745
779
  static inline VALUE vstate_get(struct generate_json_data *data)
@@ -768,12 +802,12 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
768
802
  int j;
769
803
 
770
804
  if (arg->iter > 0) fbuffer_append_char(buffer, ',');
771
- if (RB_UNLIKELY(state->object_nl)) {
772
- fbuffer_append_str(buffer, state->object_nl);
805
+ if (RB_UNLIKELY(data->state->object_nl)) {
806
+ fbuffer_append_str(buffer, data->state->object_nl);
773
807
  }
774
- if (RB_UNLIKELY(state->indent)) {
808
+ if (RB_UNLIKELY(data->state->indent)) {
775
809
  for (j = 0; j < depth; j++) {
776
- fbuffer_append_str(buffer, state->indent);
810
+ fbuffer_append_str(buffer, data->state->indent);
777
811
  }
778
812
  }
779
813
 
@@ -795,32 +829,37 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
795
829
  }
796
830
 
797
831
  if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
798
- generate_json_string(buffer, data, state, key_to_s);
832
+ generate_json_string(buffer, data, key_to_s);
799
833
  } else {
800
- generate_json(buffer, data, state, key_to_s);
834
+ generate_json(buffer, data, key_to_s);
801
835
  }
802
- if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before);
836
+ if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before);
803
837
  fbuffer_append_char(buffer, ':');
804
- if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
805
- generate_json(buffer, data, state, val);
838
+ if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
839
+ generate_json(buffer, data, val);
806
840
 
807
841
  arg->iter++;
808
842
  return ST_CONTINUE;
809
843
  }
810
844
 
811
- static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
845
+ static inline long increase_depth(struct generate_json_data *data)
812
846
  {
813
- long max_nesting = state->max_nesting;
847
+ JSON_Generator_State *state = data->state;
814
848
  long depth = ++state->depth;
815
- int j;
816
-
817
- if (max_nesting != 0 && depth > max_nesting) {
849
+ if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
818
850
  rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
819
851
  }
852
+ return depth;
853
+ }
854
+
855
+ static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
856
+ {
857
+ int j;
858
+ long depth = increase_depth(data);
820
859
 
821
860
  if (RHASH_SIZE(obj) == 0) {
822
861
  fbuffer_append(buffer, "{}", 2);
823
- --state->depth;
862
+ --data->state->depth;
824
863
  return;
825
864
  }
826
865
 
@@ -832,53 +871,49 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
832
871
  };
833
872
  rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
834
873
 
835
- depth = --state->depth;
836
- if (RB_UNLIKELY(state->object_nl)) {
837
- fbuffer_append_str(buffer, state->object_nl);
838
- if (RB_UNLIKELY(state->indent)) {
874
+ depth = --data->state->depth;
875
+ if (RB_UNLIKELY(data->state->object_nl)) {
876
+ fbuffer_append_str(buffer, data->state->object_nl);
877
+ if (RB_UNLIKELY(data->state->indent)) {
839
878
  for (j = 0; j < depth; j++) {
840
- fbuffer_append_str(buffer, state->indent);
879
+ fbuffer_append_str(buffer, data->state->indent);
841
880
  }
842
881
  }
843
882
  }
844
883
  fbuffer_append_char(buffer, '}');
845
884
  }
846
885
 
847
- static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
886
+ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
848
887
  {
849
- long max_nesting = state->max_nesting;
850
- long depth = ++state->depth;
851
888
  int i, j;
852
- if (max_nesting != 0 && depth > max_nesting) {
853
- rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
854
- }
889
+ long depth = increase_depth(data);
855
890
 
856
891
  if (RARRAY_LEN(obj) == 0) {
857
892
  fbuffer_append(buffer, "[]", 2);
858
- --state->depth;
893
+ --data->state->depth;
859
894
  return;
860
895
  }
861
896
 
862
897
  fbuffer_append_char(buffer, '[');
863
- if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
898
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
864
899
  for(i = 0; i < RARRAY_LEN(obj); i++) {
865
900
  if (i > 0) {
866
901
  fbuffer_append_char(buffer, ',');
867
- if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
902
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
868
903
  }
869
- if (RB_UNLIKELY(state->indent)) {
904
+ if (RB_UNLIKELY(data->state->indent)) {
870
905
  for (j = 0; j < depth; j++) {
871
- fbuffer_append_str(buffer, state->indent);
906
+ fbuffer_append_str(buffer, data->state->indent);
872
907
  }
873
908
  }
874
- generate_json(buffer, data, state, RARRAY_AREF(obj, i));
909
+ generate_json(buffer, data, RARRAY_AREF(obj, i));
875
910
  }
876
- state->depth = --depth;
877
- if (RB_UNLIKELY(state->array_nl)) {
878
- fbuffer_append_str(buffer, state->array_nl);
879
- if (RB_UNLIKELY(state->indent)) {
911
+ data->state->depth = --depth;
912
+ if (RB_UNLIKELY(data->state->array_nl)) {
913
+ fbuffer_append_str(buffer, data->state->array_nl);
914
+ if (RB_UNLIKELY(data->state->indent)) {
880
915
  for (j = 0; j < depth; j++) {
881
- fbuffer_append_str(buffer, state->indent);
916
+ fbuffer_append_str(buffer, data->state->indent);
882
917
  }
883
918
  }
884
919
  }
@@ -927,21 +962,28 @@ static inline VALUE ensure_valid_encoding(VALUE str)
927
962
  return str;
928
963
  }
929
964
 
930
- static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
965
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
931
966
  {
932
967
  obj = ensure_valid_encoding(obj);
933
968
 
934
969
  fbuffer_append_char(buffer, '"');
935
970
 
971
+ long len;
972
+ search_state search;
973
+ search.buffer = buffer;
974
+ RSTRING_GETMEM(obj, search.ptr, len);
975
+ search.cursor = search.ptr;
976
+ search.end = search.ptr + len;
977
+
936
978
  switch(rb_enc_str_coderange(obj)) {
937
979
  case ENC_CODERANGE_7BIT:
938
- convert_ASCII_to_JSON(buffer, obj, state->script_safe ? script_safe_escape_table : escape_table);
939
- break;
940
980
  case ENC_CODERANGE_VALID:
941
- if (RB_UNLIKELY(state->ascii_only)) {
942
- convert_UTF8_to_ASCII_only_JSON(buffer, obj, state->script_safe ? script_safe_escape_table : escape_table, state->script_safe);
981
+ if (RB_UNLIKELY(data->state->ascii_only)) {
982
+ convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
983
+ } else if (RB_UNLIKELY(data->state->script_safe)) {
984
+ convert_UTF8_to_script_safe_JSON(&search);
943
985
  } else {
944
- convert_UTF8_to_JSON(buffer, obj, state->script_safe ? script_safe_escape_table : escape_table, state->script_safe);
986
+ convert_UTF8_to_JSON(&search);
945
987
  }
946
988
  break;
947
989
  default:
@@ -951,69 +993,127 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
951
993
  fbuffer_append_char(buffer, '"');
952
994
  }
953
995
 
954
- static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
996
+ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
997
+ {
998
+ VALUE tmp;
999
+ if (rb_respond_to(obj, i_to_json)) {
1000
+ tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
1001
+ Check_Type(tmp, T_STRING);
1002
+ fbuffer_append_str(buffer, tmp);
1003
+ } else {
1004
+ tmp = rb_funcall(obj, i_to_s, 0);
1005
+ Check_Type(tmp, T_STRING);
1006
+ generate_json_string(buffer, data, tmp);
1007
+ }
1008
+ }
1009
+
1010
+ static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1011
+ {
1012
+ if (data->state->strict) {
1013
+ generate_json_string(buffer, data, rb_sym2str(obj));
1014
+ } else {
1015
+ generate_json_fallback(buffer, data, obj);
1016
+ }
1017
+ }
1018
+
1019
+ static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
955
1020
  {
956
1021
  fbuffer_append(buffer, "null", 4);
957
1022
  }
958
1023
 
959
- static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1024
+ static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
960
1025
  {
961
1026
  fbuffer_append(buffer, "false", 5);
962
1027
  }
963
1028
 
964
- static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1029
+ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
965
1030
  {
966
1031
  fbuffer_append(buffer, "true", 4);
967
1032
  }
968
1033
 
969
- static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1034
+ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
970
1035
  {
971
1036
  fbuffer_append_long(buffer, FIX2LONG(obj));
972
1037
  }
973
1038
 
974
- static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1039
+ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
975
1040
  {
976
1041
  VALUE tmp = rb_funcall(obj, i_to_s, 0);
977
1042
  fbuffer_append_str(buffer, tmp);
978
1043
  }
979
1044
 
980
1045
  #ifdef RUBY_INTEGER_UNIFICATION
981
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1046
+ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
982
1047
  {
983
1048
  if (FIXNUM_P(obj))
984
- generate_json_fixnum(buffer, data, state, obj);
1049
+ generate_json_fixnum(buffer, data, obj);
985
1050
  else
986
- generate_json_bignum(buffer, data, state, obj);
1051
+ generate_json_bignum(buffer, data, obj);
987
1052
  }
988
1053
  #endif
989
1054
 
990
- static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1055
+ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
991
1056
  {
992
1057
  double value = RFLOAT_VALUE(obj);
993
- char allow_nan = state->allow_nan;
994
- VALUE tmp = rb_funcall(obj, i_to_s, 0);
995
- if (!allow_nan) {
996
- if (isinf(value) || isnan(value)) {
997
- raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", tmp);
1058
+ char allow_nan = data->state->allow_nan;
1059
+ if (isinf(value) || isnan(value)) {
1060
+ /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
1061
+ if (!allow_nan) {
1062
+ if (data->state->strict && data->state->as_json) {
1063
+ VALUE casted_obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1064
+ if (casted_obj != obj) {
1065
+ increase_depth(data);
1066
+ generate_json(buffer, data, casted_obj);
1067
+ data->state->depth--;
1068
+ return;
1069
+ }
1070
+ }
1071
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
998
1072
  }
1073
+
1074
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
1075
+ fbuffer_append_str(buffer, tmp);
1076
+ return;
999
1077
  }
1000
- fbuffer_append_str(buffer, tmp);
1078
+
1079
+ /* This implementation writes directly into the buffer. We reserve
1080
+ * the 24 characters that fpconv_dtoa states as its maximum, plus
1081
+ * 2 more characters for the potential ".0" suffix.
1082
+ */
1083
+ fbuffer_inc_capa(buffer, 26);
1084
+ char* d = buffer->ptr + buffer->len;
1085
+ int len = fpconv_dtoa(value, d);
1086
+
1087
+ /* fpconv_dtoa converts a float to its shortest string representation,
1088
+ * but it adds a ".0" if this is a plain integer.
1089
+ */
1090
+ buffer->len += len;
1001
1091
  }
1002
1092
 
1003
- static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1093
+ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1004
1094
  {
1005
- VALUE tmp;
1095
+ VALUE fragment = RSTRUCT_GET(obj, 0);
1096
+ Check_Type(fragment, T_STRING);
1097
+ fbuffer_append_str(buffer, fragment);
1098
+ }
1099
+
1100
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1101
+ {
1102
+ bool as_json_called = false;
1103
+ start:
1006
1104
  if (obj == Qnil) {
1007
- generate_json_null(buffer, data, state, obj);
1105
+ generate_json_null(buffer, data, obj);
1008
1106
  } else if (obj == Qfalse) {
1009
- generate_json_false(buffer, data, state, obj);
1107
+ generate_json_false(buffer, data, obj);
1010
1108
  } else if (obj == Qtrue) {
1011
- generate_json_true(buffer, data, state, obj);
1109
+ generate_json_true(buffer, data, obj);
1012
1110
  } else if (RB_SPECIAL_CONST_P(obj)) {
1013
1111
  if (RB_FIXNUM_P(obj)) {
1014
- generate_json_fixnum(buffer, data, state, obj);
1112
+ generate_json_fixnum(buffer, data, obj);
1015
1113
  } else if (RB_FLONUM_P(obj)) {
1016
- generate_json_float(buffer, data, state, obj);
1114
+ generate_json_float(buffer, data, obj);
1115
+ } else if (RB_STATIC_SYM_P(obj)) {
1116
+ generate_json_symbol(buffer, data, obj);
1017
1117
  } else {
1018
1118
  goto general;
1019
1119
  }
@@ -1021,36 +1121,43 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
1021
1121
  VALUE klass = RBASIC_CLASS(obj);
1022
1122
  switch (RB_BUILTIN_TYPE(obj)) {
1023
1123
  case T_BIGNUM:
1024
- generate_json_bignum(buffer, data, state, obj);
1124
+ generate_json_bignum(buffer, data, obj);
1025
1125
  break;
1026
1126
  case T_HASH:
1027
1127
  if (klass != rb_cHash) goto general;
1028
- generate_json_object(buffer, data, state, obj);
1128
+ generate_json_object(buffer, data, obj);
1029
1129
  break;
1030
1130
  case T_ARRAY:
1031
1131
  if (klass != rb_cArray) goto general;
1032
- generate_json_array(buffer, data, state, obj);
1132
+ generate_json_array(buffer, data, obj);
1033
1133
  break;
1034
1134
  case T_STRING:
1035
1135
  if (klass != rb_cString) goto general;
1036
- generate_json_string(buffer, data, state, obj);
1136
+ generate_json_string(buffer, data, obj);
1137
+ break;
1138
+ case T_SYMBOL:
1139
+ generate_json_symbol(buffer, data, obj);
1037
1140
  break;
1038
1141
  case T_FLOAT:
1039
1142
  if (klass != rb_cFloat) goto general;
1040
- generate_json_float(buffer, data, state, obj);
1143
+ generate_json_float(buffer, data, obj);
1144
+ break;
1145
+ case T_STRUCT:
1146
+ if (klass != cFragment) goto general;
1147
+ generate_json_fragment(buffer, data, obj);
1041
1148
  break;
1042
1149
  default:
1043
1150
  general:
1044
- if (state->strict) {
1045
- raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
1046
- } else if (rb_respond_to(obj, i_to_json)) {
1047
- tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
1048
- Check_Type(tmp, T_STRING);
1049
- fbuffer_append_str(buffer, tmp);
1151
+ if (data->state->strict) {
1152
+ if (RTEST(data->state->as_json) && !as_json_called) {
1153
+ obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1154
+ as_json_called = true;
1155
+ goto start;
1156
+ } else {
1157
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
1158
+ }
1050
1159
  } else {
1051
- tmp = rb_funcall(obj, i_to_s, 0);
1052
- Check_Type(tmp, T_STRING);
1053
- generate_json_string(buffer, data, state, tmp);
1160
+ generate_json_fallback(buffer, data, obj);
1054
1161
  }
1055
1162
  }
1056
1163
  }
@@ -1060,7 +1167,7 @@ static VALUE generate_json_try(VALUE d)
1060
1167
  {
1061
1168
  struct generate_json_data *data = (struct generate_json_data *)d;
1062
1169
 
1063
- data->func(data->buffer, data, data->state, data->obj);
1170
+ data->func(data->buffer, data, data->obj);
1064
1171
 
1065
1172
  return Qnil;
1066
1173
  }
@@ -1097,8 +1204,19 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
1097
1204
  return fbuffer_finalize(&buffer);
1098
1205
  }
1099
1206
 
1100
- static VALUE cState_generate(VALUE self, VALUE obj, VALUE io)
1207
+ /* call-seq:
1208
+ * generate(obj) -> String
1209
+ * generate(obj, anIO) -> anIO
1210
+ *
1211
+ * Generates a valid JSON document from object +obj+ and returns the
1212
+ * result. If no valid JSON document can be created this method raises a
1213
+ * GeneratorError exception.
1214
+ */
1215
+ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
1101
1216
  {
1217
+ rb_check_arity(argc, 1, 2);
1218
+ VALUE obj = argv[0];
1219
+ VALUE io = argc > 1 ? argv[1] : Qnil;
1102
1220
  VALUE result = cState_partial_generate(self, obj, generate_json, io);
1103
1221
  GET_STATE(self);
1104
1222
  (void)state;
@@ -1132,6 +1250,7 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
1132
1250
  objState->space_before = origState->space_before;
1133
1251
  objState->object_nl = origState->object_nl;
1134
1252
  objState->array_nl = origState->array_nl;
1253
+ objState->as_json = origState->as_json;
1135
1254
  return obj;
1136
1255
  }
1137
1256
 
@@ -1283,6 +1402,28 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
1283
1402
  return Qnil;
1284
1403
  }
1285
1404
 
1405
+ /*
1406
+ * call-seq: as_json()
1407
+ *
1408
+ * This string is put at the end of a line that holds a JSON array.
1409
+ */
1410
+ static VALUE cState_as_json(VALUE self)
1411
+ {
1412
+ GET_STATE(self);
1413
+ return state->as_json;
1414
+ }
1415
+
1416
+ /*
1417
+ * call-seq: as_json=(as_json)
1418
+ *
1419
+ * This string is put at the end of a line that holds a JSON array.
1420
+ */
1421
+ static VALUE cState_as_json_set(VALUE self, VALUE as_json)
1422
+ {
1423
+ GET_STATE(self);
1424
+ RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
1425
+ return Qnil;
1426
+ }
1286
1427
 
1287
1428
  /*
1288
1429
  * call-seq: check_circular?
@@ -1504,6 +1645,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1504
1645
  else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
1505
1646
  else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
1506
1647
  else if (key == sym_strict) { state->strict = RTEST(val); }
1648
+ else if (key == sym_as_json) { state->as_json = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse; }
1507
1649
  return ST_CONTINUE;
1508
1650
  }
1509
1651
 
@@ -1564,6 +1706,10 @@ void Init_generator(void)
1564
1706
  rb_require("json/common");
1565
1707
 
1566
1708
  mJSON = rb_define_module("JSON");
1709
+
1710
+ rb_global_variable(&cFragment);
1711
+ cFragment = rb_const_get(mJSON, rb_intern("Fragment"));
1712
+
1567
1713
  VALUE mExt = rb_define_module_under(mJSON, "Ext");
1568
1714
  VALUE mGenerator = rb_define_module_under(mExt, "Generator");
1569
1715
 
@@ -1591,6 +1737,8 @@ void Init_generator(void)
1591
1737
  rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
1592
1738
  rb_define_method(cState, "array_nl", cState_array_nl, 0);
1593
1739
  rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
1740
+ rb_define_method(cState, "as_json", cState_as_json, 0);
1741
+ rb_define_method(cState, "as_json=", cState_as_json_set, 1);
1594
1742
  rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
1595
1743
  rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
1596
1744
  rb_define_method(cState, "script_safe", cState_script_safe, 0);
@@ -1611,7 +1759,8 @@ void Init_generator(void)
1611
1759
  rb_define_method(cState, "depth=", cState_depth_set, 1);
1612
1760
  rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
1613
1761
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
1614
- rb_define_private_method(cState, "_generate", cState_generate, 2);
1762
+ rb_define_method(cState, "generate", cState_generate, -1);
1763
+ rb_define_alias(cState, "generate_new", "generate"); // :nodoc:
1615
1764
 
1616
1765
  rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
1617
1766
 
@@ -1682,6 +1831,7 @@ void Init_generator(void)
1682
1831
  sym_script_safe = ID2SYM(rb_intern("script_safe"));
1683
1832
  sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
1684
1833
  sym_strict = ID2SYM(rb_intern("strict"));
1834
+ sym_as_json = ID2SYM(rb_intern("as_json"));
1685
1835
 
1686
1836
  usascii_encindex = rb_usascii_encindex();
1687
1837
  utf8_encindex = rb_utf8_encindex();