json 2.6.1 → 2.21.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/CHANGES.md +370 -17
  4. data/LEGAL +20 -0
  5. data/README.md +96 -213
  6. data/ext/json/ext/fbuffer/fbuffer.h +190 -118
  7. data/ext/json/ext/generator/extconf.rb +17 -2
  8. data/ext/json/ext/generator/generator.c +1532 -1074
  9. data/ext/json/ext/json.h +183 -0
  10. data/ext/json/ext/parser/extconf.rb +30 -25
  11. data/ext/json/ext/parser/parser.c +2837 -3258
  12. data/ext/json/ext/simd/conf.rb +24 -0
  13. data/ext/json/ext/simd/simd.h +208 -0
  14. data/ext/json/ext/vendor/fast_float_parser.h +814 -0
  15. data/ext/json/ext/vendor/fpconv.c +480 -0
  16. data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
  17. data/json.gemspec +48 -53
  18. data/lib/json/add/bigdecimal.rb +39 -10
  19. data/lib/json/add/complex.rb +29 -6
  20. data/lib/json/add/core.rb +2 -1
  21. data/lib/json/add/date.rb +27 -7
  22. data/lib/json/add/date_time.rb +26 -9
  23. data/lib/json/add/exception.rb +25 -7
  24. data/lib/json/add/ostruct.rb +32 -9
  25. data/lib/json/add/range.rb +33 -8
  26. data/lib/json/add/rational.rb +28 -6
  27. data/lib/json/add/regexp.rb +26 -8
  28. data/lib/json/add/set.rb +25 -6
  29. data/lib/json/add/string.rb +35 -0
  30. data/lib/json/add/struct.rb +29 -7
  31. data/lib/json/add/symbol.rb +34 -7
  32. data/lib/json/add/time.rb +29 -15
  33. data/lib/json/common.rb +746 -267
  34. data/lib/json/ext/generator/state.rb +104 -0
  35. data/lib/json/ext.rb +61 -5
  36. data/lib/json/generic_object.rb +7 -11
  37. data/lib/json/truffle_ruby/generator.rb +790 -0
  38. data/lib/json/version.rb +3 -7
  39. data/lib/json.rb +134 -24
  40. metadata +22 -26
  41. data/VERSION +0 -1
  42. data/ext/json/ext/generator/depend +0 -1
  43. data/ext/json/ext/generator/generator.h +0 -174
  44. data/ext/json/ext/parser/depend +0 -1
  45. data/ext/json/ext/parser/parser.h +0 -96
  46. data/ext/json/ext/parser/parser.rl +0 -977
  47. data/ext/json/extconf.rb +0 -3
  48. data/lib/json/pure/generator.rb +0 -479
  49. data/lib/json/pure/parser.rb +0 -337
  50. data/lib/json/pure.rb +0 -15
  51. /data/{LICENSE → COPYING} +0 -0
@@ -1,1126 +1,1364 @@
1
+ #include "../json.h"
1
2
  #include "../fbuffer/fbuffer.h"
2
- #include "generator.h"
3
+ #include "../vendor/fpconv.c"
3
4
 
4
- static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
5
- mHash, mArray,
6
- #ifdef RUBY_INTEGER_UNIFICATION
7
- mInteger,
8
- #else
9
- mFixnum, mBignum,
10
- #endif
11
- mFloat, mString, mString_Extend,
12
- mTrueClass, mFalseClass, mNilClass, eGeneratorError,
13
- eNestingError;
5
+ #include <math.h>
6
+ #include <ctype.h>
14
7
 
15
- static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
16
- i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
17
- i_pack, i_unpack, i_create_id, i_extend, i_key_p,
18
- i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
19
- i_buffer_initial_length, i_dup, i_escape_slash;
8
+ #include "../simd/simd.h"
20
9
 
21
- /*
22
- * Copyright 2001-2004 Unicode, Inc.
23
- *
24
- * Disclaimer
25
- *
26
- * This source code is provided as is by Unicode, Inc. No claims are
27
- * made as to fitness for any particular purpose. No warranties of any
28
- * kind are expressed or implied. The recipient agrees to determine
29
- * applicability of information provided. If this file has been
30
- * purchased on magnetic or optical media from Unicode, Inc., the
31
- * sole remedy for any claim will be exchange of defective media
32
- * within 90 days of receipt.
33
- *
34
- * Limitations on Rights to Redistribute This Code
35
- *
36
- * Unicode, Inc. hereby grants the right to freely use the information
37
- * supplied in this file in the creation of products supporting the
38
- * Unicode Standard, and to make copies of this file in any form
39
- * for internal or external distribution as long as this notice
40
- * remains attached.
41
- */
10
+ /* ruby api and some helpers */
42
11
 
43
- /*
44
- * Index into the table below with the first byte of a UTF-8 sequence to
45
- * get the number of trailing bytes that are supposed to follow it.
46
- * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
47
- * left as-is for anyone who may want to do such conversion, which was
48
- * allowed in earlier algorithms.
49
- */
50
- static const char trailingBytesForUTF8[256] = {
51
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
52
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
53
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
54
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
55
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
58
- 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
12
+ enum duplicate_key_action {
13
+ JSON_DEPRECATED = 0,
14
+ JSON_IGNORE,
15
+ JSON_RAISE,
59
16
  };
60
17
 
61
- /*
62
- * Magic values subtracted from a buffer value during UTF8 conversion.
63
- * This table contains as many values as there might be trailing bytes
64
- * in a UTF-8 sequence.
65
- */
66
- static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
67
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
18
+ typedef struct JSON_Generator_StateStruct {
19
+ VALUE indent;
20
+ VALUE space;
21
+ VALUE space_before;
22
+ VALUE object_nl;
23
+ VALUE array_nl;
24
+ VALUE as_json;
68
25
 
69
- /*
70
- * Utility routine to tell whether a sequence of bytes is legal UTF-8.
71
- * This must be called with the length pre-determined by the first byte.
72
- * If not calling this from ConvertUTF8to*, then the length can be set by:
73
- * length = trailingBytesForUTF8[*source]+1;
74
- * and the sequence is illegal right away if there aren't that many bytes
75
- * available.
76
- * If presented with a length > 4, this returns 0. The Unicode
77
- * definition of UTF-8 goes up to 4-byte sequences.
78
- */
79
- static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length)
80
- {
81
- UTF8 a;
82
- const UTF8 *srcptr = source+length;
83
- switch (length) {
84
- default: return 0;
85
- /* Everything else falls through when "1"... */
86
- case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
87
- case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
88
- case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
89
-
90
- switch (*source) {
91
- /* no fall-through in this inner switch */
92
- case 0xE0: if (a < 0xA0) return 0; break;
93
- case 0xED: if (a > 0x9F) return 0; break;
94
- case 0xF0: if (a < 0x90) return 0; break;
95
- case 0xF4: if (a > 0x8F) return 0; break;
96
- default: if (a < 0x80) return 0;
97
- }
26
+ long max_nesting;
27
+ long depth;
28
+ long buffer_initial_length;
98
29
 
99
- case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
100
- }
101
- if (*source > 0xF4) return 0;
102
- return 1;
103
- }
30
+ enum duplicate_key_action on_duplicate_key;
104
31
 
105
- /* Escapes the UTF16 character and stores the result in the buffer buf. */
106
- static void unicode_escape(char *buf, UTF16 character)
107
- {
108
- const char *digits = "0123456789abcdef";
32
+ bool as_json_single_arg;
33
+ bool allow_nan;
34
+ bool ascii_only;
35
+ bool script_safe;
36
+ bool strict;
37
+ VALUE sort_keys;
38
+ } JSON_Generator_State;
109
39
 
110
- buf[2] = digits[character >> 12];
111
- buf[3] = digits[(character >> 8) & 0xf];
112
- buf[4] = digits[(character >> 4) & 0xf];
113
- buf[5] = digits[character & 0xf];
114
- }
40
+ static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc;
41
+
42
+ static ID i_to_s, i_to_json, i_new, i_encode;
43
+ static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key,
44
+ sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json, sym_sort_keys;
45
+
46
+
47
+ #define GET_STATE_TO(self, state) \
48
+ TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state)
49
+
50
+ #define GET_STATE(self) \
51
+ JSON_Generator_State *state; \
52
+ GET_STATE_TO(self, state)
115
53
 
116
- /* Escapes the UTF16 character and stores the result in the buffer buf, then
117
- * the buffer buf is appended to the FBuffer buffer. */
118
- static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
119
- character)
54
+ struct generate_json_data;
55
+
56
+ typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
57
+
58
+ struct generate_json_data {
59
+ FBuffer *buffer;
60
+ VALUE vstate;
61
+ JSON_Generator_State *state;
62
+ VALUE obj;
63
+ generator_func func;
64
+ long depth;
65
+ };
66
+
67
+ static SIMD_Implementation simd_impl;
68
+
69
+ static VALUE cState_from_state_s(VALUE self, VALUE opts);
70
+ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
71
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
72
+ static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
73
+ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
74
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
75
+ static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
76
+ static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
77
+ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
78
+ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
79
+ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
80
+ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
81
+ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
82
+
83
+ static int usascii_encindex, utf8_encindex, binary_encindex;
84
+
85
+ NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
120
86
  {
121
- unicode_escape(buf, character);
122
- fbuffer_append(buffer, buf, 6);
87
+ rb_enc_associate_index(str, utf8_encindex);
88
+ VALUE exc = rb_exc_new_str(eGeneratorError, str);
89
+ rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
90
+ rb_exc_raise(exc);
123
91
  }
124
92
 
125
- /* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
126
- * and control characters are JSON escaped. */
127
- static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash)
93
+ #ifdef RBIMPL_ATTR_FORMAT
94
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
95
+ #endif
96
+ NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
128
97
  {
129
- const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
130
- const UTF8 *sourceEnd = source + RSTRING_LEN(string);
131
- char buf[6] = { '\\', 'u' };
98
+ va_list args;
99
+ va_start(args, fmt);
100
+ VALUE str = rb_vsprintf(fmt, args);
101
+ va_end(args);
102
+ raise_generator_error_str(invalid_object, str);
103
+ }
132
104
 
133
- while (source < sourceEnd) {
134
- UTF32 ch = 0;
135
- unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
136
- if (source + extraBytesToRead >= sourceEnd) {
137
- rb_raise(rb_path2class("JSON::GeneratorError"),
138
- "partial character in source, but hit end");
139
- }
140
- if (!isLegalUTF8(source, extraBytesToRead+1)) {
141
- rb_raise(rb_path2class("JSON::GeneratorError"),
142
- "source sequence is illegal/malformed utf-8");
143
- }
144
- /*
145
- * The cases all fall through. See "Note A" below.
146
- */
147
- switch (extraBytesToRead) {
148
- case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
149
- case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
150
- case 3: ch += *source++; ch <<= 6;
151
- case 2: ch += *source++; ch <<= 6;
152
- case 1: ch += *source++; ch <<= 6;
153
- case 0: ch += *source++;
154
- }
155
- ch -= offsetsFromUTF8[extraBytesToRead];
156
-
157
- if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
158
- /* UTF-16 surrogate values are illegal in UTF-32 */
159
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
160
- #if UNI_STRICT_CONVERSION
161
- source -= (extraBytesToRead+1); /* return to the illegal value itself */
162
- rb_raise(rb_path2class("JSON::GeneratorError"),
163
- "source sequence is illegal/malformed utf-8");
164
- #else
165
- unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
166
- #endif
167
- } else {
168
- /* normal case */
169
- if (ch >= 0x20 && ch <= 0x7f) {
170
- switch (ch) {
171
- case '\\':
172
- fbuffer_append(buffer, "\\\\", 2);
173
- break;
174
- case '"':
175
- fbuffer_append(buffer, "\\\"", 2);
176
- break;
177
- case '/':
178
- if(escape_slash) {
179
- fbuffer_append(buffer, "\\/", 2);
180
- break;
181
- }
182
- default:
183
- fbuffer_append_char(buffer, (char)ch);
184
- break;
185
- }
186
- } else {
187
- switch (ch) {
188
- case '\n':
189
- fbuffer_append(buffer, "\\n", 2);
190
- break;
191
- case '\r':
192
- fbuffer_append(buffer, "\\r", 2);
193
- break;
194
- case '\t':
195
- fbuffer_append(buffer, "\\t", 2);
196
- break;
197
- case '\f':
198
- fbuffer_append(buffer, "\\f", 2);
199
- break;
200
- case '\b':
201
- fbuffer_append(buffer, "\\b", 2);
202
- break;
203
- default:
204
- unicode_escape_to_buffer(buffer, buf, (UTF16) ch);
205
- break;
206
- }
207
- }
208
- }
209
- } else if (ch > UNI_MAX_UTF16) {
210
- #if UNI_STRICT_CONVERSION
211
- source -= (extraBytesToRead+1); /* return to the start */
212
- rb_raise(rb_path2class("JSON::GeneratorError"),
213
- "source sequence is illegal/malformed utf8");
105
+ // 0 - single byte char that don't need to be escaped.
106
+ // (x | 8) - char that needs to be escaped.
107
+ static const unsigned char CHAR_LENGTH_MASK = 7;
108
+ static const unsigned char ESCAPE_MASK = 8;
109
+
110
+ typedef struct _search_state {
111
+ const char *ptr;
112
+ const char *end;
113
+ const char *cursor;
114
+ FBuffer *buffer;
115
+
116
+ #ifdef HAVE_SIMD
117
+ const char *chunk_base;
118
+ const char *chunk_end;
119
+ bool has_matches;
120
+
121
+ #if defined(HAVE_SIMD_NEON)
122
+ uint64_t matches_mask;
123
+ #elif defined(HAVE_SIMD_SSE2)
124
+ int matches_mask;
214
125
  #else
215
- unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
216
- #endif
217
- } else {
218
- /* target is a character in range 0xFFFF - 0x10FFFF. */
219
- ch -= halfBase;
220
- unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
221
- unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
222
- }
126
+ #error "Unknown SIMD Implementation."
127
+ #endif /* HAVE_SIMD_NEON */
128
+ #endif /* HAVE_SIMD */
129
+ } search_state;
130
+
131
+ ALWAYS_INLINE(static) void search_flush(search_state *search)
132
+ {
133
+ // Do not remove this conditional without profiling, specifically escape-heavy text.
134
+ // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
135
+ // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
136
+ // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
137
+ // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
138
+ // nothing needs to be flushed, we can save a few memory references with this conditional.
139
+ if (search->ptr > search->cursor) {
140
+ fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor);
141
+ search->cursor = search->ptr;
223
142
  }
224
- RB_GC_GUARD(string);
225
- }
226
-
227
- /* Converts string to a JSON string in FBuffer buffer, where only the
228
- * characters required by the JSON standard are JSON escaped. The remaining
229
- * characters (should be UTF8) are just passed through and appended to the
230
- * result. */
231
- static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash)
232
- {
233
- const char *ptr = RSTRING_PTR(string), *p;
234
- unsigned long len = RSTRING_LEN(string), start = 0, end = 0;
235
- const char *escape = NULL;
236
- int escape_len;
237
- unsigned char c;
238
- char buf[6] = { '\\', 'u' };
239
- int ascii_only = rb_enc_str_asciionly_p(string);
240
-
241
- for (start = 0, end = 0; end < len;) {
242
- p = ptr + end;
243
- c = (unsigned char) *p;
244
- if (c < 0x20) {
245
- switch (c) {
246
- case '\n':
247
- escape = "\\n";
248
- escape_len = 2;
249
- break;
250
- case '\r':
251
- escape = "\\r";
252
- escape_len = 2;
253
- break;
254
- case '\t':
255
- escape = "\\t";
256
- escape_len = 2;
257
- break;
258
- case '\f':
259
- escape = "\\f";
260
- escape_len = 2;
261
- break;
262
- case '\b':
263
- escape = "\\b";
264
- escape_len = 2;
265
- break;
266
- default:
267
- unicode_escape(buf, (UTF16) *p);
268
- escape = buf;
269
- escape_len = 6;
270
- break;
271
- }
143
+ }
144
+
145
+ static const unsigned char escape_table_basic[256] = {
146
+ // ASCII Control Characters
147
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
148
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
149
+ // ASCII Characters
150
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
151
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
154
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156
+ };
157
+
158
+ static inline unsigned char search_escape_basic(search_state *search)
159
+ {
160
+ while (search->ptr < search->end) {
161
+ if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) {
162
+ search_flush(search);
163
+ return 1;
272
164
  } else {
273
- switch (c) {
274
- case '\\':
275
- escape = "\\\\";
276
- escape_len = 2;
277
- break;
278
- case '"':
279
- escape = "\\\"";
280
- escape_len = 2;
281
- break;
282
- case '/':
283
- if(escape_slash) {
284
- escape = "\\/";
285
- escape_len = 2;
286
- break;
287
- }
288
- default:
289
- {
290
- unsigned short clen = 1;
291
- if (!ascii_only) {
292
- clen += trailingBytesForUTF8[c];
293
- if (end + clen > len) {
294
- rb_raise(rb_path2class("JSON::GeneratorError"),
295
- "partial character in source, but hit end");
296
- }
297
- if (!isLegalUTF8((UTF8 *) p, clen)) {
298
- rb_raise(rb_path2class("JSON::GeneratorError"),
299
- "source sequence is illegal/malformed utf-8");
300
- }
301
- }
302
- end += clen;
303
- }
304
- continue;
305
- break;
306
- }
165
+ search->ptr++;
307
166
  }
308
- fbuffer_append(buffer, ptr + start, end - start);
309
- fbuffer_append(buffer, escape, escape_len);
310
- start = ++end;
311
- escape = NULL;
312
167
  }
313
- fbuffer_append(buffer, ptr + start, end - start);
168
+ search_flush(search);
169
+ return 0;
314
170
  }
315
171
 
316
- static char *fstrndup(const char *ptr, unsigned long len) {
317
- char *result;
318
- if (len <= 0) return NULL;
319
- result = ALLOC_N(char, len);
320
- memcpy(result, ptr, len);
321
- return result;
172
+ ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
173
+ {
174
+ const unsigned char ch = (unsigned char)*search->ptr;
175
+ switch (ch) {
176
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
177
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
178
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
179
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
180
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
181
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
182
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
183
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
184
+ default: {
185
+ const char *hexdig = "0123456789abcdef";
186
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
187
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
188
+ scratch[5] = hexdig[ch & 0xf];
189
+ fbuffer_append(search->buffer, scratch, 6);
190
+ break;
191
+ }
192
+ }
193
+ search->ptr++;
194
+ search->cursor = search->ptr;
322
195
  }
323
196
 
324
- /*
325
- * Document-module: JSON::Ext::Generator
197
+ /* Converts in_string to a JSON string (without the wrapping '"'
198
+ * characters) in FBuffer out_buffer.
326
199
  *
327
- * This is the JSON generator implemented as a C extension. It can be
328
- * configured to be used by setting
200
+ * Character are JSON-escaped according to:
329
201
  *
330
- * JSON.generator = JSON::Ext::Generator
202
+ * - Always: ASCII control characters (0x00-0x1F), dquote, and
203
+ * backslash.
331
204
  *
332
- * with the method generator= in JSON.
205
+ * - If out_ascii_only: non-ASCII characters (>0x7F)
333
206
  *
207
+ * - If script_safe: forwardslash (/), line separator (U+2028), and
208
+ * paragraph separator (U+2029)
209
+ *
210
+ * Everything else (should be UTF-8) is just passed through and
211
+ * appended to the result.
334
212
  */
335
213
 
336
- /* Explanation of the following: that's the only way to not pollute
337
- * standard library's docs with GeneratorMethods::<ClassName> which
338
- * are uninformative and take a large place in a list of classes
339
- */
340
-
341
- /*
342
- * Document-module: JSON::Ext::Generator::GeneratorMethods
343
- * :nodoc:
344
- */
345
214
 
346
- /*
347
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Array
348
- * :nodoc:
349
- */
215
+ #if defined(HAVE_SIMD_NEON)
216
+ static inline unsigned char search_escape_basic_neon(search_state *search);
217
+ #elif defined(HAVE_SIMD_SSE2)
218
+ static inline unsigned char search_escape_basic_sse2(search_state *search);
219
+ #endif
350
220
 
351
- /*
352
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
353
- * :nodoc:
354
- */
221
+ static inline unsigned char search_escape_basic(search_state *search);
355
222
 
356
- /*
357
- * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
358
- * :nodoc:
359
- */
223
+ static inline void convert_UTF8_to_JSON(search_state *search)
224
+ {
225
+ #ifdef HAVE_SIMD
226
+ #if defined(HAVE_SIMD_NEON)
227
+ while (search_escape_basic_neon(search)) {
228
+ escape_UTF8_char_basic(search);
229
+ }
230
+ #elif defined(HAVE_SIMD_SSE2)
231
+ if (simd_impl == SIMD_SSE2) {
232
+ while (search_escape_basic_sse2(search)) {
233
+ escape_UTF8_char_basic(search);
234
+ }
235
+ return;
236
+ }
237
+ while (search_escape_basic(search)) {
238
+ escape_UTF8_char_basic(search);
239
+ }
240
+ #endif
241
+ #else
242
+ while (search_escape_basic(search)) {
243
+ escape_UTF8_char_basic(search);
244
+ }
245
+ #endif /* HAVE_SIMD */
246
+ }
247
+
248
+ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
249
+ {
250
+ const unsigned char ch = (unsigned char)*search->ptr;
251
+ switch (ch_len) {
252
+ case 1: {
253
+ switch (ch) {
254
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
255
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
256
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
257
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
258
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
259
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
260
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
261
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
262
+ default: {
263
+ const char *hexdig = "0123456789abcdef";
264
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
265
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
266
+ scratch[5] = hexdig[ch & 0xf];
267
+ fbuffer_append(search->buffer, scratch, 6);
268
+ break;
269
+ }
270
+ }
271
+ break;
272
+ }
273
+ case 3: {
274
+ if (search->ptr[2] & 1) {
275
+ fbuffer_append(search->buffer, "\\u2029", 6);
276
+ } else {
277
+ fbuffer_append(search->buffer, "\\u2028", 6);
278
+ }
279
+ break;
280
+ }
281
+ }
282
+ search->cursor = (search->ptr += ch_len);
283
+ }
360
284
 
361
- /*
362
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
363
- * :nodoc:
364
- */
285
+ #ifdef HAVE_SIMD
365
286
 
366
- /*
367
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Float
368
- * :nodoc:
369
- */
287
+ ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
288
+ {
289
+ RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
370
290
 
371
- /*
372
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
373
- * :nodoc:
374
- */
291
+ // Flush the buffer so everything up until the last 'len' characters are unflushed.
292
+ search_flush(search);
375
293
 
376
- /*
377
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
378
- * :nodoc:
379
- */
294
+ FBuffer *buf = search->buffer;
295
+ fbuffer_inc_capa(buf, vec_len);
380
296
 
381
- /*
382
- * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
383
- * :nodoc:
384
- */
297
+ char *s = (buf->ptr + buf->len);
385
298
 
386
- /*
387
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Object
388
- * :nodoc:
389
- */
299
+ // Pad the buffer with dummy characters that won't need escaping.
300
+ // This seem wasteful at first sight, but memset of vector length is very fast.
301
+ // This is a space as it can be directly represented as an immediate on AArch64.
302
+ memset(s, ' ', vec_len);
390
303
 
391
- /*
392
- * Document-module: JSON::Ext::Generator::GeneratorMethods::String
393
- * :nodoc:
394
- */
304
+ // Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
305
+ // to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
306
+ if (vec_len == 16) {
307
+ RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
308
+ json_fast_memcpy16(s, search->ptr, len);
309
+ } else {
310
+ MEMCPY(s, search->ptr, char, len);
311
+ }
395
312
 
396
- /*
397
- * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
398
- * :nodoc:
399
- */
313
+ return s;
314
+ }
400
315
 
401
- /*
402
- * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
403
- * :nodoc:
404
- */
316
+ #ifdef HAVE_SIMD_NEON
405
317
 
406
- /*
407
- * call-seq: to_json(state = nil)
408
- *
409
- * Returns a JSON string containing a JSON object, that is generated from
410
- * this Hash instance.
411
- * _state_ is a JSON::State object, that can also be used to configure the
412
- * produced JSON string output further.
413
- */
414
- static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
318
+ ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
415
319
  {
416
- GENERATE_JSON(object);
417
- }
320
+ uint64_t mask = search->matches_mask;
321
+ uint32_t index = trailing_zeros64(mask) >> 2;
418
322
 
419
- /*
420
- * call-seq: to_json(state = nil)
421
- *
422
- * Returns a JSON string containing a JSON array, that is generated from
423
- * this Array instance.
424
- * _state_ is a JSON::State object, that can also be used to configure the
425
- * produced JSON string output further.
426
- */
427
- static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
428
- GENERATE_JSON(array);
323
+ // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character.
324
+ // If we want to use a similar approach for full escaping we'll need to ensure:
325
+ // search->chunk_base + index >= search->ptr
326
+ // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match
327
+ // is one byte after the previous match then:
328
+ // search->chunk_base + index == search->ptr
329
+ search->ptr = search->chunk_base + index;
330
+ mask &= mask - 1;
331
+ search->matches_mask = mask;
332
+ search_flush(search);
333
+ return 1;
429
334
  }
430
335
 
431
- #ifdef RUBY_INTEGER_UNIFICATION
432
- /*
433
- * call-seq: to_json(*)
434
- *
435
- * Returns a JSON string representation for this Integer number.
436
- */
437
- static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
336
+ static inline unsigned char search_escape_basic_neon(search_state *search)
438
337
  {
439
- GENERATE_JSON(integer);
440
- }
338
+ if (RB_UNLIKELY(search->has_matches)) {
339
+ // There are more matches if search->matches_mask > 0.
340
+ if (search->matches_mask > 0) {
341
+ return neon_next_match(search);
342
+ } else {
343
+ // neon_next_match will only advance search->ptr up to the last matching character.
344
+ // Skip over any characters in the last chunk that occur after the last match.
345
+ search->has_matches = false;
346
+ search->ptr = search->chunk_end;
347
+ }
348
+ }
441
349
 
442
- #else
443
- /*
444
- * call-seq: to_json(*)
445
- *
446
- * Returns a JSON string representation for this Integer number.
447
- */
448
- static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
449
- {
450
- GENERATE_JSON(fixnum);
350
+ /*
351
+ * The code below implements an SIMD-based algorithm to determine if N bytes at a time
352
+ * need to be escaped.
353
+ *
354
+ * Assume the ptr = "Te\sting!" (the double quotes are included in the string)
355
+ *
356
+ * The explanation will be limited to the first 8 bytes of the string for simplicity. However
357
+ * the vector insructions may work on larger vectors.
358
+ *
359
+ * First, we load three constants 'lower_bound', 'backslash' and 'dblquote" in vector registers.
360
+ *
361
+ * lower_bound: [20 20 20 20 20 20 20 20]
362
+ * backslash: [5C 5C 5C 5C 5C 5C 5C 5C]
363
+ * dblquote: [22 22 22 22 22 22 22 22]
364
+ *
365
+ * Next we load the first chunk of the ptr:
366
+ * [22 54 65 5C 73 74 69 6E] (" T e \ s t i n)
367
+ *
368
+ * First we check if any byte in chunk is less than 32 (0x20). This returns the following vector
369
+ * as no bytes are less than 32 (0x20):
370
+ * [0 0 0 0 0 0 0 0]
371
+ *
372
+ * Next, we check if any byte in chunk is equal to a backslash:
373
+ * [0 0 0 FF 0 0 0 0]
374
+ *
375
+ * Finally we check if any byte in chunk is equal to a double quote:
376
+ * [FF 0 0 0 0 0 0 0]
377
+ *
378
+ * Now we have three vectors where each byte indicates if the corresponding byte in chunk
379
+ * needs to be escaped. We combine these vectors with a series of logical OR instructions.
380
+ * This is the needs_escape vector and it is equal to:
381
+ * [FF 0 0 FF 0 0 0 0]
382
+ *
383
+ * Next we compute the bitwise AND between each byte and 0x1 and compute the horizontal sum of
384
+ * the values in the vector. This computes how many bytes need to be escaped within this chunk.
385
+ *
386
+ * Finally we compute a mask that indicates which bytes need to be escaped. If the mask is 0 then,
387
+ * no bytes need to be escaped and we can continue to the next chunk. If the mask is not 0 then we
388
+ * have at least one byte that needs to be escaped.
389
+ */
390
+
391
+ if (string_scan_simd_neon(&search->ptr, search->end, &search->matches_mask)) {
392
+ search->has_matches = true;
393
+ search->chunk_base = search->ptr;
394
+ search->chunk_end = search->ptr + sizeof(uint8x16_t);
395
+ return neon_next_match(search);
396
+ }
397
+
398
+ // There are fewer than 16 bytes left.
399
+ unsigned long remaining = (search->end - search->ptr);
400
+ if (remaining >= SIMD_MINIMUM_THRESHOLD) {
401
+ char *s = copy_remaining_bytes(search, sizeof(uint8x16_t), remaining);
402
+
403
+ uint64_t mask = compute_chunk_mask_neon(s);
404
+
405
+ if (!mask) {
406
+ // Nothing to escape, ensure search_flush doesn't do anything by setting
407
+ // search->cursor to search->ptr.
408
+ fbuffer_consumed(search->buffer, remaining);
409
+ search->ptr = search->end;
410
+ search->cursor = search->end;
411
+ return 0;
412
+ }
413
+
414
+ search->matches_mask = mask;
415
+ search->has_matches = true;
416
+ search->chunk_end = search->end;
417
+ search->chunk_base = search->ptr;
418
+ return neon_next_match(search);
419
+ }
420
+
421
+ if (search->ptr < search->end) {
422
+ return search_escape_basic(search);
423
+ }
424
+
425
+ search_flush(search);
426
+ return 0;
451
427
  }
428
+ #endif /* HAVE_SIMD_NEON */
452
429
 
453
- /*
454
- * call-seq: to_json(*)
455
- *
456
- * Returns a JSON string representation for this Integer number.
457
- */
458
- static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
430
+ #ifdef HAVE_SIMD_SSE2
431
+
432
+ ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
459
433
  {
460
- GENERATE_JSON(bignum);
434
+ int mask = search->matches_mask;
435
+ int index = trailing_zeros(mask);
436
+
437
+ // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character.
438
+ // If we want to use a similar approach for full escaping we'll need to ensure:
439
+ // search->chunk_base + index >= search->ptr
440
+ // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match
441
+ // is one byte after the previous match then:
442
+ // search->chunk_base + index == search->ptr
443
+ search->ptr = search->chunk_base + index;
444
+ mask &= mask - 1;
445
+ search->matches_mask = mask;
446
+ search_flush(search);
447
+ return 1;
461
448
  }
449
+
450
+ #if defined(__clang__) || defined(__GNUC__)
451
+ #define TARGET_SSE2 __attribute__((target("sse2")))
452
+ #else
453
+ #define TARGET_SSE2
462
454
  #endif
463
455
 
464
- /*
465
- * call-seq: to_json(*)
466
- *
467
- * Returns a JSON string representation for this Float number.
468
- */
469
- static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
456
+ ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
470
457
  {
471
- GENERATE_JSON(float);
472
- }
458
+ if (RB_UNLIKELY(search->has_matches)) {
459
+ // There are more matches if search->matches_mask > 0.
460
+ if (search->matches_mask > 0) {
461
+ return sse2_next_match(search);
462
+ } else {
463
+ // sse2_next_match will only advance search->ptr up to the last matching character.
464
+ // Skip over any characters in the last chunk that occur after the last match.
465
+ search->has_matches = false;
466
+ if (RB_UNLIKELY(search->chunk_base + sizeof(__m128i) >= search->end)) {
467
+ search->ptr = search->end;
468
+ } else {
469
+ search->ptr = search->chunk_base + sizeof(__m128i);
470
+ }
471
+ }
472
+ }
473
473
 
474
- /*
475
- * call-seq: String.included(modul)
476
- *
477
- * Extends _modul_ with the String::Extend module.
478
- */
479
- static VALUE mString_included_s(VALUE self, VALUE modul) {
480
- VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
481
- return result;
482
- }
474
+ if (string_scan_simd_sse2(&search->ptr, search->end, &search->matches_mask)) {
475
+ search->has_matches = true;
476
+ search->chunk_base = search->ptr;
477
+ search->chunk_end = search->ptr + sizeof(__m128i);
478
+ return sse2_next_match(search);
479
+ }
483
480
 
484
- /*
485
- * call-seq: to_json(*)
486
- *
487
- * This string should be encoded with UTF-8 A call to this method
488
- * returns a JSON string encoded with UTF16 big endian characters as
489
- * \u????.
490
- */
491
- static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
492
- {
493
- GENERATE_JSON(string);
494
- }
481
+ // There are fewer than 16 bytes left.
482
+ unsigned long remaining = (search->end - search->ptr);
483
+ if (remaining >= SIMD_MINIMUM_THRESHOLD) {
484
+ char *s = copy_remaining_bytes(search, sizeof(__m128i), remaining);
495
485
 
496
- /*
497
- * call-seq: to_json_raw_object()
498
- *
499
- * This method creates a raw object hash, that can be nested into
500
- * other data structures and will be generated as a raw string. This
501
- * method should be used, if you want to convert raw strings to JSON
502
- * instead of UTF-8 strings, e. g. binary data.
503
- */
504
- static VALUE mString_to_json_raw_object(VALUE self)
505
- {
506
- VALUE ary;
507
- VALUE result = rb_hash_new();
508
- rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
509
- ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
510
- rb_hash_aset(result, rb_str_new2("raw"), ary);
511
- return result;
486
+ int needs_escape_mask = compute_chunk_mask_sse2(s);
487
+
488
+ if (needs_escape_mask == 0) {
489
+ // Nothing to escape, ensure search_flush doesn't do anything by setting
490
+ // search->cursor to search->ptr.
491
+ fbuffer_consumed(search->buffer, remaining);
492
+ search->ptr = search->end;
493
+ search->cursor = search->end;
494
+ return 0;
495
+ }
496
+
497
+ search->has_matches = true;
498
+ search->matches_mask = needs_escape_mask;
499
+ search->chunk_base = search->ptr;
500
+ return sse2_next_match(search);
501
+ }
502
+
503
+ if (search->ptr < search->end) {
504
+ return search_escape_basic(search);
505
+ }
506
+
507
+ search_flush(search);
508
+ return 0;
512
509
  }
513
510
 
514
- /*
515
- * call-seq: to_json_raw(*args)
516
- *
517
- * This method creates a JSON text from the result of a call to
518
- * to_json_raw_object of this String.
519
- */
520
- static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
511
+ #endif /* HAVE_SIMD_SSE2 */
512
+
513
+ #endif /* HAVE_SIMD */
514
+
515
+ static const unsigned char script_safe_escape_table[256] = {
516
+ // ASCII Control Characters
517
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
518
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
519
+ // ASCII Characters
520
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/'
521
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
522
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
523
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
524
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
525
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
526
+ // Continuation byte
527
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
528
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
529
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
530
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
531
+ // First byte of a 2-byte code point
532
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
533
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
534
+ // First byte of a 3-byte code point
535
+ 3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029
536
+ //First byte of a 4+ byte code point
537
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
538
+ };
539
+
540
+ static inline unsigned char search_script_safe_escape(search_state *search)
521
541
  {
522
- VALUE obj = mString_to_json_raw_object(self);
523
- Check_Type(obj, T_HASH);
524
- return mHash_to_json(argc, argv, obj);
542
+ while (search->ptr < search->end) {
543
+ unsigned char ch = (unsigned char)*search->ptr;
544
+ unsigned char ch_len = script_safe_escape_table[ch];
545
+
546
+ if (RB_UNLIKELY(ch_len)) {
547
+ if (ch_len & ESCAPE_MASK) {
548
+ if (RB_UNLIKELY(ch_len == 11)) {
549
+ const unsigned char *uptr = (const unsigned char *)search->ptr;
550
+ if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) {
551
+ search->ptr += 3;
552
+ continue;
553
+ }
554
+ }
555
+ search_flush(search);
556
+ return ch_len & CHAR_LENGTH_MASK;
557
+ } else {
558
+ search->ptr += ch_len;
559
+ }
560
+ } else {
561
+ search->ptr++;
562
+ }
563
+ }
564
+ search_flush(search);
565
+ return 0;
525
566
  }
526
567
 
527
- /*
528
- * call-seq: json_create(o)
529
- *
530
- * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
531
- * key "raw"). The Ruby String can be created by this module method.
532
- */
533
- static VALUE mString_Extend_json_create(VALUE self, VALUE o)
568
+ static void convert_UTF8_to_script_safe_JSON(search_state *search)
534
569
  {
535
- VALUE ary;
536
- Check_Type(o, T_HASH);
537
- ary = rb_hash_aref(o, rb_str_new2("raw"));
538
- return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
570
+ unsigned char ch_len;
571
+ while ((ch_len = search_script_safe_escape(search))) {
572
+ escape_UTF8_char(search, ch_len);
573
+ }
539
574
  }
540
575
 
541
- /*
542
- * call-seq: to_json(*)
543
- *
544
- * Returns a JSON string for true: 'true'.
545
- */
546
- static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
576
+ static const unsigned char ascii_only_escape_table[256] = {
577
+ // ASCII Control Characters
578
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
579
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
580
+ // ASCII Characters
581
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
582
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
583
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
584
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
585
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
586
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
587
+ // Continuation byte
588
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
589
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
590
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
591
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
592
+ // First byte of a 2-byte code point
593
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
594
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
595
+ // First byte of a 3-byte code point
596
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
597
+ //First byte of a 4+ byte code point
598
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
599
+ };
600
+
601
+ static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256])
547
602
  {
548
- GENERATE_JSON(true);
603
+ while (search->ptr < search->end) {
604
+ unsigned char ch = (unsigned char)*search->ptr;
605
+ unsigned char ch_len = escape_table[ch];
606
+
607
+ if (RB_UNLIKELY(ch_len)) {
608
+ search_flush(search);
609
+ return ch_len & CHAR_LENGTH_MASK;
610
+ } else {
611
+ search->ptr++;
612
+ }
613
+ }
614
+ search_flush(search);
615
+ return 0;
549
616
  }
550
617
 
551
- /*
552
- * call-seq: to_json(*)
553
- *
554
- * Returns a JSON string for false: 'false'.
555
- */
556
- static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
557
- {
558
- GENERATE_JSON(false);
618
+ static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len)
619
+ {
620
+ const unsigned char ch = (unsigned char)*search->ptr;
621
+ switch (ch_len) {
622
+ case 1: {
623
+ switch (ch) {
624
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
625
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
626
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
627
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
628
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
629
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
630
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
631
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
632
+ default: {
633
+ const char *hexdig = "0123456789abcdef";
634
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
635
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
636
+ scratch[5] = hexdig[ch & 0xf];
637
+ fbuffer_append(search->buffer, scratch, 6);
638
+ break;
639
+ }
640
+ }
641
+ break;
642
+ }
643
+ default: {
644
+ const char *hexdig = "0123456789abcdef";
645
+ char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
646
+
647
+ uint32_t wchar = 0;
648
+
649
+ switch (ch_len) {
650
+ case 2:
651
+ wchar = ch & 0x1F;
652
+ break;
653
+ case 3:
654
+ wchar = ch & 0x0F;
655
+ break;
656
+ case 4:
657
+ wchar = ch & 0x07;
658
+ break;
659
+ }
660
+
661
+ for (short i = 1; i < ch_len; i++) {
662
+ wchar = (wchar << 6) | (search->ptr[i] & 0x3F);
663
+ }
664
+
665
+ if (wchar <= 0xFFFF) {
666
+ scratch[2] = hexdig[wchar >> 12];
667
+ scratch[3] = hexdig[(wchar >> 8) & 0xf];
668
+ scratch[4] = hexdig[(wchar >> 4) & 0xf];
669
+ scratch[5] = hexdig[wchar & 0xf];
670
+ fbuffer_append(search->buffer, scratch, 6);
671
+ } else {
672
+ uint16_t hi, lo;
673
+ wchar -= 0x10000;
674
+ hi = 0xD800 + (uint16_t)(wchar >> 10);
675
+ lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
676
+
677
+ scratch[2] = hexdig[hi >> 12];
678
+ scratch[3] = hexdig[(hi >> 8) & 0xf];
679
+ scratch[4] = hexdig[(hi >> 4) & 0xf];
680
+ scratch[5] = hexdig[hi & 0xf];
681
+
682
+ scratch[8] = hexdig[lo >> 12];
683
+ scratch[9] = hexdig[(lo >> 8) & 0xf];
684
+ scratch[10] = hexdig[(lo >> 4) & 0xf];
685
+ scratch[11] = hexdig[lo & 0xf];
686
+
687
+ fbuffer_append(search->buffer, scratch, 12);
688
+ }
689
+
690
+ break;
691
+ }
692
+ }
693
+ search->cursor = (search->ptr += ch_len);
559
694
  }
560
695
 
561
- /*
562
- * call-seq: to_json(*)
563
- *
564
- * Returns a JSON string for nil: 'null'.
565
- */
566
- static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
696
+ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256])
567
697
  {
568
- GENERATE_JSON(null);
698
+ unsigned char ch_len;
699
+ while ((ch_len = search_ascii_only_escape(search, escape_table))) {
700
+ full_escape_UTF8_char(search, ch_len);
701
+ }
569
702
  }
570
703
 
571
- /*
572
- * call-seq: to_json(*)
573
- *
574
- * Converts this object to a string (calling #to_s), converts
575
- * it to a JSON string, and returns the result. This is a fallback, if no
576
- * special method #to_json was defined for some object.
577
- */
578
- static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
704
+ static void State_mark(void *ptr)
579
705
  {
580
- VALUE state;
581
- VALUE string = rb_funcall(self, i_to_s, 0);
582
- rb_scan_args(argc, argv, "01", &state);
583
- Check_Type(string, T_STRING);
584
- state = cState_from_state_s(cState, state);
585
- return cState_partial_generate(state, string);
706
+ JSON_Generator_State *state = ptr;
707
+ rb_gc_mark_movable(state->indent);
708
+ rb_gc_mark_movable(state->space);
709
+ rb_gc_mark_movable(state->space_before);
710
+ rb_gc_mark_movable(state->object_nl);
711
+ rb_gc_mark_movable(state->array_nl);
712
+ rb_gc_mark_movable(state->as_json);
713
+ rb_gc_mark_movable(state->sort_keys);
586
714
  }
587
715
 
588
- static void State_free(void *ptr)
716
+ static void State_compact(void *ptr)
589
717
  {
590
718
  JSON_Generator_State *state = ptr;
591
- if (state->indent) ruby_xfree(state->indent);
592
- if (state->space) ruby_xfree(state->space);
593
- if (state->space_before) ruby_xfree(state->space_before);
594
- if (state->object_nl) ruby_xfree(state->object_nl);
595
- if (state->array_nl) ruby_xfree(state->array_nl);
596
- if (state->array_delim) fbuffer_free(state->array_delim);
597
- if (state->object_delim) fbuffer_free(state->object_delim);
598
- if (state->object_delim2) fbuffer_free(state->object_delim2);
599
- ruby_xfree(state);
719
+ state->indent = rb_gc_location(state->indent);
720
+ state->space = rb_gc_location(state->space);
721
+ state->space_before = rb_gc_location(state->space_before);
722
+ state->object_nl = rb_gc_location(state->object_nl);
723
+ state->array_nl = rb_gc_location(state->array_nl);
724
+ state->as_json = rb_gc_location(state->as_json);
725
+ state->sort_keys = rb_gc_location(state->sort_keys);
600
726
  }
601
727
 
602
728
  static size_t State_memsize(const void *ptr)
603
729
  {
604
- const JSON_Generator_State *state = ptr;
605
- size_t size = sizeof(*state);
606
- if (state->indent) size += state->indent_len + 1;
607
- if (state->space) size += state->space_len + 1;
608
- if (state->space_before) size += state->space_before_len + 1;
609
- if (state->object_nl) size += state->object_nl_len + 1;
610
- if (state->array_nl) size += state->array_nl_len + 1;
611
- if (state->array_delim) size += FBUFFER_CAPA(state->array_delim);
612
- if (state->object_delim) size += FBUFFER_CAPA(state->object_delim);
613
- if (state->object_delim2) size += FBUFFER_CAPA(state->object_delim2);
614
- return size;
615
- }
616
-
617
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
618
- # undef RUBY_TYPED_FROZEN_SHAREABLE
619
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
730
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
731
+ return 0;
732
+ #else
733
+ return sizeof(JSON_Generator_State);
620
734
  #endif
735
+ }
621
736
 
622
- #ifdef NEW_TYPEDDATA_WRAPPER
623
737
  static const rb_data_type_t JSON_Generator_State_type = {
624
- "JSON/Generator/State",
625
- {NULL, State_free, State_memsize,},
626
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
627
- 0, 0,
628
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
629
- #endif
738
+ .wrap_struct_name = "JSON/Generator/State",
739
+ .function = {
740
+ .dmark = State_mark,
741
+ .dfree = RUBY_DEFAULT_FREE,
742
+ .dsize = State_memsize,
743
+ .dcompact = State_compact,
744
+ },
745
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
630
746
  };
631
- #endif
747
+
748
+ static void state_init(JSON_Generator_State *state)
749
+ {
750
+ state->max_nesting = 100;
751
+ state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
752
+ }
632
753
 
633
754
  static VALUE cState_s_allocate(VALUE klass)
634
755
  {
635
756
  JSON_Generator_State *state;
636
- return TypedData_Make_Struct(klass, JSON_Generator_State,
637
- &JSON_Generator_State_type, state);
757
+ VALUE obj = TypedData_Make_Struct(klass, JSON_Generator_State, &JSON_Generator_State_type, state);
758
+ state_init(state);
759
+ return obj;
638
760
  }
639
761
 
640
- /*
641
- * call-seq: configure(opts)
642
- *
643
- * Configure this State instance with the Hash _opts_, and return
644
- * itself.
645
- */
646
- static VALUE cState_configure(VALUE self, VALUE opts)
762
+ static void vstate_spill(struct generate_json_data *data)
647
763
  {
648
- VALUE tmp;
649
- GET_STATE(self);
650
- tmp = rb_check_convert_type(opts, T_HASH, "Hash", "to_hash");
651
- if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
652
- opts = tmp;
653
- tmp = rb_hash_aref(opts, ID2SYM(i_indent));
654
- if (RTEST(tmp)) {
655
- unsigned long len;
656
- Check_Type(tmp, T_STRING);
657
- len = RSTRING_LEN(tmp);
658
- state->indent = fstrndup(RSTRING_PTR(tmp), len + 1);
659
- state->indent_len = len;
660
- }
661
- tmp = rb_hash_aref(opts, ID2SYM(i_space));
662
- if (RTEST(tmp)) {
663
- unsigned long len;
664
- Check_Type(tmp, T_STRING);
665
- len = RSTRING_LEN(tmp);
666
- state->space = fstrndup(RSTRING_PTR(tmp), len + 1);
667
- state->space_len = len;
764
+ VALUE vstate = cState_s_allocate(cState);
765
+ GET_STATE(vstate);
766
+ MEMCPY(state, data->state, JSON_Generator_State, 1);
767
+ data->state = state;
768
+ data->vstate = vstate;
769
+ RB_OBJ_WRITTEN(vstate, Qundef, state->indent);
770
+ RB_OBJ_WRITTEN(vstate, Qundef, state->space);
771
+ RB_OBJ_WRITTEN(vstate, Qundef, state->space_before);
772
+ RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
773
+ RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
774
+ RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
775
+ RB_OBJ_WRITTEN(vstate, Qundef, state->sort_keys);
776
+ }
777
+
778
+ static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
779
+ {
780
+ if (RB_UNLIKELY(!data->vstate)) {
781
+ vstate_spill(data);
668
782
  }
669
- tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
670
- if (RTEST(tmp)) {
671
- unsigned long len;
672
- Check_Type(tmp, T_STRING);
673
- len = RSTRING_LEN(tmp);
674
- state->space_before = fstrndup(RSTRING_PTR(tmp), len + 1);
675
- state->space_before_len = len;
783
+ GET_STATE(data->vstate);
784
+ state->depth = data->depth;
785
+ VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
786
+ // no need to restore state->depth, vstate is just a temporary State
787
+ return tmp;
788
+ }
789
+
790
+ static VALUE
791
+ json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
792
+ {
793
+ VALUE proc_args[2] = {object, is_key};
794
+ return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
795
+ }
796
+
797
+ static VALUE
798
+ convert_string_subclass(VALUE key)
799
+ {
800
+ VALUE key_to_s = rb_funcall(key, i_to_s, 0);
801
+
802
+ if (RB_UNLIKELY(!RB_TYPE_P(key_to_s, T_STRING))) {
803
+ VALUE cname = rb_obj_class(key);
804
+ rb_raise(rb_eTypeError,
805
+ "can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")",
806
+ cname, "String", cname, "to_s", rb_obj_class(key_to_s));
676
807
  }
677
- tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
678
- if (RTEST(tmp)) {
679
- unsigned long len;
680
- Check_Type(tmp, T_STRING);
681
- len = RSTRING_LEN(tmp);
682
- state->array_nl = fstrndup(RSTRING_PTR(tmp), len + 1);
683
- state->array_nl_len = len;
808
+
809
+ return key_to_s;
810
+ }
811
+
812
+ static bool enc_utf8_compatible_p(int enc_idx)
813
+ {
814
+ if (enc_idx == usascii_encindex) return true;
815
+ if (enc_idx == utf8_encindex) return true;
816
+ return false;
817
+ }
818
+
819
+ static VALUE encode_json_string_try(VALUE str)
820
+ {
821
+ return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
822
+ }
823
+
824
+ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
825
+ {
826
+ raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
827
+ return Qundef;
828
+ }
829
+
830
+ static inline int json_str_coderange(VALUE str) {
831
+ int coderange = RB_ENC_CODERANGE(str);
832
+ if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
833
+ coderange = rb_enc_str_coderange(str);
684
834
  }
685
- tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
686
- if (RTEST(tmp)) {
687
- unsigned long len;
688
- Check_Type(tmp, T_STRING);
689
- len = RSTRING_LEN(tmp);
690
- state->object_nl = fstrndup(RSTRING_PTR(tmp), len + 1);
691
- state->object_nl_len = len;
835
+ return coderange;
836
+ }
837
+
838
+ static inline bool valid_json_string_p(VALUE str)
839
+ {
840
+ int coderange = json_str_coderange(str);
841
+
842
+ if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
843
+ return true;
692
844
  }
693
- tmp = ID2SYM(i_max_nesting);
694
- state->max_nesting = 100;
695
- if (option_given_p(opts, tmp)) {
696
- VALUE max_nesting = rb_hash_aref(opts, tmp);
697
- if (RTEST(max_nesting)) {
698
- Check_Type(max_nesting, T_FIXNUM);
699
- state->max_nesting = FIX2LONG(max_nesting);
700
- } else {
701
- state->max_nesting = 0;
702
- }
845
+
846
+ if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
847
+ return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
703
848
  }
704
- tmp = ID2SYM(i_depth);
705
- state->depth = 0;
706
- if (option_given_p(opts, tmp)) {
707
- VALUE depth = rb_hash_aref(opts, tmp);
708
- if (RTEST(depth)) {
709
- Check_Type(depth, T_FIXNUM);
710
- state->depth = FIX2LONG(depth);
711
- } else {
712
- state->depth = 0;
849
+
850
+ return false;
851
+ }
852
+
853
+ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
854
+ {
855
+ if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
856
+ VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
857
+ if (coerced_str != str) {
858
+ if (RB_TYPE_P(coerced_str, T_STRING)) {
859
+ if (!valid_json_string_p(coerced_str)) {
860
+ raise_generator_error(str, "source sequence is illegal/malformed utf-8");
861
+ }
862
+ } else {
863
+ // as_json could return another type than T_STRING
864
+ if (is_key) {
865
+ raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
866
+ }
867
+ }
868
+
869
+ return coerced_str;
713
870
  }
714
871
  }
715
- tmp = ID2SYM(i_buffer_initial_length);
716
- if (option_given_p(opts, tmp)) {
717
- VALUE buffer_initial_length = rb_hash_aref(opts, tmp);
718
- if (RTEST(buffer_initial_length)) {
719
- long initial_length;
720
- Check_Type(buffer_initial_length, T_FIXNUM);
721
- initial_length = FIX2LONG(buffer_initial_length);
722
- if (initial_length > 0) state->buffer_initial_length = initial_length;
872
+
873
+ if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
874
+ VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
875
+ switch (rb_enc_str_coderange(utf8_string)) {
876
+ case ENC_CODERANGE_7BIT:
877
+ return utf8_string;
878
+ case ENC_CODERANGE_VALID:
879
+ // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
880
+ // TODO: Raise in 3.0.0
881
+ rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
882
+ return utf8_string;
883
+ break;
723
884
  }
724
885
  }
725
- tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
726
- state->allow_nan = RTEST(tmp);
727
- tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
728
- state->ascii_only = RTEST(tmp);
729
- tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
730
- state->escape_slash = RTEST(tmp);
731
- return self;
886
+
887
+ return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
732
888
  }
733
889
 
734
- static void set_state_ivars(VALUE hash, VALUE state)
890
+ ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
735
891
  {
736
- VALUE ivars = rb_obj_instance_variables(state);
737
- int i = 0;
738
- for (i = 0; i < RARRAY_LEN(ivars); i++) {
739
- VALUE key = rb_funcall(rb_ary_entry(ivars, i), i_to_s, 0);
740
- long key_len = RSTRING_LEN(key);
741
- VALUE value = rb_iv_get(state, StringValueCStr(key));
742
- rb_hash_aset(hash, rb_str_intern(rb_str_substr(key, 1, key_len - 1)), value);
892
+ if (RB_LIKELY(valid_json_string_p(str))) {
893
+ return str;
894
+ }
895
+ else {
896
+ return convert_invalid_encoding(data, str, as_json_called, is_key);
743
897
  }
744
898
  }
745
899
 
746
- /*
747
- * call-seq: to_h
748
- *
749
- * Returns the configuration instance variables as a hash, that can be
750
- * passed to the configure method.
751
- */
752
- static VALUE cState_to_h(VALUE self)
900
+ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
753
901
  {
754
- VALUE result = rb_hash_new();
755
- GET_STATE(self);
756
- set_state_ivars(result, self);
757
- rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len));
758
- rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len));
759
- rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len));
760
- rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new(state->object_nl, state->object_nl_len));
761
- rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len));
762
- rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
763
- rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
764
- rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
765
- rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse);
766
- rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
767
- rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
768
- return result;
769
- }
902
+ fbuffer_append_char(buffer, '"');
770
903
 
771
- /*
772
- * call-seq: [](name)
773
- *
774
- * Returns the value returned by method +name+.
775
- */
776
- static VALUE cState_aref(VALUE self, VALUE name)
777
- {
778
- name = rb_funcall(name, i_to_s, 0);
779
- if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) {
780
- return rb_funcall(self, i_send, 1, name);
781
- } else {
782
- return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
904
+ long len;
905
+ search_state search;
906
+ search.buffer = buffer;
907
+ RSTRING_GETMEM(obj, search.ptr, len);
908
+ search.cursor = search.ptr;
909
+ search.end = search.ptr + len;
910
+
911
+ #ifdef HAVE_SIMD
912
+ search.matches_mask = 0;
913
+ search.has_matches = false;
914
+ search.chunk_base = NULL;
915
+ search.chunk_end = NULL;
916
+ #endif /* HAVE_SIMD */
917
+
918
+ switch (json_str_coderange(obj)) {
919
+ case ENC_CODERANGE_7BIT:
920
+ case ENC_CODERANGE_VALID:
921
+ if (RB_UNLIKELY(data->state->ascii_only)) {
922
+ convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
923
+ } else if (RB_UNLIKELY(data->state->script_safe)) {
924
+ convert_UTF8_to_script_safe_JSON(&search);
925
+ } else {
926
+ convert_UTF8_to_JSON(&search);
927
+ }
928
+ break;
929
+ default:
930
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
931
+ break;
783
932
  }
933
+ fbuffer_append_char(buffer, '"');
784
934
  }
785
935
 
786
- /*
787
- * call-seq: []=(name, value)
788
- *
789
- * Sets the attribute name to value.
790
- */
791
- static VALUE cState_aset(VALUE self, VALUE name, VALUE value)
936
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
792
937
  {
793
- VALUE name_writer;
794
-
795
- name = rb_funcall(name, i_to_s, 0);
796
- name_writer = rb_str_cat2(rb_str_dup(name), "=");
797
- if (RTEST(rb_funcall(self, i_respond_to_p, 1, name_writer))) {
798
- return rb_funcall(self, i_send, 2, name_writer, value);
799
- } else {
800
- rb_ivar_set(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)), value);
801
- }
802
- return Qnil;
938
+ obj = ensure_valid_encoding(data, obj, false, false);
939
+ raw_generate_json_string(buffer, data, obj);
803
940
  }
804
941
 
805
942
  struct hash_foreach_arg {
806
- FBuffer *buffer;
807
- JSON_Generator_State *state;
808
- VALUE Vstate;
809
- int iter;
943
+ VALUE hash;
944
+ struct generate_json_data *data;
945
+ int first_key_type;
946
+ bool first;
947
+ bool mixed_keys_encountered;
810
948
  };
811
949
 
950
+ NOINLINE(static) void
951
+ json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
952
+ {
953
+ if (arg->mixed_keys_encountered) {
954
+ return;
955
+ }
956
+ arg->mixed_keys_encountered = true;
957
+
958
+ JSON_Generator_State *state = arg->data->state;
959
+ if (state->on_duplicate_key != JSON_IGNORE) {
960
+ VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
961
+ rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
962
+ }
963
+ }
964
+
812
965
  static int
813
966
  json_object_i(VALUE key, VALUE val, VALUE _arg)
814
967
  {
815
968
  struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg;
816
- FBuffer *buffer = arg->buffer;
817
- JSON_Generator_State *state = arg->state;
818
- VALUE Vstate = arg->Vstate;
819
-
820
- char *object_nl = state->object_nl;
821
- long object_nl_len = state->object_nl_len;
822
- char *indent = state->indent;
823
- long indent_len = state->indent_len;
824
- char *delim = FBUFFER_PTR(state->object_delim);
825
- long delim_len = FBUFFER_LEN(state->object_delim);
826
- char *delim2 = FBUFFER_PTR(state->object_delim2);
827
- long delim2_len = FBUFFER_LEN(state->object_delim2);
828
- long depth = state->depth;
829
- int j;
830
- VALUE klass, key_to_s;
831
-
832
- if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len);
833
- if (object_nl) {
834
- fbuffer_append(buffer, object_nl, object_nl_len);
969
+ struct generate_json_data *data = arg->data;
970
+
971
+ FBuffer *buffer = data->buffer;
972
+ JSON_Generator_State *state = data->state;
973
+
974
+ long depth = data->depth;
975
+ int key_type = rb_type(key);
976
+
977
+ if (arg->first) {
978
+ arg->first = false;
979
+ arg->first_key_type = key_type;
835
980
  }
836
- if (indent) {
837
- for (j = 0; j < depth; j++) {
838
- fbuffer_append(buffer, indent, indent_len);
839
- }
981
+ else {
982
+ fbuffer_append_char(buffer, ',');
983
+ }
984
+
985
+ if (RB_UNLIKELY(data->state->object_nl)) {
986
+ fbuffer_append_str(buffer, data->state->object_nl);
987
+ }
988
+ if (RB_UNLIKELY(data->state->indent)) {
989
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
990
+ }
991
+
992
+ VALUE key_to_s;
993
+ bool as_json_called = false;
994
+
995
+ start:
996
+ switch (key_type) {
997
+ case T_STRING:
998
+ if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
999
+ json_inspect_hash_with_mixed_keys(arg);
1000
+ }
1001
+
1002
+ if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
1003
+ key_to_s = key;
1004
+ } else {
1005
+ key_to_s = convert_string_subclass(key);
1006
+ }
1007
+ break;
1008
+ case T_SYMBOL:
1009
+ if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
1010
+ json_inspect_hash_with_mixed_keys(arg);
1011
+ }
1012
+
1013
+ key_to_s = rb_sym2str(key);
1014
+ break;
1015
+ default:
1016
+ if (data->state->strict) {
1017
+ if (RTEST(data->state->as_json) && !as_json_called) {
1018
+ key = json_call_as_json(data->state, key, Qtrue);
1019
+ key_type = rb_type(key);
1020
+ as_json_called = true;
1021
+ goto start;
1022
+ } else {
1023
+ raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
1024
+ }
1025
+ }
1026
+ key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
1027
+ break;
840
1028
  }
841
1029
 
842
- klass = CLASS_OF(key);
843
- if (klass == rb_cString) {
844
- key_to_s = key;
845
- } else if (klass == rb_cSymbol) {
846
- key_to_s = rb_id2str(SYM2ID(key));
1030
+ key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
1031
+
1032
+ if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
1033
+ raw_generate_json_string(buffer, data, key_to_s);
847
1034
  } else {
848
- key_to_s = rb_funcall(key, i_to_s, 0);
1035
+ generate_json(buffer, data, key_to_s);
849
1036
  }
850
- Check_Type(key_to_s, T_STRING);
851
- generate_json(buffer, Vstate, state, key_to_s);
852
- fbuffer_append(buffer, delim2, delim2_len);
853
- generate_json(buffer, Vstate, state, val);
1037
+ if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before);
1038
+ fbuffer_append_char(buffer, ':');
1039
+ if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
1040
+ generate_json(buffer, data, val);
854
1041
 
855
- arg->iter++;
856
1042
  return ST_CONTINUE;
857
1043
  }
858
1044
 
859
- static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1045
+ static inline long increase_depth(struct generate_json_data *data)
1046
+ {
1047
+ JSON_Generator_State *state = data->state;
1048
+ long depth = ++data->depth;
1049
+ if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
1050
+ rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
1051
+ }
1052
+ return depth;
1053
+ }
1054
+
1055
+ static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
860
1056
  {
861
- char *object_nl = state->object_nl;
862
- long object_nl_len = state->object_nl_len;
863
- char *indent = state->indent;
864
- long indent_len = state->indent_len;
865
- long max_nesting = state->max_nesting;
866
- long depth = ++state->depth;
867
- int j;
868
- struct hash_foreach_arg arg;
1057
+ if (RB_UNLIKELY(data->state->sort_keys)) {
1058
+ obj = rb_proc_call_with_block(data->state->sort_keys, 1, &obj, Qnil);
1059
+ Check_Type(obj, T_HASH);
1060
+ }
869
1061
 
870
- if (max_nesting != 0 && depth > max_nesting) {
871
- fbuffer_free(buffer);
872
- rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
1062
+ long depth = increase_depth(data);
1063
+
1064
+ if (RHASH_SIZE(obj) == 0) {
1065
+ fbuffer_append(buffer, "{}", 2);
1066
+ --data->depth;
1067
+ return;
873
1068
  }
1069
+
874
1070
  fbuffer_append_char(buffer, '{');
875
1071
 
876
- arg.buffer = buffer;
877
- arg.state = state;
878
- arg.Vstate = Vstate;
879
- arg.iter = 0;
1072
+ struct hash_foreach_arg arg = {
1073
+ .hash = obj,
1074
+ .data = data,
1075
+ .first = true,
1076
+ };
880
1077
  rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
881
1078
 
882
- depth = --state->depth;
883
- if (object_nl) {
884
- fbuffer_append(buffer, object_nl, object_nl_len);
885
- if (indent) {
886
- for (j = 0; j < depth; j++) {
887
- fbuffer_append(buffer, indent, indent_len);
888
- }
1079
+ depth = --data->depth;
1080
+ if (RB_UNLIKELY(data->state->object_nl)) {
1081
+ fbuffer_append_str(buffer, data->state->object_nl);
1082
+ if (RB_UNLIKELY(data->state->indent)) {
1083
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
889
1084
  }
890
1085
  }
891
1086
  fbuffer_append_char(buffer, '}');
892
1087
  }
893
1088
 
894
- static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
895
- {
896
- char *array_nl = state->array_nl;
897
- long array_nl_len = state->array_nl_len;
898
- char *indent = state->indent;
899
- long indent_len = state->indent_len;
900
- long max_nesting = state->max_nesting;
901
- char *delim = FBUFFER_PTR(state->array_delim);
902
- long delim_len = FBUFFER_LEN(state->array_delim);
903
- long depth = ++state->depth;
904
- int i, j;
905
- if (max_nesting != 0 && depth > max_nesting) {
906
- fbuffer_free(buffer);
907
- rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
1089
+ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1090
+ {
1091
+ long depth = increase_depth(data);
1092
+
1093
+ if (RARRAY_LEN(obj) == 0) {
1094
+ fbuffer_append(buffer, "[]", 2);
1095
+ --data->depth;
1096
+ return;
908
1097
  }
1098
+
909
1099
  fbuffer_append_char(buffer, '[');
910
- if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len);
911
- for(i = 0; i < RARRAY_LEN(obj); i++) {
912
- if (i > 0) fbuffer_append(buffer, delim, delim_len);
913
- if (indent) {
914
- for (j = 0; j < depth; j++) {
915
- fbuffer_append(buffer, indent, indent_len);
916
- }
1100
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
1101
+ for (int i = 0; i < RARRAY_LEN(obj); i++) {
1102
+ if (i > 0) {
1103
+ fbuffer_append_char(buffer, ',');
1104
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
1105
+ }
1106
+ if (RB_UNLIKELY(data->state->indent)) {
1107
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
917
1108
  }
918
- generate_json(buffer, Vstate, state, rb_ary_entry(obj, i));
1109
+ generate_json(buffer, data, RARRAY_AREF(obj, i));
919
1110
  }
920
- state->depth = --depth;
921
- if (array_nl) {
922
- fbuffer_append(buffer, array_nl, array_nl_len);
923
- if (indent) {
924
- for (j = 0; j < depth; j++) {
925
- fbuffer_append(buffer, indent, indent_len);
926
- }
1111
+ data->depth = --depth;
1112
+ if (RB_UNLIKELY(data->state->array_nl)) {
1113
+ fbuffer_append_str(buffer, data->state->array_nl);
1114
+ if (RB_UNLIKELY(data->state->indent)) {
1115
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
927
1116
  }
928
1117
  }
929
1118
  fbuffer_append_char(buffer, ']');
930
1119
  }
931
1120
 
932
- #ifdef HAVE_RUBY_ENCODING_H
933
- static int enc_utf8_compatible_p(rb_encoding *enc)
1121
+ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
934
1122
  {
935
- if (enc == rb_usascii_encoding()) return 1;
936
- if (enc == rb_utf8_encoding()) return 1;
937
- return 0;
1123
+ VALUE tmp;
1124
+ if (rb_respond_to(obj, i_to_json)) {
1125
+ tmp = json_call_to_json(data, obj);
1126
+ Check_Type(tmp, T_STRING);
1127
+ fbuffer_append_str(buffer, tmp);
1128
+ } else {
1129
+ tmp = rb_funcall(obj, i_to_s, 0);
1130
+ Check_Type(tmp, T_STRING);
1131
+ generate_json_string(buffer, data, tmp);
1132
+ }
938
1133
  }
939
- #endif
940
1134
 
941
- static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1135
+ static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
942
1136
  {
943
- fbuffer_append_char(buffer, '"');
944
- #ifdef HAVE_RUBY_ENCODING_H
945
- if (!enc_utf8_compatible_p(rb_enc_get(obj))) {
946
- obj = rb_str_export_to_enc(obj, rb_utf8_encoding());
947
- }
948
- #endif
949
- if (state->ascii_only) {
950
- convert_UTF8_to_JSON_ASCII(buffer, obj, state->escape_slash);
1137
+ if (data->state->strict) {
1138
+ generate_json_string(buffer, data, rb_sym2str(obj));
951
1139
  } else {
952
- convert_UTF8_to_JSON(buffer, obj, state->escape_slash);
1140
+ generate_json_fallback(buffer, data, obj);
953
1141
  }
954
- fbuffer_append_char(buffer, '"');
955
1142
  }
956
1143
 
957
- static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1144
+ static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
958
1145
  {
959
1146
  fbuffer_append(buffer, "null", 4);
960
1147
  }
961
1148
 
962
- static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1149
+ static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
963
1150
  {
964
1151
  fbuffer_append(buffer, "false", 5);
965
1152
  }
966
1153
 
967
- static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1154
+ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
968
1155
  {
969
1156
  fbuffer_append(buffer, "true", 4);
970
1157
  }
971
1158
 
972
- static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1159
+ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
973
1160
  {
974
1161
  fbuffer_append_long(buffer, FIX2LONG(obj));
975
1162
  }
976
1163
 
977
- static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1164
+ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
978
1165
  {
979
1166
  VALUE tmp = rb_funcall(obj, i_to_s, 0);
980
- fbuffer_append_str(buffer, tmp);
1167
+ fbuffer_append_str(buffer, StringValue(tmp));
981
1168
  }
982
1169
 
983
- #ifdef RUBY_INTEGER_UNIFICATION
984
- static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
985
- {
986
- if (FIXNUM_P(obj))
987
- generate_json_fixnum(buffer, Vstate, state, obj);
988
- else
989
- generate_json_bignum(buffer, Vstate, state, obj);
990
- }
991
- #endif
992
- static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1170
+ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
993
1171
  {
994
1172
  double value = RFLOAT_VALUE(obj);
995
- char allow_nan = state->allow_nan;
996
- VALUE tmp = rb_funcall(obj, i_to_s, 0);
997
- if (!allow_nan) {
998
- if (isinf(value)) {
999
- fbuffer_free(buffer);
1000
- rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
1001
- } else if (isnan(value)) {
1002
- fbuffer_free(buffer);
1003
- rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
1173
+ char allow_nan = data->state->allow_nan;
1174
+ if (isinf(value) || isnan(value)) {
1175
+ /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
1176
+ if (!allow_nan) {
1177
+ if (data->state->strict && data->state->as_json) {
1178
+ VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
1179
+ if (casted_obj != obj) {
1180
+ increase_depth(data);
1181
+ generate_json(buffer, data, casted_obj);
1182
+ data->depth--;
1183
+ return;
1184
+ }
1185
+ }
1186
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
1004
1187
  }
1188
+
1189
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
1190
+ fbuffer_append_str(buffer, tmp);
1191
+ return;
1005
1192
  }
1006
- fbuffer_append_str(buffer, tmp);
1193
+
1194
+ /* This implementation writes directly into the buffer. We reserve
1195
+ * the 32 characters that fpconv_dtoa states as its maximum.
1196
+ */
1197
+ fbuffer_inc_capa(buffer, 32);
1198
+ char* d = buffer->ptr + buffer->len;
1199
+ int len = fpconv_dtoa(value, d);
1200
+ /* fpconv_dtoa converts a float to its shortest string representation,
1201
+ * but it adds a ".0" if this is a plain integer.
1202
+ */
1203
+ fbuffer_consumed(buffer, len);
1007
1204
  }
1008
1205
 
1009
- static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
1206
+ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1010
1207
  {
1011
- VALUE tmp;
1012
- VALUE klass = CLASS_OF(obj);
1013
- if (klass == rb_cHash) {
1014
- generate_json_object(buffer, Vstate, state, obj);
1015
- } else if (klass == rb_cArray) {
1016
- generate_json_array(buffer, Vstate, state, obj);
1017
- } else if (klass == rb_cString) {
1018
- generate_json_string(buffer, Vstate, state, obj);
1019
- } else if (obj == Qnil) {
1020
- generate_json_null(buffer, Vstate, state, obj);
1208
+ VALUE fragment = RSTRUCT_GET(obj, 0);
1209
+ Check_Type(fragment, T_STRING);
1210
+ fbuffer_append_str(buffer, fragment);
1211
+ }
1212
+
1213
+ static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
1214
+ {
1215
+ bool as_json_called = false;
1216
+ start:
1217
+ if (obj == Qnil) {
1218
+ generate_json_null(buffer, data, obj);
1021
1219
  } else if (obj == Qfalse) {
1022
- generate_json_false(buffer, Vstate, state, obj);
1220
+ generate_json_false(buffer, data, obj);
1023
1221
  } else if (obj == Qtrue) {
1024
- generate_json_true(buffer, Vstate, state, obj);
1025
- } else if (FIXNUM_P(obj)) {
1026
- generate_json_fixnum(buffer, Vstate, state, obj);
1027
- } else if (RB_TYPE_P(obj, T_BIGNUM)) {
1028
- generate_json_bignum(buffer, Vstate, state, obj);
1029
- } else if (klass == rb_cFloat) {
1030
- generate_json_float(buffer, Vstate, state, obj);
1031
- } else if (rb_respond_to(obj, i_to_json)) {
1032
- tmp = rb_funcall(obj, i_to_json, 1, Vstate);
1033
- Check_Type(tmp, T_STRING);
1034
- fbuffer_append_str(buffer, tmp);
1222
+ generate_json_true(buffer, data, obj);
1223
+ } else if (RB_SPECIAL_CONST_P(obj)) {
1224
+ if (RB_FIXNUM_P(obj)) {
1225
+ generate_json_fixnum(buffer, data, obj);
1226
+ } else if (RB_FLONUM_P(obj)) {
1227
+ generate_json_float(buffer, data, obj);
1228
+ } else if (RB_STATIC_SYM_P(obj)) {
1229
+ generate_json_symbol(buffer, data, obj);
1230
+ } else {
1231
+ goto general;
1232
+ }
1035
1233
  } else {
1036
- tmp = rb_funcall(obj, i_to_s, 0);
1037
- Check_Type(tmp, T_STRING);
1038
- generate_json_string(buffer, Vstate, state, tmp);
1234
+ VALUE klass = RBASIC_CLASS(obj);
1235
+ switch (RB_BUILTIN_TYPE(obj)) {
1236
+ case T_BIGNUM:
1237
+ generate_json_bignum(buffer, data, obj);
1238
+ break;
1239
+ case T_HASH:
1240
+ if (fallback && klass != rb_cHash) goto general;
1241
+ generate_json_object(buffer, data, obj);
1242
+ break;
1243
+ case T_ARRAY:
1244
+ if (fallback && klass != rb_cArray) goto general;
1245
+ generate_json_array(buffer, data, obj);
1246
+ break;
1247
+ case T_STRING:
1248
+ if (fallback && klass != rb_cString) goto general;
1249
+
1250
+ if (RB_LIKELY(valid_json_string_p(obj))) {
1251
+ raw_generate_json_string(buffer, data, obj);
1252
+ } else if (as_json_called) {
1253
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
1254
+ } else {
1255
+ obj = ensure_valid_encoding(data, obj, false, false);
1256
+ as_json_called = true;
1257
+ goto start;
1258
+ }
1259
+ break;
1260
+ case T_SYMBOL:
1261
+ generate_json_symbol(buffer, data, obj);
1262
+ break;
1263
+ case T_FLOAT:
1264
+ if (fallback && klass != rb_cFloat) goto general;
1265
+ generate_json_float(buffer, data, obj);
1266
+ break;
1267
+ case T_STRUCT:
1268
+ if (klass != cFragment) goto general;
1269
+ generate_json_fragment(buffer, data, obj);
1270
+ break;
1271
+ default:
1272
+ general:
1273
+ if (data->state->strict) {
1274
+ if (RTEST(data->state->as_json) && !as_json_called) {
1275
+ obj = json_call_as_json(data->state, obj, Qfalse);
1276
+ as_json_called = true;
1277
+ goto start;
1278
+ } else {
1279
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
1280
+ }
1281
+ } else {
1282
+ generate_json_fallback(buffer, data, obj);
1283
+ }
1284
+ }
1039
1285
  }
1040
1286
  }
1041
1287
 
1042
- static FBuffer *cState_prepare_buffer(VALUE self)
1288
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1043
1289
  {
1044
- FBuffer *buffer;
1045
- GET_STATE(self);
1046
- buffer = fbuffer_alloc(state->buffer_initial_length);
1290
+ generate_json_general(buffer, data, obj, true);
1291
+ }
1047
1292
 
1048
- if (state->object_delim) {
1049
- fbuffer_clear(state->object_delim);
1050
- } else {
1051
- state->object_delim = fbuffer_alloc(16);
1052
- }
1053
- fbuffer_append_char(state->object_delim, ',');
1054
- if (state->object_delim2) {
1055
- fbuffer_clear(state->object_delim2);
1056
- } else {
1057
- state->object_delim2 = fbuffer_alloc(16);
1058
- }
1059
- if (state->space_before) fbuffer_append(state->object_delim2, state->space_before, state->space_before_len);
1060
- fbuffer_append_char(state->object_delim2, ':');
1061
- if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len);
1293
+ static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1294
+ {
1295
+ generate_json_general(buffer, data, obj, false);
1296
+ }
1062
1297
 
1063
- if (state->array_delim) {
1064
- fbuffer_clear(state->array_delim);
1065
- } else {
1066
- state->array_delim = fbuffer_alloc(16);
1067
- }
1068
- fbuffer_append_char(state->array_delim, ',');
1069
- if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len);
1070
- return buffer;
1298
+ static VALUE generate_json_try(VALUE d)
1299
+ {
1300
+ struct generate_json_data *data = (struct generate_json_data *)d;
1301
+
1302
+ data->func(data->buffer, data, data->obj);
1303
+
1304
+ return fbuffer_finalize(data->buffer);
1305
+ }
1306
+
1307
+ static VALUE generate_json_ensure(VALUE d)
1308
+ {
1309
+ struct generate_json_data *data = (struct generate_json_data *)d;
1310
+ fbuffer_free(data->buffer);
1311
+
1312
+ return Qundef;
1071
1313
  }
1072
1314
 
1073
- static VALUE cState_partial_generate(VALUE self, VALUE obj)
1315
+ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
1074
1316
  {
1075
- FBuffer *buffer = cState_prepare_buffer(self);
1076
1317
  GET_STATE(self);
1077
- generate_json(buffer, self, state, obj);
1078
- return fbuffer_to_s(buffer);
1318
+
1319
+ char stack_buffer[FBUFFER_STACK_SIZE];
1320
+ FBuffer buffer = { 0 };
1321
+ fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1322
+
1323
+ struct generate_json_data data = {
1324
+ .buffer = &buffer,
1325
+ .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
1326
+ .state = state,
1327
+ .depth = state->depth,
1328
+ .obj = obj,
1329
+ .func = func
1330
+ };
1331
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1079
1332
  }
1080
1333
 
1081
- /*
1082
- * call-seq: generate(obj)
1334
+ /* call-seq:
1335
+ * generate(obj) -> String
1336
+ * generate(obj, anIO) -> anIO
1083
1337
  *
1084
1338
  * Generates a valid JSON document from object +obj+ and returns the
1085
1339
  * result. If no valid JSON document can be created this method raises a
1086
1340
  * GeneratorError exception.
1087
1341
  */
1088
- static VALUE cState_generate(VALUE self, VALUE obj)
1342
+ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
1089
1343
  {
1090
- VALUE result = cState_partial_generate(self, obj);
1091
- GET_STATE(self);
1092
- (void)state;
1093
- return result;
1344
+ rb_check_arity(argc, 1, 2);
1345
+ VALUE obj = argv[0];
1346
+ VALUE io = argc > 1 ? argv[1] : Qnil;
1347
+ return cState_partial_generate(self, obj, generate_json, io);
1348
+ }
1349
+
1350
+ /* :nodoc: */
1351
+ static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
1352
+ {
1353
+ rb_check_arity(argc, 1, 2);
1354
+ VALUE obj = argv[0];
1355
+ VALUE io = argc > 1 ? argv[1] : Qnil;
1356
+ return cState_partial_generate(self, obj, generate_json_no_fallback, io);
1094
1357
  }
1095
1358
 
1096
- /*
1097
- * call-seq: new(opts = {})
1098
- *
1099
- * Instantiates a new State object, configured by _opts_.
1100
- *
1101
- * _opts_ can have the following keys:
1102
- *
1103
- * * *indent*: a string used to indent levels (default: ''),
1104
- * * *space*: a string that is put after, a : or , delimiter (default: ''),
1105
- * * *space_before*: a string that is put before a : pair delimiter (default: ''),
1106
- * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
1107
- * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
1108
- * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
1109
- * generated, otherwise an exception is thrown, if these values are
1110
- * encountered. This options defaults to false.
1111
- * * *ascii_only*: true if only ASCII characters should be generated. This
1112
- * option defaults to false.
1113
- * * *buffer_initial_length*: sets the initial length of the generator's
1114
- * internal buffer.
1115
- */
1116
1359
  static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
1117
1360
  {
1118
- VALUE opts;
1119
- GET_STATE(self);
1120
- state->max_nesting = 100;
1121
- state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
1122
- rb_scan_args(argc, argv, "01", &opts);
1123
- if (!NIL_P(opts)) cState_configure(self, opts);
1361
+ rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
1124
1362
  return self;
1125
1363
  }
1126
1364
 
@@ -1140,14 +1378,15 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
1140
1378
  if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
1141
1379
 
1142
1380
  MEMCPY(objState, origState, JSON_Generator_State, 1);
1143
- objState->indent = fstrndup(origState->indent, origState->indent_len);
1144
- objState->space = fstrndup(origState->space, origState->space_len);
1145
- objState->space_before = fstrndup(origState->space_before, origState->space_before_len);
1146
- objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len);
1147
- objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len);
1148
- if (origState->array_delim) objState->array_delim = fbuffer_dup(origState->array_delim);
1149
- if (origState->object_delim) objState->object_delim = fbuffer_dup(origState->object_delim);
1150
- if (origState->object_delim2) objState->object_delim2 = fbuffer_dup(origState->object_delim2);
1381
+
1382
+ RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
1383
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space);
1384
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
1385
+ RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
1386
+ RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
1387
+ RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
1388
+ RB_OBJ_WRITTEN(obj, Qundef, objState->sort_keys);
1389
+
1151
1390
  return obj;
1152
1391
  }
1153
1392
 
@@ -1177,7 +1416,18 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
1177
1416
  static VALUE cState_indent(VALUE self)
1178
1417
  {
1179
1418
  GET_STATE(self);
1180
- return state->indent ? rb_str_new(state->indent, state->indent_len) : rb_str_new2("");
1419
+ return state->indent ? state->indent : rb_str_freeze(rb_utf8_str_new("", 0));
1420
+ }
1421
+
1422
+ static VALUE string_config(VALUE config)
1423
+ {
1424
+ if (RTEST(config)) {
1425
+ Check_Type(config, T_STRING);
1426
+ if (RSTRING_LEN(config)) {
1427
+ return rb_str_new_frozen(config);
1428
+ }
1429
+ }
1430
+ return Qfalse;
1181
1431
  }
1182
1432
 
1183
1433
  /*
@@ -1187,21 +1437,9 @@ static VALUE cState_indent(VALUE self)
1187
1437
  */
1188
1438
  static VALUE cState_indent_set(VALUE self, VALUE indent)
1189
1439
  {
1190
- unsigned long len;
1440
+ rb_check_frozen(self);
1191
1441
  GET_STATE(self);
1192
- Check_Type(indent, T_STRING);
1193
- len = RSTRING_LEN(indent);
1194
- if (len == 0) {
1195
- if (state->indent) {
1196
- ruby_xfree(state->indent);
1197
- state->indent = NULL;
1198
- state->indent_len = 0;
1199
- }
1200
- } else {
1201
- if (state->indent) ruby_xfree(state->indent);
1202
- state->indent = fstrndup(RSTRING_PTR(indent), len);
1203
- state->indent_len = len;
1204
- }
1442
+ RB_OBJ_WRITE(self, &state->indent, string_config(indent));
1205
1443
  return Qnil;
1206
1444
  }
1207
1445
 
@@ -1214,7 +1452,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
1214
1452
  static VALUE cState_space(VALUE self)
1215
1453
  {
1216
1454
  GET_STATE(self);
1217
- return state->space ? rb_str_new(state->space, state->space_len) : rb_str_new2("");
1455
+ return state->space ? state->space : rb_str_freeze(rb_utf8_str_new("", 0));
1218
1456
  }
1219
1457
 
1220
1458
  /*
@@ -1225,21 +1463,9 @@ static VALUE cState_space(VALUE self)
1225
1463
  */
1226
1464
  static VALUE cState_space_set(VALUE self, VALUE space)
1227
1465
  {
1228
- unsigned long len;
1466
+ rb_check_frozen(self);
1229
1467
  GET_STATE(self);
1230
- Check_Type(space, T_STRING);
1231
- len = RSTRING_LEN(space);
1232
- if (len == 0) {
1233
- if (state->space) {
1234
- ruby_xfree(state->space);
1235
- state->space = NULL;
1236
- state->space_len = 0;
1237
- }
1238
- } else {
1239
- if (state->space) ruby_xfree(state->space);
1240
- state->space = fstrndup(RSTRING_PTR(space), len);
1241
- state->space_len = len;
1242
- }
1468
+ RB_OBJ_WRITE(self, &state->space, string_config(space));
1243
1469
  return Qnil;
1244
1470
  }
1245
1471
 
@@ -1251,7 +1477,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
1251
1477
  static VALUE cState_space_before(VALUE self)
1252
1478
  {
1253
1479
  GET_STATE(self);
1254
- return state->space_before ? rb_str_new(state->space_before, state->space_before_len) : rb_str_new2("");
1480
+ return state->space_before ? state->space_before : rb_str_freeze(rb_utf8_str_new("", 0));
1255
1481
  }
1256
1482
 
1257
1483
  /*
@@ -1261,21 +1487,9 @@ static VALUE cState_space_before(VALUE self)
1261
1487
  */
1262
1488
  static VALUE cState_space_before_set(VALUE self, VALUE space_before)
1263
1489
  {
1264
- unsigned long len;
1490
+ rb_check_frozen(self);
1265
1491
  GET_STATE(self);
1266
- Check_Type(space_before, T_STRING);
1267
- len = RSTRING_LEN(space_before);
1268
- if (len == 0) {
1269
- if (state->space_before) {
1270
- ruby_xfree(state->space_before);
1271
- state->space_before = NULL;
1272
- state->space_before_len = 0;
1273
- }
1274
- } else {
1275
- if (state->space_before) ruby_xfree(state->space_before);
1276
- state->space_before = fstrndup(RSTRING_PTR(space_before), len);
1277
- state->space_before_len = len;
1278
- }
1492
+ RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
1279
1493
  return Qnil;
1280
1494
  }
1281
1495
 
@@ -1288,7 +1502,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
1288
1502
  static VALUE cState_object_nl(VALUE self)
1289
1503
  {
1290
1504
  GET_STATE(self);
1291
- return state->object_nl ? rb_str_new(state->object_nl, state->object_nl_len) : rb_str_new2("");
1505
+ return state->object_nl ? state->object_nl : rb_str_freeze(rb_utf8_str_new("", 0));
1292
1506
  }
1293
1507
 
1294
1508
  /*
@@ -1299,20 +1513,9 @@ static VALUE cState_object_nl(VALUE self)
1299
1513
  */
1300
1514
  static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
1301
1515
  {
1302
- unsigned long len;
1516
+ rb_check_frozen(self);
1303
1517
  GET_STATE(self);
1304
- Check_Type(object_nl, T_STRING);
1305
- len = RSTRING_LEN(object_nl);
1306
- if (len == 0) {
1307
- if (state->object_nl) {
1308
- ruby_xfree(state->object_nl);
1309
- state->object_nl = NULL;
1310
- }
1311
- } else {
1312
- if (state->object_nl) ruby_xfree(state->object_nl);
1313
- state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
1314
- state->object_nl_len = len;
1315
- }
1518
+ RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
1316
1519
  return Qnil;
1317
1520
  }
1318
1521
 
@@ -1324,7 +1527,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
1324
1527
  static VALUE cState_array_nl(VALUE self)
1325
1528
  {
1326
1529
  GET_STATE(self);
1327
- return state->array_nl ? rb_str_new(state->array_nl, state->array_nl_len) : rb_str_new2("");
1530
+ return state->array_nl ? state->array_nl : rb_str_freeze(rb_utf8_str_new("", 0));
1328
1531
  }
1329
1532
 
1330
1533
  /*
@@ -1334,23 +1537,35 @@ static VALUE cState_array_nl(VALUE self)
1334
1537
  */
1335
1538
  static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
1336
1539
  {
1337
- unsigned long len;
1540
+ rb_check_frozen(self);
1338
1541
  GET_STATE(self);
1339
- Check_Type(array_nl, T_STRING);
1340
- len = RSTRING_LEN(array_nl);
1341
- if (len == 0) {
1342
- if (state->array_nl) {
1343
- ruby_xfree(state->array_nl);
1344
- state->array_nl = NULL;
1345
- }
1346
- } else {
1347
- if (state->array_nl) ruby_xfree(state->array_nl);
1348
- state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
1349
- state->array_nl_len = len;
1350
- }
1542
+ RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
1351
1543
  return Qnil;
1352
1544
  }
1353
1545
 
1546
+ /*
1547
+ * call-seq: as_json()
1548
+ *
1549
+ * This string is put at the end of a line that holds a JSON array.
1550
+ */
1551
+ static VALUE cState_as_json(VALUE self)
1552
+ {
1553
+ GET_STATE(self);
1554
+ return state->as_json;
1555
+ }
1556
+
1557
+ /*
1558
+ * call-seq: as_json=(as_json)
1559
+ *
1560
+ * This string is put at the end of a line that holds a JSON array.
1561
+ */
1562
+ static VALUE cState_as_json_set(VALUE self, VALUE as_json)
1563
+ {
1564
+ rb_check_frozen(self);
1565
+ GET_STATE(self);
1566
+ RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
1567
+ return Qnil;
1568
+ }
1354
1569
 
1355
1570
  /*
1356
1571
  * call-seq: check_circular?
@@ -1376,6 +1591,25 @@ static VALUE cState_max_nesting(VALUE self)
1376
1591
  return LONG2FIX(state->max_nesting);
1377
1592
  }
1378
1593
 
1594
+ static long long_config(VALUE num)
1595
+ {
1596
+ return RTEST(num) ? NUM2LONG(num) : 0;
1597
+ }
1598
+
1599
+ // depth must never be negative; reject early with a clear error.
1600
+ static long depth_config(VALUE num)
1601
+ {
1602
+ if (!RTEST(num)) return 0;
1603
+ long d = NUM2LONG(num);
1604
+ if (RB_UNLIKELY(d < 0)) {
1605
+ rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
1606
+ }
1607
+ if (RB_UNLIKELY(d > INT_MAX)) {
1608
+ rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
1609
+ }
1610
+ return d;
1611
+ }
1612
+
1379
1613
  /*
1380
1614
  * call-seq: max_nesting=(depth)
1381
1615
  *
@@ -1384,33 +1618,67 @@ static VALUE cState_max_nesting(VALUE self)
1384
1618
  */
1385
1619
  static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
1386
1620
  {
1621
+ rb_check_frozen(self);
1387
1622
  GET_STATE(self);
1388
- Check_Type(depth, T_FIXNUM);
1389
- return state->max_nesting = FIX2LONG(depth);
1623
+ state->max_nesting = long_config(depth);
1624
+ return Qnil;
1390
1625
  }
1391
1626
 
1392
1627
  /*
1393
- * call-seq: escape_slash
1628
+ * call-seq: script_safe
1394
1629
  *
1395
1630
  * If this boolean is true, the forward slashes will be escaped in
1396
1631
  * the json output.
1397
1632
  */
1398
- static VALUE cState_escape_slash(VALUE self)
1633
+ static VALUE cState_script_safe(VALUE self)
1399
1634
  {
1400
1635
  GET_STATE(self);
1401
- return state->escape_slash ? Qtrue : Qfalse;
1636
+ return state->script_safe ? Qtrue : Qfalse;
1402
1637
  }
1403
1638
 
1404
1639
  /*
1405
- * call-seq: escape_slash=(depth)
1640
+ * call-seq: script_safe=(enable)
1406
1641
  *
1407
1642
  * This sets whether or not the forward slashes will be escaped in
1408
1643
  * the json output.
1409
1644
  */
1410
- static VALUE cState_escape_slash_set(VALUE self, VALUE enable)
1645
+ static VALUE cState_script_safe_set(VALUE self, VALUE enable)
1646
+ {
1647
+ rb_check_frozen(self);
1648
+ GET_STATE(self);
1649
+ state->script_safe = RTEST(enable);
1650
+ return Qnil;
1651
+ }
1652
+
1653
+ /*
1654
+ * call-seq: strict
1655
+ *
1656
+ * If this boolean is false, types unsupported by the JSON format will
1657
+ * be serialized as strings.
1658
+ * If this boolean is true, types unsupported by the JSON format will
1659
+ * raise a JSON::GeneratorError.
1660
+ */
1661
+ static VALUE cState_strict(VALUE self)
1662
+ {
1663
+ GET_STATE(self);
1664
+ return state->strict ? Qtrue : Qfalse;
1665
+ }
1666
+
1667
+ /*
1668
+ * call-seq: strict=(enable)
1669
+ *
1670
+ * This sets whether or not to serialize types unsupported by the
1671
+ * JSON format as strings.
1672
+ * If this boolean is false, types unsupported by the JSON format will
1673
+ * be serialized as strings.
1674
+ * If this boolean is true, types unsupported by the JSON format will
1675
+ * raise a JSON::GeneratorError.
1676
+ */
1677
+ static VALUE cState_strict_set(VALUE self, VALUE enable)
1411
1678
  {
1679
+ rb_check_frozen(self);
1412
1680
  GET_STATE(self);
1413
- state->escape_slash = RTEST(enable);
1681
+ state->strict = RTEST(enable);
1414
1682
  return Qnil;
1415
1683
  }
1416
1684
 
@@ -1426,6 +1694,19 @@ static VALUE cState_allow_nan_p(VALUE self)
1426
1694
  return state->allow_nan ? Qtrue : Qfalse;
1427
1695
  }
1428
1696
 
1697
+ /*
1698
+ * call-seq: allow_nan=(enable)
1699
+ *
1700
+ * This sets whether or not to serialize NaN, Infinity, and -Infinity
1701
+ */
1702
+ static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
1703
+ {
1704
+ rb_check_frozen(self);
1705
+ GET_STATE(self);
1706
+ state->allow_nan = RTEST(enable);
1707
+ return Qnil;
1708
+ }
1709
+
1429
1710
  /*
1430
1711
  * call-seq: ascii_only?
1431
1712
  *
@@ -1438,6 +1719,81 @@ static VALUE cState_ascii_only_p(VALUE self)
1438
1719
  return state->ascii_only ? Qtrue : Qfalse;
1439
1720
  }
1440
1721
 
1722
+ /*
1723
+ * call-seq: ascii_only=(enable)
1724
+ *
1725
+ * This sets whether only ASCII characters should be generated.
1726
+ */
1727
+ static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
1728
+ {
1729
+ rb_check_frozen(self);
1730
+ GET_STATE(self);
1731
+ state->ascii_only = RTEST(enable);
1732
+ return Qnil;
1733
+ }
1734
+
1735
+ static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc)
1736
+ {
1737
+ if (!rb_obj_is_proc(proc)) {
1738
+ rb_raise(rb_eTypeError, "sort_key_proc must be a Proc");
1739
+ }
1740
+ return default_sort_keys_proc = proc;
1741
+ }
1742
+
1743
+ static VALUE normalize_sort_keys(VALUE value)
1744
+ {
1745
+ if (rb_obj_is_proc(value)) {
1746
+ return value;
1747
+ } else if (value == Qtrue) {
1748
+ return default_sort_keys_proc;
1749
+ } else if (RTEST(value)) {
1750
+ rb_raise(rb_eTypeError, "The `sort_keys` argument must be a boolean or a Proc");
1751
+ } else {
1752
+ return Qfalse;
1753
+ }
1754
+ }
1755
+
1756
+ /*
1757
+ * call-seq: sort_keys
1758
+ *
1759
+ * Get the value of sort_keys.
1760
+ */
1761
+ static VALUE cState_sort_keys_p(VALUE self)
1762
+ {
1763
+ GET_STATE(self);
1764
+ return state->sort_keys;
1765
+ }
1766
+
1767
+ /*
1768
+ * call-seq: sort_keys=(value)
1769
+ *
1770
+ * value is a boolean or a proc. If the value is the boolean true, object keys
1771
+ * will be sorted lexicographically in ascending order.
1772
+ *
1773
+ * If the value is a proc, it receives the entire Hash and must return a Hash
1774
+ * with its pairs in the desired order, allowing for arbitrary sorting.
1775
+ */
1776
+ static VALUE cState_sort_keys_set(VALUE self, VALUE value)
1777
+ {
1778
+ rb_check_frozen(self);
1779
+ GET_STATE(self);
1780
+ RB_OBJ_WRITE(self, &state->sort_keys, normalize_sort_keys(value));
1781
+ return Qnil;
1782
+ }
1783
+
1784
+ static VALUE cState_allow_duplicate_key_p(VALUE self)
1785
+ {
1786
+ GET_STATE(self);
1787
+ switch (state->on_duplicate_key) {
1788
+ case JSON_IGNORE:
1789
+ return Qtrue;
1790
+ case JSON_DEPRECATED:
1791
+ return Qnil;
1792
+ default:
1793
+ return Qfalse;
1794
+ }
1795
+ }
1796
+
1441
1797
  /*
1442
1798
  * call-seq: depth
1443
1799
  *
@@ -1457,9 +1813,9 @@ static VALUE cState_depth(VALUE self)
1457
1813
  */
1458
1814
  static VALUE cState_depth_set(VALUE self, VALUE depth)
1459
1815
  {
1816
+ rb_check_frozen(self);
1460
1817
  GET_STATE(self);
1461
- Check_Type(depth, T_FIXNUM);
1462
- state->depth = FIX2LONG(depth);
1818
+ state->depth = depth_config(depth);
1463
1819
  return Qnil;
1464
1820
  }
1465
1821
 
@@ -1474,6 +1830,15 @@ static VALUE cState_buffer_initial_length(VALUE self)
1474
1830
  return LONG2FIX(state->buffer_initial_length);
1475
1831
  }
1476
1832
 
1833
+ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_initial_length)
1834
+ {
1835
+ Check_Type(buffer_initial_length, T_FIXNUM);
1836
+ long initial_length = FIX2LONG(buffer_initial_length);
1837
+ if (initial_length > 0) {
1838
+ state->buffer_initial_length = initial_length;
1839
+ }
1840
+ }
1841
+
1477
1842
  /*
1478
1843
  * call-seq: buffer_initial_length=(length)
1479
1844
  *
@@ -1482,19 +1847,113 @@ static VALUE cState_buffer_initial_length(VALUE self)
1482
1847
  */
1483
1848
  static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
1484
1849
  {
1485
- long initial_length;
1850
+ rb_check_frozen(self);
1486
1851
  GET_STATE(self);
1487
- Check_Type(buffer_initial_length, T_FIXNUM);
1488
- initial_length = FIX2LONG(buffer_initial_length);
1489
- if (initial_length > 0) {
1490
- state->buffer_initial_length = initial_length;
1491
- }
1852
+ buffer_initial_length_set(state, buffer_initial_length);
1492
1853
  return Qnil;
1493
1854
  }
1494
1855
 
1495
- /*
1496
- *
1497
- */
1856
+ struct configure_state_data {
1857
+ JSON_Generator_State *state;
1858
+ VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated
1859
+ };
1860
+
1861
+ static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value)
1862
+ {
1863
+ if (RTEST(data->vstate)) {
1864
+ RB_OBJ_WRITE(data->vstate, field, value);
1865
+ } else {
1866
+ *field = value;
1867
+ }
1868
+ }
1869
+
1870
+ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1871
+ {
1872
+ struct configure_state_data *data = (struct configure_state_data *)_arg;
1873
+ JSON_Generator_State *state = data->state;
1874
+
1875
+ if (key == sym_indent) { state_write_value(data, &state->indent, string_config(val)); }
1876
+ else if (key == sym_space) { state_write_value(data, &state->space, string_config(val)); }
1877
+ else if (key == sym_space_before) { state_write_value(data, &state->space_before, string_config(val)); }
1878
+ else if (key == sym_object_nl) { state_write_value(data, &state->object_nl, string_config(val)); }
1879
+ else if (key == sym_array_nl) { state_write_value(data, &state->array_nl, string_config(val)); }
1880
+ else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
1881
+ else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
1882
+ else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
1883
+ else if (key == sym_depth) { state->depth = depth_config(val); }
1884
+ else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
1885
+ else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
1886
+ else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
1887
+ else if (key == sym_strict) { state->strict = RTEST(val); }
1888
+ else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1889
+ else if (key == sym_as_json) {
1890
+ VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
1891
+ state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
1892
+ state_write_value(data, &state->as_json, proc);
1893
+ }
1894
+ else if (key == sym_sort_keys) {
1895
+ state_write_value(data, &state->sort_keys, normalize_sort_keys(val));
1896
+ }
1897
+ return ST_CONTINUE;
1898
+ }
1899
+
1900
+ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE config)
1901
+ {
1902
+ if (!RTEST(config)) return;
1903
+
1904
+ Check_Type(config, T_HASH);
1905
+
1906
+ if (!RHASH_SIZE(config)) return;
1907
+
1908
+ struct configure_state_data data = {
1909
+ .state = state,
1910
+ .vstate = vstate
1911
+ };
1912
+
1913
+ // We assume in most cases few keys are set so it's faster to go over
1914
+ // the provided keys than to check all possible keys.
1915
+ rb_hash_foreach(config, configure_state_i, (VALUE)&data);
1916
+ }
1917
+
1918
+ static VALUE cState_configure(VALUE self, VALUE opts)
1919
+ {
1920
+ rb_check_frozen(self);
1921
+ GET_STATE(self);
1922
+ configure_state(state, self, opts);
1923
+ return self;
1924
+ }
1925
+
1926
+ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
1927
+ {
1928
+ JSON_Generator_State state = {0};
1929
+ state_init(&state);
1930
+ configure_state(&state, Qfalse, opts);
1931
+
1932
+ char stack_buffer[FBUFFER_STACK_SIZE];
1933
+ FBuffer buffer = { 0 };
1934
+ fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1935
+
1936
+ struct generate_json_data data = {
1937
+ .buffer = &buffer,
1938
+ .vstate = Qfalse,
1939
+ .state = &state,
1940
+ .depth = state.depth,
1941
+ .obj = obj,
1942
+ .func = func,
1943
+ };
1944
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1945
+ }
1946
+
1947
+ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1948
+ {
1949
+ return cState_m_do_generate(klass, obj, opts, io, generate_json);
1950
+ }
1951
+
1952
+ static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1953
+ {
1954
+ return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
1955
+ }
1956
+
1498
1957
  void Init_generator(void)
1499
1958
  {
1500
1959
  #ifdef HAVE_RB_EXT_RACTOR_SAFE
@@ -1505,18 +1964,30 @@ void Init_generator(void)
1505
1964
  rb_require("json/common");
1506
1965
 
1507
1966
  mJSON = rb_define_module("JSON");
1508
- mExt = rb_define_module_under(mJSON, "Ext");
1509
- mGenerator = rb_define_module_under(mExt, "Generator");
1510
1967
 
1968
+ rb_global_variable(&cFragment);
1969
+ cFragment = rb_const_get(mJSON, rb_intern("Fragment"));
1970
+
1971
+ VALUE mExt = rb_define_module_under(mJSON, "Ext");
1972
+ VALUE mGenerator = rb_define_module_under(mExt, "Generator");
1973
+
1974
+ rb_global_variable(&default_sort_keys_proc);
1975
+
1976
+ rb_global_variable(&eGeneratorError);
1511
1977
  eGeneratorError = rb_path2class("JSON::GeneratorError");
1978
+
1979
+ rb_global_variable(&eNestingError);
1512
1980
  eNestingError = rb_path2class("JSON::NestingError");
1513
- rb_gc_register_mark_object(eGeneratorError);
1514
- rb_gc_register_mark_object(eNestingError);
1515
1981
 
1516
1982
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
1517
1983
  rb_define_alloc_func(cState, cState_s_allocate);
1518
1984
  rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
1985
+ rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1);
1986
+
1519
1987
  rb_define_method(cState, "initialize", cState_initialize, -1);
1988
+ rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
1989
+ rb_define_private_method(cState, "_configure", cState_configure, 1);
1990
+
1520
1991
  rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
1521
1992
  rb_define_method(cState, "indent", cState_indent, 0);
1522
1993
  rb_define_method(cState, "indent=", cState_indent_set, 1);
@@ -1528,81 +1999,68 @@ void Init_generator(void)
1528
1999
  rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
1529
2000
  rb_define_method(cState, "array_nl", cState_array_nl, 0);
1530
2001
  rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
2002
+ rb_define_method(cState, "as_json", cState_as_json, 0);
2003
+ rb_define_method(cState, "as_json=", cState_as_json_set, 1);
1531
2004
  rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
1532
2005
  rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
1533
- rb_define_method(cState, "escape_slash", cState_escape_slash, 0);
1534
- rb_define_method(cState, "escape_slash?", cState_escape_slash, 0);
1535
- rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1);
2006
+ rb_define_method(cState, "script_safe", cState_script_safe, 0);
2007
+ rb_define_method(cState, "script_safe?", cState_script_safe, 0);
2008
+ rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
2009
+ rb_define_alias(cState, "escape_slash", "script_safe");
2010
+ rb_define_alias(cState, "escape_slash?", "script_safe?");
2011
+ rb_define_alias(cState, "escape_slash=", "script_safe=");
2012
+ rb_define_method(cState, "strict", cState_strict, 0);
2013
+ rb_define_method(cState, "strict?", cState_strict, 0);
2014
+ rb_define_method(cState, "strict=", cState_strict_set, 1);
1536
2015
  rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
1537
2016
  rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
2017
+ rb_define_method(cState, "allow_nan=", cState_allow_nan_set, 1);
1538
2018
  rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
2019
+ rb_define_method(cState, "ascii_only=", cState_ascii_only_set, 1);
1539
2020
  rb_define_method(cState, "depth", cState_depth, 0);
1540
2021
  rb_define_method(cState, "depth=", cState_depth_set, 1);
1541
2022
  rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
1542
2023
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
1543
- rb_define_method(cState, "configure", cState_configure, 1);
1544
- rb_define_alias(cState, "merge", "configure");
1545
- rb_define_method(cState, "to_h", cState_to_h, 0);
1546
- rb_define_alias(cState, "to_hash", "to_h");
1547
- rb_define_method(cState, "[]", cState_aref, 1);
1548
- rb_define_method(cState, "[]=", cState_aset, 2);
1549
- rb_define_method(cState, "generate", cState_generate, 1);
1550
-
1551
- mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
1552
- mObject = rb_define_module_under(mGeneratorMethods, "Object");
1553
- rb_define_method(mObject, "to_json", mObject_to_json, -1);
1554
- mHash = rb_define_module_under(mGeneratorMethods, "Hash");
1555
- rb_define_method(mHash, "to_json", mHash_to_json, -1);
1556
- mArray = rb_define_module_under(mGeneratorMethods, "Array");
1557
- rb_define_method(mArray, "to_json", mArray_to_json, -1);
1558
- #ifdef RUBY_INTEGER_UNIFICATION
1559
- mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
1560
- rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
1561
- #else
1562
- mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
1563
- rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
1564
- mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
1565
- rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
1566
- #endif
1567
- mFloat = rb_define_module_under(mGeneratorMethods, "Float");
1568
- rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
1569
- mString = rb_define_module_under(mGeneratorMethods, "String");
1570
- rb_define_singleton_method(mString, "included", mString_included_s, 1);
1571
- rb_define_method(mString, "to_json", mString_to_json, -1);
1572
- rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
1573
- rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
1574
- mString_Extend = rb_define_module_under(mString, "Extend");
1575
- rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
1576
- mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
1577
- rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
1578
- mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
1579
- rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
1580
- mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
1581
- rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
2024
+ rb_define_method(cState, "generate", cState_generate, -1);
2025
+ rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
2026
+ rb_define_method(cState, "sort_keys", cState_sort_keys_p, 0);
2027
+ rb_define_method(cState, "sort_keys=", cState_sort_keys_set, 1);
2028
+
2029
+ rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
2030
+
2031
+ rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
2032
+ rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
2033
+
2034
+ rb_global_variable(&Encoding_UTF_8);
2035
+ Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
1582
2036
 
1583
2037
  i_to_s = rb_intern("to_s");
1584
2038
  i_to_json = rb_intern("to_json");
1585
2039
  i_new = rb_intern("new");
1586
- i_indent = rb_intern("indent");
1587
- i_space = rb_intern("space");
1588
- i_space_before = rb_intern("space_before");
1589
- i_object_nl = rb_intern("object_nl");
1590
- i_array_nl = rb_intern("array_nl");
1591
- i_max_nesting = rb_intern("max_nesting");
1592
- i_escape_slash = rb_intern("escape_slash");
1593
- i_allow_nan = rb_intern("allow_nan");
1594
- i_ascii_only = rb_intern("ascii_only");
1595
- i_depth = rb_intern("depth");
1596
- i_buffer_initial_length = rb_intern("buffer_initial_length");
1597
- i_pack = rb_intern("pack");
1598
- i_unpack = rb_intern("unpack");
1599
- i_create_id = rb_intern("create_id");
1600
- i_extend = rb_intern("extend");
1601
- i_key_p = rb_intern("key?");
1602
- i_aref = rb_intern("[]");
1603
- i_send = rb_intern("__send__");
1604
- i_respond_to_p = rb_intern("respond_to?");
1605
- i_match = rb_intern("match");
1606
- i_keys = rb_intern("keys");
1607
- i_dup = rb_intern("dup");
2040
+ i_encode = rb_intern("encode");
2041
+
2042
+ sym_indent = ID2SYM(rb_intern("indent"));
2043
+ sym_space = ID2SYM(rb_intern("space"));
2044
+ sym_space_before = ID2SYM(rb_intern("space_before"));
2045
+ sym_object_nl = ID2SYM(rb_intern("object_nl"));
2046
+ sym_array_nl = ID2SYM(rb_intern("array_nl"));
2047
+ sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
2048
+ sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
2049
+ sym_ascii_only = ID2SYM(rb_intern("ascii_only"));
2050
+ sym_depth = ID2SYM(rb_intern("depth"));
2051
+ sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length"));
2052
+ sym_script_safe = ID2SYM(rb_intern("script_safe"));
2053
+ sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
2054
+ sym_strict = ID2SYM(rb_intern("strict"));
2055
+ sym_as_json = ID2SYM(rb_intern("as_json"));
2056
+ sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
2057
+ sym_sort_keys = ID2SYM(rb_intern("sort_keys"));
2058
+
2059
+ usascii_encindex = rb_usascii_encindex();
2060
+ utf8_encindex = rb_utf8_encindex();
2061
+ binary_encindex = rb_ascii8bit_encindex();
2062
+
2063
+ rb_require("json/ext/generator/state");
2064
+
2065
+ simd_impl = find_simd_implementation();
1608
2066
  }