json 2.12.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
- #include "ruby.h"
1
+ #include "../json.h"
2
2
  #include "../fbuffer/fbuffer.h"
3
3
  #include "../vendor/fpconv.c"
4
4
 
5
5
  #include <math.h>
6
6
  #include <ctype.h>
7
7
 
8
- #include "simd.h"
8
+ #include "../simd/simd.h"
9
9
 
10
10
  /* ruby api and some helpers */
11
11
 
@@ -21,21 +21,20 @@ typedef struct JSON_Generator_StateStruct {
21
21
  long depth;
22
22
  long buffer_initial_length;
23
23
 
24
+ bool allow_duplicate_key;
25
+ bool as_json_single_arg;
24
26
  bool allow_nan;
25
27
  bool ascii_only;
26
28
  bool script_safe;
27
29
  bool strict;
30
+ VALUE sort_keys;
28
31
  } JSON_Generator_State;
29
32
 
30
- #ifndef RB_UNLIKELY
31
- #define RB_UNLIKELY(cond) (cond)
32
- #endif
33
-
34
- static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
33
+ static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc;
35
34
 
36
- static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
37
- static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
38
- sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
35
+ static ID i_to_s, i_to_json, i_new, i_encode;
36
+ 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,
37
+ sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_strict, sym_as_json, sym_sort_keys;
39
38
 
40
39
 
41
40
  #define GET_STATE_TO(self, state) \
@@ -55,8 +54,11 @@ struct generate_json_data {
55
54
  JSON_Generator_State *state;
56
55
  VALUE obj;
57
56
  generator_func func;
57
+ long depth;
58
58
  };
59
59
 
60
+ static SIMD_Implementation simd_impl;
61
+
60
62
  static VALUE cState_from_state_s(VALUE self, VALUE opts);
61
63
  static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
62
64
  static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
@@ -66,9 +68,6 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
66
68
  static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
67
69
  static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
68
70
  static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
69
- #ifdef RUBY_INTEGER_UNIFICATION
70
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
71
- #endif
72
71
  static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
73
72
  static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
74
73
  static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
@@ -76,23 +75,18 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
76
75
 
77
76
  static int usascii_encindex, utf8_encindex, binary_encindex;
78
77
 
79
- #ifdef RBIMPL_ATTR_NORETURN
80
- RBIMPL_ATTR_NORETURN()
81
- #endif
82
- static void raise_generator_error_str(VALUE invalid_object, VALUE str)
78
+ NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
83
79
  {
80
+ rb_enc_associate_index(str, utf8_encindex);
84
81
  VALUE exc = rb_exc_new_str(eGeneratorError, str);
85
82
  rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
86
83
  rb_exc_raise(exc);
87
84
  }
88
85
 
89
- #ifdef RBIMPL_ATTR_NORETURN
90
- RBIMPL_ATTR_NORETURN()
91
- #endif
92
86
  #ifdef RBIMPL_ATTR_FORMAT
93
87
  RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
94
88
  #endif
95
- static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
89
+ NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
96
90
  {
97
91
  va_list args;
98
92
  va_start(args, fmt);
@@ -127,18 +121,12 @@ typedef struct _search_state {
127
121
  #endif /* HAVE_SIMD */
128
122
  } search_state;
129
123
 
130
- #if (defined(__GNUC__ ) || defined(__clang__))
131
- #define FORCE_INLINE __attribute__((always_inline))
132
- #else
133
- #define FORCE_INLINE
134
- #endif
135
-
136
- static inline FORCE_INLINE void search_flush(search_state *search)
124
+ ALWAYS_INLINE(static) void search_flush(search_state *search)
137
125
  {
138
126
  // Do not remove this conditional without profiling, specifically escape-heavy text.
139
127
  // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
140
- // For back-to-back characters that need to be escaped, specifcally for the SIMD code paths, this method
141
- // will be called just before calling escape_UTF8_char_basic. There will be no characers to append for the
128
+ // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
129
+ // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
142
130
  // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
143
131
  // nothing needs to be flushed, we can save a few memory references with this conditional.
144
132
  if (search->ptr > search->cursor) {
@@ -160,8 +148,6 @@ static const unsigned char escape_table_basic[256] = {
160
148
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161
149
  };
162
150
 
163
- static unsigned char (*search_escape_basic_impl)(search_state *);
164
-
165
151
  static inline unsigned char search_escape_basic(search_state *search)
166
152
  {
167
153
  while (search->ptr < search->end) {
@@ -176,7 +162,7 @@ static inline unsigned char search_escape_basic(search_state *search)
176
162
  return 0;
177
163
  }
178
164
 
179
- static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
165
+ ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
180
166
  {
181
167
  const unsigned char ch = (unsigned char)*search->ptr;
182
168
  switch (ch) {
@@ -217,11 +203,39 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
217
203
  * Everything else (should be UTF-8) is just passed through and
218
204
  * appended to the result.
219
205
  */
206
+
207
+
208
+ #if defined(HAVE_SIMD_NEON)
209
+ static inline unsigned char search_escape_basic_neon(search_state *search);
210
+ #elif defined(HAVE_SIMD_SSE2)
211
+ static inline unsigned char search_escape_basic_sse2(search_state *search);
212
+ #endif
213
+
214
+ static inline unsigned char search_escape_basic(search_state *search);
215
+
220
216
  static inline void convert_UTF8_to_JSON(search_state *search)
221
217
  {
222
- while (search_escape_basic_impl(search)) {
218
+ #ifdef HAVE_SIMD
219
+ #if defined(HAVE_SIMD_NEON)
220
+ while (search_escape_basic_neon(search)) {
221
+ escape_UTF8_char_basic(search);
222
+ }
223
+ #elif defined(HAVE_SIMD_SSE2)
224
+ if (simd_impl == SIMD_SSE2) {
225
+ while (search_escape_basic_sse2(search)) {
226
+ escape_UTF8_char_basic(search);
227
+ }
228
+ return;
229
+ }
230
+ while (search_escape_basic(search)) {
231
+ escape_UTF8_char_basic(search);
232
+ }
233
+ #endif
234
+ #else
235
+ while (search_escape_basic(search)) {
223
236
  escape_UTF8_char_basic(search);
224
237
  }
238
+ #endif /* HAVE_SIMD */
225
239
  }
226
240
 
227
241
  static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
@@ -263,8 +277,10 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
263
277
 
264
278
  #ifdef HAVE_SIMD
265
279
 
266
- static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
280
+ ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
267
281
  {
282
+ RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
283
+
268
284
  // Flush the buffer so everything up until the last 'len' characters are unflushed.
269
285
  search_flush(search);
270
286
 
@@ -274,19 +290,25 @@ static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsi
274
290
  char *s = (buf->ptr + buf->len);
275
291
 
276
292
  // Pad the buffer with dummy characters that won't need escaping.
277
- // This seem wateful at first sight, but memset of vector length is very fast.
278
- memset(s, 'X', vec_len);
293
+ // This seem wasteful at first sight, but memset of vector length is very fast.
294
+ // This is a space as it can be directly represented as an immediate on AArch64.
295
+ memset(s, ' ', vec_len);
279
296
 
280
297
  // Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
281
298
  // to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
282
- MEMCPY(s, search->ptr, char, len);
299
+ if (vec_len == 16) {
300
+ RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
301
+ json_fast_memcpy16(s, search->ptr, len);
302
+ } else {
303
+ MEMCPY(s, search->ptr, char, len);
304
+ }
283
305
 
284
306
  return s;
285
307
  }
286
308
 
287
309
  #ifdef HAVE_SIMD_NEON
288
310
 
289
- static inline FORCE_INLINE unsigned char neon_next_match(search_state *search)
311
+ ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
290
312
  {
291
313
  uint64_t mask = search->matches_mask;
292
314
  uint32_t index = trailing_zeros64(mask) >> 2;
@@ -304,28 +326,6 @@ static inline FORCE_INLINE unsigned char neon_next_match(search_state *search)
304
326
  return 1;
305
327
  }
306
328
 
307
- // See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
308
- static inline FORCE_INLINE uint64_t neon_match_mask(uint8x16_t matches)
309
- {
310
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4);
311
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
312
- return mask & 0x8888888888888888ull;
313
- }
314
-
315
- static inline FORCE_INLINE uint64_t neon_rules_update(const char *ptr)
316
- {
317
- uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
318
-
319
- // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
320
- // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/
321
- const uint8x16_t too_low_or_dbl_quote = vcltq_u8(veorq_u8(chunk, vdupq_n_u8(2)), vdupq_n_u8(33));
322
-
323
- uint8x16_t has_backslash = vceqq_u8(chunk, vdupq_n_u8('\\'));
324
- uint8x16_t needs_escape = vorrq_u8(too_low_or_dbl_quote, has_backslash);
325
-
326
- return neon_match_mask(needs_escape);
327
- }
328
-
329
329
  static inline unsigned char search_escape_basic_neon(search_state *search)
330
330
  {
331
331
  if (RB_UNLIKELY(search->has_matches)) {
@@ -333,7 +333,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
333
333
  if (search->matches_mask > 0) {
334
334
  return neon_next_match(search);
335
335
  } else {
336
- // neon_next_match will only advance search->ptr up to the last matching character.
336
+ // neon_next_match will only advance search->ptr up to the last matching character.
337
337
  // Skip over any characters in the last chunk that occur after the last match.
338
338
  search->has_matches = false;
339
339
  search->ptr = search->chunk_end;
@@ -342,67 +342,61 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
342
342
 
343
343
  /*
344
344
  * The code below implements an SIMD-based algorithm to determine if N bytes at a time
345
- * need to be escaped.
346
- *
345
+ * need to be escaped.
346
+ *
347
347
  * Assume the ptr = "Te\sting!" (the double quotes are included in the string)
348
- *
348
+ *
349
349
  * The explanation will be limited to the first 8 bytes of the string for simplicity. However
350
350
  * the vector insructions may work on larger vectors.
351
- *
351
+ *
352
352
  * First, we load three constants 'lower_bound', 'backslash' and 'dblquote" in vector registers.
353
- *
354
- * lower_bound: [20 20 20 20 20 20 20 20]
355
- * backslash: [5C 5C 5C 5C 5C 5C 5C 5C]
356
- * dblquote: [22 22 22 22 22 22 22 22]
357
- *
358
- * Next we load the first chunk of the ptr:
353
+ *
354
+ * lower_bound: [20 20 20 20 20 20 20 20]
355
+ * backslash: [5C 5C 5C 5C 5C 5C 5C 5C]
356
+ * dblquote: [22 22 22 22 22 22 22 22]
357
+ *
358
+ * Next we load the first chunk of the ptr:
359
359
  * [22 54 65 5C 73 74 69 6E] (" T e \ s t i n)
360
- *
360
+ *
361
361
  * First we check if any byte in chunk is less than 32 (0x20). This returns the following vector
362
362
  * as no bytes are less than 32 (0x20):
363
363
  * [0 0 0 0 0 0 0 0]
364
- *
364
+ *
365
365
  * Next, we check if any byte in chunk is equal to a backslash:
366
366
  * [0 0 0 FF 0 0 0 0]
367
- *
367
+ *
368
368
  * Finally we check if any byte in chunk is equal to a double quote:
369
- * [FF 0 0 0 0 0 0 0]
370
- *
369
+ * [FF 0 0 0 0 0 0 0]
370
+ *
371
371
  * Now we have three vectors where each byte indicates if the corresponding byte in chunk
372
372
  * needs to be escaped. We combine these vectors with a series of logical OR instructions.
373
373
  * This is the needs_escape vector and it is equal to:
374
- * [FF 0 0 FF 0 0 0 0]
375
- *
374
+ * [FF 0 0 FF 0 0 0 0]
375
+ *
376
376
  * Next we compute the bitwise AND between each byte and 0x1 and compute the horizontal sum of
377
377
  * the values in the vector. This computes how many bytes need to be escaped within this chunk.
378
- *
378
+ *
379
379
  * Finally we compute a mask that indicates which bytes need to be escaped. If the mask is 0 then,
380
380
  * no bytes need to be escaped and we can continue to the next chunk. If the mask is not 0 then we
381
381
  * have at least one byte that needs to be escaped.
382
382
  */
383
- while (search->ptr + sizeof(uint8x16_t) <= search->end) {
384
- uint64_t mask = neon_rules_update(search->ptr);
385
383
 
386
- if (!mask) {
387
- search->ptr += sizeof(uint8x16_t);
388
- continue;
389
- }
390
- search->matches_mask = mask;
384
+ if (string_scan_simd_neon(&search->ptr, search->end, &search->matches_mask)) {
391
385
  search->has_matches = true;
392
386
  search->chunk_base = search->ptr;
393
387
  search->chunk_end = search->ptr + sizeof(uint8x16_t);
394
388
  return neon_next_match(search);
395
389
  }
396
390
 
397
- // There are fewer than 16 bytes left.
391
+ // There are fewer than 16 bytes left.
398
392
  unsigned long remaining = (search->end - search->ptr);
399
393
  if (remaining >= SIMD_MINIMUM_THRESHOLD) {
400
394
  char *s = copy_remaining_bytes(search, sizeof(uint8x16_t), remaining);
401
395
 
402
- uint64_t mask = neon_rules_update(s);
396
+ uint64_t mask = compute_chunk_mask_neon(s);
403
397
 
404
398
  if (!mask) {
405
- // Nothing to escape, ensure search_flush doesn't do anything by setting
399
+ // Nothing to escape, ensure search_flush doesn't do anything by setting
406
400
  // search->cursor to search->ptr.
407
401
  fbuffer_consumed(search->buffer, remaining);
408
402
  search->ptr = search->end;
@@ -428,12 +422,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
428
422
 
429
423
  #ifdef HAVE_SIMD_SSE2
430
424
 
431
- #define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a)
432
- #define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a)
433
- #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1))
434
- #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a)
435
-
436
- static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
425
+ ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
437
426
  {
438
427
  int mask = search->matches_mask;
439
428
  int index = trailing_zeros(mask);
@@ -457,26 +446,14 @@ static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
457
446
  #define TARGET_SSE2
458
447
  #endif
459
448
 
460
- static inline TARGET_SSE2 FORCE_INLINE int sse2_update(const char *ptr)
461
- {
462
- __m128i chunk = _mm_loadu_si128((__m128i const*)ptr);
463
-
464
- // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
465
- // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/
466
- __m128i too_low_or_dbl_quote = _mm_cmplt_epu8(_mm_xor_si128(chunk, _mm_set1_epi8(2)), _mm_set1_epi8(33));
467
- __m128i has_backslash = _mm_cmpeq_epi8(chunk, _mm_set1_epi8('\\'));
468
- __m128i needs_escape = _mm_or_si128(too_low_or_dbl_quote, has_backslash);
469
- return _mm_movemask_epi8(needs_escape);
470
- }
471
-
472
- static inline TARGET_SSE2 FORCE_INLINE unsigned char search_escape_basic_sse2(search_state *search)
449
+ ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
473
450
  {
474
451
  if (RB_UNLIKELY(search->has_matches)) {
475
452
  // There are more matches if search->matches_mask > 0.
476
453
  if (search->matches_mask > 0) {
477
454
  return sse2_next_match(search);
478
455
  } else {
479
- // sse2_next_match will only advance search->ptr up to the last matching character.
456
+ // sse2_next_match will only advance search->ptr up to the last matching character.
480
457
  // Skip over any characters in the last chunk that occur after the last match.
481
458
  search->has_matches = false;
482
459
  if (RB_UNLIKELY(search->chunk_base + sizeof(__m128i) >= search->end)) {
@@ -487,29 +464,22 @@ static inline TARGET_SSE2 FORCE_INLINE unsigned char search_escape_basic_sse2(se
487
464
  }
488
465
  }
489
466
 
490
- while (search->ptr + sizeof(__m128i) <= search->end) {
491
- int needs_escape_mask = sse2_update(search->ptr);
492
-
493
- if (needs_escape_mask == 0) {
494
- search->ptr += sizeof(__m128i);
495
- continue;
496
- }
497
-
467
+ if (string_scan_simd_sse2(&search->ptr, search->end, &search->matches_mask)) {
498
468
  search->has_matches = true;
499
- search->matches_mask = needs_escape_mask;
500
469
  search->chunk_base = search->ptr;
470
+ search->chunk_end = search->ptr + sizeof(__m128i);
501
471
  return sse2_next_match(search);
502
472
  }
503
473
 
504
- // There are fewer than 16 bytes left.
474
+ // There are fewer than 16 bytes left.
505
475
  unsigned long remaining = (search->end - search->ptr);
506
476
  if (remaining >= SIMD_MINIMUM_THRESHOLD) {
507
477
  char *s = copy_remaining_bytes(search, sizeof(__m128i), remaining);
508
478
 
509
- int needs_escape_mask = sse2_update(s);
479
+ int needs_escape_mask = compute_chunk_mask_sse2(s);
510
480
 
511
481
  if (needs_escape_mask == 0) {
512
- // Nothing to escape, ensure search_flush doesn't do anything by setting
482
+ // Nothing to escape, ensure search_flush doesn't do anything by setting
513
483
  // search->cursor to search->ptr.
514
484
  fbuffer_consumed(search->buffer, remaining);
515
485
  search->ptr = search->end;
@@ -638,7 +608,8 @@ static inline unsigned char search_ascii_only_escape(search_state *search, const
638
608
  return 0;
639
609
  }
640
610
 
641
- static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len) {
611
+ static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len)
612
+ {
642
613
  const unsigned char ch = (unsigned char)*search->ptr;
643
614
  switch (ch_len) {
644
615
  case 1: {
@@ -668,7 +639,7 @@ static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_
668
639
 
669
640
  uint32_t wchar = 0;
670
641
 
671
- switch(ch_len) {
642
+ switch (ch_len) {
672
643
  case 2:
673
644
  wchar = ch & 0x1F;
674
645
  break;
@@ -723,288 +694,6 @@ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned
723
694
  }
724
695
  }
725
696
 
726
- /*
727
- * Document-module: JSON::Ext::Generator
728
- *
729
- * This is the JSON generator implemented as a C extension. It can be
730
- * configured to be used by setting
731
- *
732
- * JSON.generator = JSON::Ext::Generator
733
- *
734
- * with the method generator= in JSON.
735
- *
736
- */
737
-
738
- /* Explanation of the following: that's the only way to not pollute
739
- * standard library's docs with GeneratorMethods::<ClassName> which
740
- * are uninformative and take a large place in a list of classes
741
- */
742
-
743
- /*
744
- * Document-module: JSON::Ext::Generator::GeneratorMethods
745
- * :nodoc:
746
- */
747
-
748
- /*
749
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Array
750
- * :nodoc:
751
- */
752
-
753
- /*
754
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
755
- * :nodoc:
756
- */
757
-
758
- /*
759
- * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
760
- * :nodoc:
761
- */
762
-
763
- /*
764
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
765
- * :nodoc:
766
- */
767
-
768
- /*
769
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Float
770
- * :nodoc:
771
- */
772
-
773
- /*
774
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
775
- * :nodoc:
776
- */
777
-
778
- /*
779
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
780
- * :nodoc:
781
- */
782
-
783
- /*
784
- * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
785
- * :nodoc:
786
- */
787
-
788
- /*
789
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Object
790
- * :nodoc:
791
- */
792
-
793
- /*
794
- * Document-module: JSON::Ext::Generator::GeneratorMethods::String
795
- * :nodoc:
796
- */
797
-
798
- /*
799
- * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
800
- * :nodoc:
801
- */
802
-
803
- /*
804
- * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
805
- * :nodoc:
806
- */
807
-
808
- /*
809
- * call-seq: to_json(state = nil)
810
- *
811
- * Returns a JSON string containing a JSON object, that is generated from
812
- * this Hash instance.
813
- * _state_ is a JSON::State object, that can also be used to configure the
814
- * produced JSON string output further.
815
- */
816
- static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
817
- {
818
- rb_check_arity(argc, 0, 1);
819
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
820
- return cState_partial_generate(Vstate, self, generate_json_object, Qfalse);
821
- }
822
-
823
- /*
824
- * call-seq: to_json(state = nil)
825
- *
826
- * Returns a JSON string containing a JSON array, that is generated from
827
- * this Array instance.
828
- * _state_ is a JSON::State object, that can also be used to configure the
829
- * produced JSON string output further.
830
- */
831
- static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
832
- rb_check_arity(argc, 0, 1);
833
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
834
- return cState_partial_generate(Vstate, self, generate_json_array, Qfalse);
835
- }
836
-
837
- #ifdef RUBY_INTEGER_UNIFICATION
838
- /*
839
- * call-seq: to_json(*)
840
- *
841
- * Returns a JSON string representation for this Integer number.
842
- */
843
- static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
844
- {
845
- rb_check_arity(argc, 0, 1);
846
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
847
- return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse);
848
- }
849
-
850
- #else
851
- /*
852
- * call-seq: to_json(*)
853
- *
854
- * Returns a JSON string representation for this Integer number.
855
- */
856
- static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
857
- {
858
- rb_check_arity(argc, 0, 1);
859
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
860
- return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse);
861
- }
862
-
863
- /*
864
- * call-seq: to_json(*)
865
- *
866
- * Returns a JSON string representation for this Integer number.
867
- */
868
- static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
869
- {
870
- rb_check_arity(argc, 0, 1);
871
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
872
- return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse);
873
- }
874
- #endif
875
-
876
- /*
877
- * call-seq: to_json(*)
878
- *
879
- * Returns a JSON string representation for this Float number.
880
- */
881
- static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
882
- {
883
- rb_check_arity(argc, 0, 1);
884
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
885
- return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
886
- }
887
-
888
- /*
889
- * call-seq: String.included(modul)
890
- *
891
- * Extends _modul_ with the String::Extend module.
892
- */
893
- static VALUE mString_included_s(VALUE self, VALUE modul) {
894
- VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
895
- rb_call_super(1, &modul);
896
- return result;
897
- }
898
-
899
- /*
900
- * call-seq: to_json(*)
901
- *
902
- * This string should be encoded with UTF-8 A call to this method
903
- * returns a JSON string encoded with UTF16 big endian characters as
904
- * \u????.
905
- */
906
- static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
907
- {
908
- rb_check_arity(argc, 0, 1);
909
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
910
- return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
911
- }
912
-
913
- /*
914
- * call-seq: to_json_raw_object()
915
- *
916
- * This method creates a raw object hash, that can be nested into
917
- * other data structures and will be generated as a raw string. This
918
- * method should be used, if you want to convert raw strings to JSON
919
- * instead of UTF-8 strings, e. g. binary data.
920
- */
921
- static VALUE mString_to_json_raw_object(VALUE self)
922
- {
923
- VALUE ary;
924
- VALUE result = rb_hash_new();
925
- rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
926
- ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
927
- rb_hash_aset(result, rb_utf8_str_new_lit("raw"), ary);
928
- return result;
929
- }
930
-
931
- /*
932
- * call-seq: to_json_raw(*args)
933
- *
934
- * This method creates a JSON text from the result of a call to
935
- * to_json_raw_object of this String.
936
- */
937
- static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
938
- {
939
- VALUE obj = mString_to_json_raw_object(self);
940
- Check_Type(obj, T_HASH);
941
- return mHash_to_json(argc, argv, obj);
942
- }
943
-
944
- /*
945
- * call-seq: json_create(o)
946
- *
947
- * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
948
- * key "raw"). The Ruby String can be created by this module method.
949
- */
950
- static VALUE mString_Extend_json_create(VALUE self, VALUE o)
951
- {
952
- VALUE ary;
953
- Check_Type(o, T_HASH);
954
- ary = rb_hash_aref(o, rb_str_new2("raw"));
955
- return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
956
- }
957
-
958
- /*
959
- * call-seq: to_json(*)
960
- *
961
- * Returns a JSON string for true: 'true'.
962
- */
963
- static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
964
- {
965
- rb_check_arity(argc, 0, 1);
966
- return rb_utf8_str_new("true", 4);
967
- }
968
-
969
- /*
970
- * call-seq: to_json(*)
971
- *
972
- * Returns a JSON string for false: 'false'.
973
- */
974
- static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
975
- {
976
- rb_check_arity(argc, 0, 1);
977
- return rb_utf8_str_new("false", 5);
978
- }
979
-
980
- /*
981
- * call-seq: to_json(*)
982
- *
983
- * Returns a JSON string for nil: 'null'.
984
- */
985
- static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
986
- {
987
- rb_check_arity(argc, 0, 1);
988
- return rb_utf8_str_new("null", 4);
989
- }
990
-
991
- /*
992
- * call-seq: to_json(*)
993
- *
994
- * Converts this object to a string (calling #to_s), converts
995
- * it to a JSON string, and returns the result. This is a fallback, if no
996
- * special method #to_json was defined for some object.
997
- */
998
- static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
999
- {
1000
- VALUE state;
1001
- VALUE string = rb_funcall(self, i_to_s, 0);
1002
- rb_scan_args(argc, argv, "01", &state);
1003
- Check_Type(string, T_STRING);
1004
- state = cState_from_state_s(cState, state);
1005
- return cState_partial_generate(state, string, generate_json_string, Qfalse);
1006
- }
1007
-
1008
697
  static void State_mark(void *ptr)
1009
698
  {
1010
699
  JSON_Generator_State *state = ptr;
@@ -1014,6 +703,7 @@ static void State_mark(void *ptr)
1014
703
  rb_gc_mark_movable(state->object_nl);
1015
704
  rb_gc_mark_movable(state->array_nl);
1016
705
  rb_gc_mark_movable(state->as_json);
706
+ rb_gc_mark_movable(state->sort_keys);
1017
707
  }
1018
708
 
1019
709
  static void State_compact(void *ptr)
@@ -1025,34 +715,27 @@ static void State_compact(void *ptr)
1025
715
  state->object_nl = rb_gc_location(state->object_nl);
1026
716
  state->array_nl = rb_gc_location(state->array_nl);
1027
717
  state->as_json = rb_gc_location(state->as_json);
1028
- }
1029
-
1030
- static void State_free(void *ptr)
1031
- {
1032
- JSON_Generator_State *state = ptr;
1033
- ruby_xfree(state);
718
+ state->sort_keys = rb_gc_location(state->sort_keys);
1034
719
  }
1035
720
 
1036
721
  static size_t State_memsize(const void *ptr)
1037
722
  {
723
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
724
+ return 0;
725
+ #else
1038
726
  return sizeof(JSON_Generator_State);
1039
- }
1040
-
1041
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
1042
- # undef RUBY_TYPED_FROZEN_SHAREABLE
1043
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
1044
727
  #endif
728
+ }
1045
729
 
1046
730
  static const rb_data_type_t JSON_Generator_State_type = {
1047
- "JSON/Generator/State",
1048
- {
731
+ .wrap_struct_name = "JSON/Generator/State",
732
+ .function = {
1049
733
  .dmark = State_mark,
1050
- .dfree = State_free,
734
+ .dfree = RUBY_DEFAULT_FREE,
1051
735
  .dsize = State_memsize,
1052
736
  .dcompact = State_compact,
1053
737
  },
1054
- 0, 0,
1055
- RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
738
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
1056
739
  };
1057
740
 
1058
741
  static void state_init(JSON_Generator_State *state)
@@ -1082,20 +765,27 @@ static void vstate_spill(struct generate_json_data *data)
1082
765
  RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
1083
766
  RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
1084
767
  RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
768
+ RB_OBJ_WRITTEN(vstate, Qundef, state->sort_keys);
1085
769
  }
1086
770
 
1087
- static inline VALUE vstate_get(struct generate_json_data *data)
771
+ static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
1088
772
  {
1089
773
  if (RB_UNLIKELY(!data->vstate)) {
1090
774
  vstate_spill(data);
1091
775
  }
1092
- return data->vstate;
776
+ GET_STATE(data->vstate);
777
+ state->depth = data->depth;
778
+ VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
779
+ // no need to restore state->depth, vstate is just a temporary State
780
+ return tmp;
1093
781
  }
1094
782
 
1095
- struct hash_foreach_arg {
1096
- struct generate_json_data *data;
1097
- int iter;
1098
- };
783
+ static VALUE
784
+ json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
785
+ {
786
+ VALUE proc_args[2] = {object, is_key};
787
+ return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
788
+ }
1099
789
 
1100
790
  static VALUE
1101
791
  convert_string_subclass(VALUE key)
@@ -1112,6 +802,158 @@ convert_string_subclass(VALUE key)
1112
802
  return key_to_s;
1113
803
  }
1114
804
 
805
+ static bool enc_utf8_compatible_p(int enc_idx)
806
+ {
807
+ if (enc_idx == usascii_encindex) return true;
808
+ if (enc_idx == utf8_encindex) return true;
809
+ return false;
810
+ }
811
+
812
+ static VALUE encode_json_string_try(VALUE str)
813
+ {
814
+ return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
815
+ }
816
+
817
+ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
818
+ {
819
+ raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
820
+ return Qundef;
821
+ }
822
+
823
+ static inline int json_str_coderange(VALUE str) {
824
+ int coderange = RB_ENC_CODERANGE(str);
825
+ if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
826
+ coderange = rb_enc_str_coderange(str);
827
+ }
828
+ return coderange;
829
+ }
830
+
831
+ static inline bool valid_json_string_p(VALUE str)
832
+ {
833
+ int coderange = json_str_coderange(str);
834
+
835
+ if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
836
+ return true;
837
+ }
838
+
839
+ if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
840
+ return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
841
+ }
842
+
843
+ return false;
844
+ }
845
+
846
+ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
847
+ {
848
+ if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
849
+ VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
850
+ if (coerced_str != str) {
851
+ if (RB_TYPE_P(coerced_str, T_STRING)) {
852
+ if (!valid_json_string_p(coerced_str)) {
853
+ raise_generator_error(str, "source sequence is illegal/malformed utf-8");
854
+ }
855
+ } else {
856
+ // as_json could return another type than T_STRING
857
+ if (is_key) {
858
+ raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
859
+ }
860
+ }
861
+
862
+ return coerced_str;
863
+ }
864
+ }
865
+
866
+ if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
867
+ VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
868
+ switch (rb_enc_str_coderange(utf8_string)) {
869
+ case ENC_CODERANGE_7BIT:
870
+ return utf8_string;
871
+ case ENC_CODERANGE_VALID:
872
+ // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
873
+ // TODO: Raise in 3.0.0
874
+ rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
875
+ return utf8_string;
876
+ break;
877
+ }
878
+ }
879
+
880
+ return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
881
+ }
882
+
883
+ ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
884
+ {
885
+ if (RB_LIKELY(valid_json_string_p(str))) {
886
+ return str;
887
+ }
888
+ else {
889
+ return convert_invalid_encoding(data, str, as_json_called, is_key);
890
+ }
891
+ }
892
+
893
+ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
894
+ {
895
+ fbuffer_append_char(buffer, '"');
896
+
897
+ long len;
898
+ search_state search;
899
+ search.buffer = buffer;
900
+ RSTRING_GETMEM(obj, search.ptr, len);
901
+ search.cursor = search.ptr;
902
+ search.end = search.ptr + len;
903
+
904
+ #ifdef HAVE_SIMD
905
+ search.matches_mask = 0;
906
+ search.has_matches = false;
907
+ search.chunk_base = NULL;
908
+ search.chunk_end = NULL;
909
+ #endif /* HAVE_SIMD */
910
+
911
+ switch (json_str_coderange(obj)) {
912
+ case ENC_CODERANGE_7BIT:
913
+ case ENC_CODERANGE_VALID:
914
+ if (RB_UNLIKELY(data->state->ascii_only)) {
915
+ convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
916
+ } else if (RB_UNLIKELY(data->state->script_safe)) {
917
+ convert_UTF8_to_script_safe_JSON(&search);
918
+ } else {
919
+ convert_UTF8_to_JSON(&search);
920
+ }
921
+ break;
922
+ default:
923
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
924
+ break;
925
+ }
926
+ fbuffer_append_char(buffer, '"');
927
+ }
928
+
929
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
930
+ {
931
+ obj = ensure_valid_encoding(data, obj, false, false);
932
+ raw_generate_json_string(buffer, data, obj);
933
+ }
934
+
935
+ struct hash_foreach_arg {
936
+ VALUE hash;
937
+ struct generate_json_data *data;
938
+ int first_key_type;
939
+ bool first;
940
+ bool mixed_keys_encountered;
941
+ };
942
+
943
+ NOINLINE(static) void
944
+ json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
945
+ {
946
+ if (arg->mixed_keys_encountered) {
947
+ return;
948
+ }
949
+ arg->mixed_keys_encountered = true;
950
+
951
+ JSON_Generator_State *state = arg->data->state;
952
+ if (!state->allow_duplicate_key) {
953
+ rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 1, arg->hash);
954
+ }
955
+ }
956
+
1115
957
  static int
1116
958
  json_object_i(VALUE key, VALUE val, VALUE _arg)
1117
959
  {
@@ -1121,22 +963,34 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1121
963
  FBuffer *buffer = data->buffer;
1122
964
  JSON_Generator_State *state = data->state;
1123
965
 
1124
- long depth = state->depth;
1125
- int j;
966
+ long depth = data->depth;
967
+ int key_type = rb_type(key);
968
+
969
+ if (arg->first) {
970
+ arg->first = false;
971
+ arg->first_key_type = key_type;
972
+ }
973
+ else {
974
+ fbuffer_append_char(buffer, ',');
975
+ }
1126
976
 
1127
- if (arg->iter > 0) fbuffer_append_char(buffer, ',');
1128
977
  if (RB_UNLIKELY(data->state->object_nl)) {
1129
978
  fbuffer_append_str(buffer, data->state->object_nl);
1130
979
  }
1131
980
  if (RB_UNLIKELY(data->state->indent)) {
1132
- for (j = 0; j < depth; j++) {
1133
- fbuffer_append_str(buffer, data->state->indent);
1134
- }
981
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1135
982
  }
1136
983
 
1137
984
  VALUE key_to_s;
1138
- switch(rb_type(key)) {
985
+ bool as_json_called = false;
986
+
987
+ start:
988
+ switch (key_type) {
1139
989
  case T_STRING:
990
+ if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
991
+ json_inspect_hash_with_mixed_keys(arg);
992
+ }
993
+
1140
994
  if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
1141
995
  key_to_s = key;
1142
996
  } else {
@@ -1144,15 +998,31 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1144
998
  }
1145
999
  break;
1146
1000
  case T_SYMBOL:
1001
+ if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
1002
+ json_inspect_hash_with_mixed_keys(arg);
1003
+ }
1004
+
1147
1005
  key_to_s = rb_sym2str(key);
1148
1006
  break;
1149
1007
  default:
1008
+ if (data->state->strict) {
1009
+ if (RTEST(data->state->as_json) && !as_json_called) {
1010
+ key = json_call_as_json(data->state, key, Qtrue);
1011
+ key_type = rb_type(key);
1012
+ as_json_called = true;
1013
+ goto start;
1014
+ } else {
1015
+ raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
1016
+ }
1017
+ }
1150
1018
  key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
1151
1019
  break;
1152
1020
  }
1153
1021
 
1022
+ key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
1023
+
1154
1024
  if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
1155
- generate_json_string(buffer, data, key_to_s);
1025
+ raw_generate_json_string(buffer, data, key_to_s);
1156
1026
  } else {
1157
1027
  generate_json(buffer, data, key_to_s);
1158
1028
  }
@@ -1161,46 +1031,48 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1161
1031
  if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
1162
1032
  generate_json(buffer, data, val);
1163
1033
 
1164
- arg->iter++;
1165
1034
  return ST_CONTINUE;
1166
1035
  }
1167
1036
 
1168
1037
  static inline long increase_depth(struct generate_json_data *data)
1169
1038
  {
1170
1039
  JSON_Generator_State *state = data->state;
1171
- long depth = ++state->depth;
1040
+ long depth = ++data->depth;
1172
1041
  if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
1173
- rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
1042
+ rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
1174
1043
  }
1175
1044
  return depth;
1176
1045
  }
1177
1046
 
1178
1047
  static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1179
1048
  {
1180
- int j;
1049
+ if (RB_UNLIKELY(data->state->sort_keys)) {
1050
+ obj = rb_proc_call_with_block(data->state->sort_keys, 1, &obj, Qnil);
1051
+ Check_Type(obj, T_HASH);
1052
+ }
1053
+
1181
1054
  long depth = increase_depth(data);
1182
1055
 
1183
1056
  if (RHASH_SIZE(obj) == 0) {
1184
1057
  fbuffer_append(buffer, "{}", 2);
1185
- --data->state->depth;
1058
+ --data->depth;
1186
1059
  return;
1187
1060
  }
1188
1061
 
1189
1062
  fbuffer_append_char(buffer, '{');
1190
1063
 
1191
1064
  struct hash_foreach_arg arg = {
1065
+ .hash = obj,
1192
1066
  .data = data,
1193
- .iter = 0,
1067
+ .first = true,
1194
1068
  };
1195
1069
  rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
1196
1070
 
1197
- depth = --data->state->depth;
1071
+ depth = --data->depth;
1198
1072
  if (RB_UNLIKELY(data->state->object_nl)) {
1199
1073
  fbuffer_append_str(buffer, data->state->object_nl);
1200
1074
  if (RB_UNLIKELY(data->state->indent)) {
1201
- for (j = 0; j < depth; j++) {
1202
- fbuffer_append_str(buffer, data->state->indent);
1203
- }
1075
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1204
1076
  }
1205
1077
  }
1206
1078
  fbuffer_append_char(buffer, '}');
@@ -1208,125 +1080,41 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
1208
1080
 
1209
1081
  static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1210
1082
  {
1211
- int i, j;
1212
1083
  long depth = increase_depth(data);
1213
1084
 
1214
1085
  if (RARRAY_LEN(obj) == 0) {
1215
1086
  fbuffer_append(buffer, "[]", 2);
1216
- --data->state->depth;
1087
+ --data->depth;
1217
1088
  return;
1218
1089
  }
1219
1090
 
1220
1091
  fbuffer_append_char(buffer, '[');
1221
1092
  if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
1222
- for(i = 0; i < RARRAY_LEN(obj); i++) {
1093
+ for (int i = 0; i < RARRAY_LEN(obj); i++) {
1223
1094
  if (i > 0) {
1224
1095
  fbuffer_append_char(buffer, ',');
1225
1096
  if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
1226
1097
  }
1227
1098
  if (RB_UNLIKELY(data->state->indent)) {
1228
- for (j = 0; j < depth; j++) {
1229
- fbuffer_append_str(buffer, data->state->indent);
1230
- }
1099
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1231
1100
  }
1232
1101
  generate_json(buffer, data, RARRAY_AREF(obj, i));
1233
1102
  }
1234
- data->state->depth = --depth;
1103
+ data->depth = --depth;
1235
1104
  if (RB_UNLIKELY(data->state->array_nl)) {
1236
1105
  fbuffer_append_str(buffer, data->state->array_nl);
1237
1106
  if (RB_UNLIKELY(data->state->indent)) {
1238
- for (j = 0; j < depth; j++) {
1239
- fbuffer_append_str(buffer, data->state->indent);
1240
- }
1107
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1241
1108
  }
1242
1109
  }
1243
1110
  fbuffer_append_char(buffer, ']');
1244
1111
  }
1245
1112
 
1246
- static inline int enc_utf8_compatible_p(int enc_idx)
1247
- {
1248
- if (enc_idx == usascii_encindex) return 1;
1249
- if (enc_idx == utf8_encindex) return 1;
1250
- return 0;
1251
- }
1252
-
1253
- static VALUE encode_json_string_try(VALUE str)
1254
- {
1255
- return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
1256
- }
1257
-
1258
- static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
1259
- {
1260
- raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
1261
- return Qundef;
1262
- }
1263
-
1264
- static inline VALUE ensure_valid_encoding(VALUE str)
1265
- {
1266
- int encindex = RB_ENCODING_GET(str);
1267
- VALUE utf8_string;
1268
- if (RB_UNLIKELY(!enc_utf8_compatible_p(encindex))) {
1269
- if (encindex == binary_encindex) {
1270
- utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
1271
- switch (rb_enc_str_coderange(utf8_string)) {
1272
- case ENC_CODERANGE_7BIT:
1273
- return utf8_string;
1274
- case ENC_CODERANGE_VALID:
1275
- // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
1276
- // TODO: Raise in 3.0.0
1277
- rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
1278
- return utf8_string;
1279
- break;
1280
- }
1281
- }
1282
-
1283
- str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
1284
- }
1285
- return str;
1286
- }
1287
-
1288
- static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1289
- {
1290
- obj = ensure_valid_encoding(obj);
1291
-
1292
- fbuffer_append_char(buffer, '"');
1293
-
1294
- long len;
1295
- search_state search;
1296
- search.buffer = buffer;
1297
- RSTRING_GETMEM(obj, search.ptr, len);
1298
- search.cursor = search.ptr;
1299
- search.end = search.ptr + len;
1300
-
1301
- #ifdef HAVE_SIMD
1302
- search.matches_mask = 0;
1303
- search.has_matches = false;
1304
- search.chunk_base = NULL;
1305
- #endif /* HAVE_SIMD */
1306
-
1307
- switch(rb_enc_str_coderange(obj)) {
1308
- case ENC_CODERANGE_7BIT:
1309
- case ENC_CODERANGE_VALID:
1310
- if (RB_UNLIKELY(data->state->ascii_only)) {
1311
- convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
1312
- } else if (RB_UNLIKELY(data->state->script_safe)) {
1313
- convert_UTF8_to_script_safe_JSON(&search);
1314
- } else {
1315
- convert_UTF8_to_JSON(&search);
1316
- }
1317
- break;
1318
- default:
1319
- raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
1320
- break;
1321
- }
1322
- fbuffer_append_char(buffer, '"');
1323
- }
1324
-
1325
1113
  static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1326
1114
  {
1327
1115
  VALUE tmp;
1328
1116
  if (rb_respond_to(obj, i_to_json)) {
1329
- tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
1117
+ tmp = json_call_to_json(data, obj);
1330
1118
  Check_Type(tmp, T_STRING);
1331
1119
  fbuffer_append_str(buffer, tmp);
1332
1120
  } else {
@@ -1336,15 +1124,6 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d
1336
1124
  }
1337
1125
  }
1338
1126
 
1339
- static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1340
- {
1341
- if (data->state->strict) {
1342
- generate_json_string(buffer, data, rb_sym2str(obj));
1343
- } else {
1344
- generate_json_fallback(buffer, data, obj);
1345
- }
1346
- }
1347
-
1348
1127
  static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1349
1128
  {
1350
1129
  fbuffer_append(buffer, "null", 4);
@@ -1368,18 +1147,8 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
1368
1147
  static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1369
1148
  {
1370
1149
  VALUE tmp = rb_funcall(obj, i_to_s, 0);
1371
- fbuffer_append_str(buffer, tmp);
1372
- }
1373
-
1374
- #ifdef RUBY_INTEGER_UNIFICATION
1375
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1376
- {
1377
- if (FIXNUM_P(obj))
1378
- generate_json_fixnum(buffer, data, obj);
1379
- else
1380
- generate_json_bignum(buffer, data, obj);
1150
+ fbuffer_append_str(buffer, StringValue(tmp));
1381
1151
  }
1382
- #endif
1383
1152
 
1384
1153
  static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1385
1154
  {
@@ -1389,11 +1158,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
1389
1158
  /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
1390
1159
  if (!allow_nan) {
1391
1160
  if (data->state->strict && data->state->as_json) {
1392
- VALUE casted_obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1161
+ VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
1393
1162
  if (casted_obj != obj) {
1394
1163
  increase_depth(data);
1395
1164
  generate_json(buffer, data, casted_obj);
1396
- data->state->depth--;
1165
+ data->depth--;
1397
1166
  return;
1398
1167
  }
1399
1168
  }
@@ -1406,12 +1175,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
1406
1175
  }
1407
1176
 
1408
1177
  /* This implementation writes directly into the buffer. We reserve
1409
- * the 28 characters that fpconv_dtoa states as its maximum.
1178
+ * the 32 characters that fpconv_dtoa states as its maximum.
1410
1179
  */
1411
- fbuffer_inc_capa(buffer, 28);
1180
+ fbuffer_inc_capa(buffer, 32);
1412
1181
  char* d = buffer->ptr + buffer->len;
1413
1182
  int len = fpconv_dtoa(value, d);
1414
-
1415
1183
  /* fpconv_dtoa converts a float to its shortest string representation,
1416
1184
  * but it adds a ".0" if this is a plain integer.
1417
1185
  */
@@ -1425,7 +1193,7 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
1425
1193
  fbuffer_append_str(buffer, fragment);
1426
1194
  }
1427
1195
 
1428
- static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1196
+ static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
1429
1197
  {
1430
1198
  bool as_json_called = false;
1431
1199
  start:
@@ -1441,7 +1209,13 @@ start:
1441
1209
  } else if (RB_FLONUM_P(obj)) {
1442
1210
  generate_json_float(buffer, data, obj);
1443
1211
  } else if (RB_STATIC_SYM_P(obj)) {
1444
- generate_json_symbol(buffer, data, obj);
1212
+ if (data->state->strict) {
1213
+ obj = rb_sym2str(obj);
1214
+ JSON_ASSERT(RBASIC_CLASS(obj) == rb_cString);
1215
+ goto generate_string;
1216
+ }
1217
+
1218
+ generate_json_fallback(buffer, data, obj);
1445
1219
  } else {
1446
1220
  goto general;
1447
1221
  }
@@ -1452,22 +1226,38 @@ start:
1452
1226
  generate_json_bignum(buffer, data, obj);
1453
1227
  break;
1454
1228
  case T_HASH:
1455
- if (klass != rb_cHash) goto general;
1229
+ if (fallback && klass != rb_cHash) goto general;
1456
1230
  generate_json_object(buffer, data, obj);
1457
1231
  break;
1458
1232
  case T_ARRAY:
1459
- if (klass != rb_cArray) goto general;
1233
+ if (fallback && klass != rb_cArray) goto general;
1460
1234
  generate_json_array(buffer, data, obj);
1461
1235
  break;
1462
1236
  case T_STRING:
1463
- if (klass != rb_cString) goto general;
1464
- generate_json_string(buffer, data, obj);
1237
+ if (fallback && klass != rb_cString) goto general;
1238
+
1239
+ generate_string:
1240
+ if (RB_LIKELY(valid_json_string_p(obj))) {
1241
+ raw_generate_json_string(buffer, data, obj);
1242
+ } else if (as_json_called) {
1243
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
1244
+ } else {
1245
+ obj = ensure_valid_encoding(data, obj, false, false);
1246
+ as_json_called = true;
1247
+ goto start;
1248
+ }
1465
1249
  break;
1466
1250
  case T_SYMBOL:
1467
- generate_json_symbol(buffer, data, obj);
1251
+ if (data->state->strict) {
1252
+ obj = rb_sym2str(obj);
1253
+ JSON_ASSERT(RBASIC_CLASS(obj) == rb_cString);
1254
+ goto generate_string;
1255
+ }
1256
+
1257
+ generate_json_fallback(buffer, data, obj);
1468
1258
  break;
1469
1259
  case T_FLOAT:
1470
- if (klass != rb_cFloat) goto general;
1260
+ if (fallback && klass != rb_cFloat) goto general;
1471
1261
  generate_json_float(buffer, data, obj);
1472
1262
  break;
1473
1263
  case T_STRUCT:
@@ -1478,7 +1268,7 @@ start:
1478
1268
  general:
1479
1269
  if (data->state->strict) {
1480
1270
  if (RTEST(data->state->as_json) && !as_json_called) {
1481
- obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1271
+ obj = json_call_as_json(data->state, obj, Qfalse);
1482
1272
  as_json_called = true;
1483
1273
  goto start;
1484
1274
  } else {
@@ -1491,45 +1281,50 @@ start:
1491
1281
  }
1492
1282
  }
1493
1283
 
1284
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1285
+ {
1286
+ generate_json_general(buffer, data, obj, true);
1287
+ }
1288
+
1289
+ static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1290
+ {
1291
+ generate_json_general(buffer, data, obj, false);
1292
+ }
1293
+
1494
1294
  static VALUE generate_json_try(VALUE d)
1495
1295
  {
1496
1296
  struct generate_json_data *data = (struct generate_json_data *)d;
1497
1297
 
1498
1298
  data->func(data->buffer, data, data->obj);
1499
1299
 
1500
- return Qnil;
1300
+ return fbuffer_finalize(data->buffer);
1501
1301
  }
1502
1302
 
1503
- static VALUE generate_json_rescue(VALUE d, VALUE exc)
1303
+ static VALUE generate_json_ensure(VALUE d)
1504
1304
  {
1505
1305
  struct generate_json_data *data = (struct generate_json_data *)d;
1506
1306
  fbuffer_free(data->buffer);
1507
1307
 
1508
- rb_exc_raise(exc);
1509
-
1510
1308
  return Qundef;
1511
1309
  }
1512
1310
 
1513
- static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
1311
+ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
1514
1312
  {
1515
1313
  GET_STATE(self);
1516
1314
 
1517
1315
  char stack_buffer[FBUFFER_STACK_SIZE];
1518
- FBuffer buffer = {
1519
- .io = RTEST(io) ? io : Qfalse,
1520
- };
1521
- fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1316
+ FBuffer buffer = { 0 };
1317
+ fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1522
1318
 
1523
1319
  struct generate_json_data data = {
1524
1320
  .buffer = &buffer,
1525
- .vstate = self,
1321
+ .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
1526
1322
  .state = state,
1323
+ .depth = state->depth,
1527
1324
  .obj = obj,
1528
1325
  .func = func
1529
1326
  };
1530
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1531
-
1532
- return fbuffer_finalize(&buffer);
1327
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1533
1328
  }
1534
1329
 
1535
1330
  /* call-seq:
@@ -1545,10 +1340,16 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
1545
1340
  rb_check_arity(argc, 1, 2);
1546
1341
  VALUE obj = argv[0];
1547
1342
  VALUE io = argc > 1 ? argv[1] : Qnil;
1548
- VALUE result = cState_partial_generate(self, obj, generate_json, io);
1549
- GET_STATE(self);
1550
- (void)state;
1551
- return result;
1343
+ return cState_partial_generate(self, obj, generate_json, io);
1344
+ }
1345
+
1346
+ /* :nodoc: */
1347
+ static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
1348
+ {
1349
+ rb_check_arity(argc, 1, 2);
1350
+ VALUE obj = argv[0];
1351
+ VALUE io = argc > 1 ? argv[1] : Qnil;
1352
+ return cState_partial_generate(self, obj, generate_json_no_fallback, io);
1552
1353
  }
1553
1354
 
1554
1355
  static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
@@ -1573,12 +1374,15 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
1573
1374
  if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
1574
1375
 
1575
1376
  MEMCPY(objState, origState, JSON_Generator_State, 1);
1576
- objState->indent = origState->indent;
1577
- objState->space = origState->space;
1578
- objState->space_before = origState->space_before;
1579
- objState->object_nl = origState->object_nl;
1580
- objState->array_nl = origState->array_nl;
1581
- objState->as_json = origState->as_json;
1377
+
1378
+ RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
1379
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space);
1380
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
1381
+ RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
1382
+ RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
1383
+ RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
1384
+ RB_OBJ_WRITTEN(obj, Qundef, objState->sort_keys);
1385
+
1582
1386
  return obj;
1583
1387
  }
1584
1388
 
@@ -1629,6 +1433,7 @@ static VALUE string_config(VALUE config)
1629
1433
  */
1630
1434
  static VALUE cState_indent_set(VALUE self, VALUE indent)
1631
1435
  {
1436
+ rb_check_frozen(self);
1632
1437
  GET_STATE(self);
1633
1438
  RB_OBJ_WRITE(self, &state->indent, string_config(indent));
1634
1439
  return Qnil;
@@ -1654,6 +1459,7 @@ static VALUE cState_space(VALUE self)
1654
1459
  */
1655
1460
  static VALUE cState_space_set(VALUE self, VALUE space)
1656
1461
  {
1462
+ rb_check_frozen(self);
1657
1463
  GET_STATE(self);
1658
1464
  RB_OBJ_WRITE(self, &state->space, string_config(space));
1659
1465
  return Qnil;
@@ -1677,6 +1483,7 @@ static VALUE cState_space_before(VALUE self)
1677
1483
  */
1678
1484
  static VALUE cState_space_before_set(VALUE self, VALUE space_before)
1679
1485
  {
1486
+ rb_check_frozen(self);
1680
1487
  GET_STATE(self);
1681
1488
  RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
1682
1489
  return Qnil;
@@ -1702,6 +1509,7 @@ static VALUE cState_object_nl(VALUE self)
1702
1509
  */
1703
1510
  static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
1704
1511
  {
1512
+ rb_check_frozen(self);
1705
1513
  GET_STATE(self);
1706
1514
  RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
1707
1515
  return Qnil;
@@ -1725,6 +1533,7 @@ static VALUE cState_array_nl(VALUE self)
1725
1533
  */
1726
1534
  static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
1727
1535
  {
1536
+ rb_check_frozen(self);
1728
1537
  GET_STATE(self);
1729
1538
  RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
1730
1539
  return Qnil;
@@ -1748,6 +1557,7 @@ static VALUE cState_as_json(VALUE self)
1748
1557
  */
1749
1558
  static VALUE cState_as_json_set(VALUE self, VALUE as_json)
1750
1559
  {
1560
+ rb_check_frozen(self);
1751
1561
  GET_STATE(self);
1752
1562
  RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
1753
1563
  return Qnil;
@@ -1779,7 +1589,21 @@ static VALUE cState_max_nesting(VALUE self)
1779
1589
 
1780
1590
  static long long_config(VALUE num)
1781
1591
  {
1782
- return RTEST(num) ? FIX2LONG(num) : 0;
1592
+ return RTEST(num) ? NUM2LONG(num) : 0;
1593
+ }
1594
+
1595
+ // depth must never be negative; reject early with a clear error.
1596
+ static long depth_config(VALUE num)
1597
+ {
1598
+ if (!RTEST(num)) return 0;
1599
+ long d = NUM2LONG(num);
1600
+ if (RB_UNLIKELY(d < 0)) {
1601
+ rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
1602
+ }
1603
+ if (RB_UNLIKELY(d > INT_MAX)) {
1604
+ rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
1605
+ }
1606
+ return d;
1783
1607
  }
1784
1608
 
1785
1609
  /*
@@ -1790,6 +1614,7 @@ static long long_config(VALUE num)
1790
1614
  */
1791
1615
  static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
1792
1616
  {
1617
+ rb_check_frozen(self);
1793
1618
  GET_STATE(self);
1794
1619
  state->max_nesting = long_config(depth);
1795
1620
  return Qnil;
@@ -1815,6 +1640,7 @@ static VALUE cState_script_safe(VALUE self)
1815
1640
  */
1816
1641
  static VALUE cState_script_safe_set(VALUE self, VALUE enable)
1817
1642
  {
1643
+ rb_check_frozen(self);
1818
1644
  GET_STATE(self);
1819
1645
  state->script_safe = RTEST(enable);
1820
1646
  return Qnil;
@@ -1846,6 +1672,7 @@ static VALUE cState_strict(VALUE self)
1846
1672
  */
1847
1673
  static VALUE cState_strict_set(VALUE self, VALUE enable)
1848
1674
  {
1675
+ rb_check_frozen(self);
1849
1676
  GET_STATE(self);
1850
1677
  state->strict = RTEST(enable);
1851
1678
  return Qnil;
@@ -1870,6 +1697,7 @@ static VALUE cState_allow_nan_p(VALUE self)
1870
1697
  */
1871
1698
  static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
1872
1699
  {
1700
+ rb_check_frozen(self);
1873
1701
  GET_STATE(self);
1874
1702
  state->allow_nan = RTEST(enable);
1875
1703
  return Qnil;
@@ -1894,11 +1722,67 @@ static VALUE cState_ascii_only_p(VALUE self)
1894
1722
  */
1895
1723
  static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
1896
1724
  {
1725
+ rb_check_frozen(self);
1897
1726
  GET_STATE(self);
1898
1727
  state->ascii_only = RTEST(enable);
1899
1728
  return Qnil;
1900
1729
  }
1901
1730
 
1731
+ static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc)
1732
+ {
1733
+ if (!rb_obj_is_proc(proc)) {
1734
+ rb_raise(rb_eTypeError, "sort_key_proc must be a Proc");
1735
+ }
1736
+ return default_sort_keys_proc = proc;
1737
+ }
1738
+
1739
+ static VALUE normalize_sort_keys(VALUE value)
1740
+ {
1741
+ if (rb_obj_is_proc(value)) {
1742
+ return value;
1743
+ } else if (value == Qtrue) {
1744
+ return default_sort_keys_proc;
1745
+ } else if (RTEST(value)) {
1746
+ rb_raise(rb_eTypeError, "The `sort_keys` argument must be a boolean or a Proc");
1747
+ } else {
1748
+ return Qfalse;
1749
+ }
1750
+ }
1751
+
1752
+ /*
1753
+ * call-seq: sort_keys
1754
+ *
1755
+ * Get the value of sort_keys.
1756
+ */
1757
+ static VALUE cState_sort_keys_p(VALUE self)
1758
+ {
1759
+ GET_STATE(self);
1760
+ return state->sort_keys;
1761
+ }
1762
+
1763
+ /*
1764
+ * call-seq: sort_keys=(value)
1765
+ *
1766
+ * value is a boolean or a proc. If the value is the boolean true, object keys
1767
+ * will be sorted lexicographically in ascending order.
1768
+ *
1769
+ * If the value is a proc, it receives the entire Hash and must return a Hash
1770
+ * with its pairs in the desired order, allowing for arbitrary sorting.
1771
+ */
1772
+ static VALUE cState_sort_keys_set(VALUE self, VALUE value)
1773
+ {
1774
+ rb_check_frozen(self);
1775
+ GET_STATE(self);
1776
+ RB_OBJ_WRITE(self, &state->sort_keys, normalize_sort_keys(value));
1777
+ return Qnil;
1778
+ }
1779
+
1780
+ static VALUE cState_allow_duplicate_key_p(VALUE self)
1781
+ {
1782
+ GET_STATE(self);
1783
+ return state->allow_duplicate_key ? Qtrue : Qfalse;
1784
+ }
1785
+
1902
1786
  /*
1903
1787
  * call-seq: depth
1904
1788
  *
@@ -1918,8 +1802,9 @@ static VALUE cState_depth(VALUE self)
1918
1802
  */
1919
1803
  static VALUE cState_depth_set(VALUE self, VALUE depth)
1920
1804
  {
1805
+ rb_check_frozen(self);
1921
1806
  GET_STATE(self);
1922
- state->depth = long_config(depth);
1807
+ state->depth = depth_config(depth);
1923
1808
  return Qnil;
1924
1809
  }
1925
1810
 
@@ -1951,33 +1836,63 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
1951
1836
  */
1952
1837
  static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
1953
1838
  {
1839
+ rb_check_frozen(self);
1954
1840
  GET_STATE(self);
1955
1841
  buffer_initial_length_set(state, buffer_initial_length);
1956
1842
  return Qnil;
1957
1843
  }
1958
1844
 
1845
+ struct configure_state_data {
1846
+ JSON_Generator_State *state;
1847
+ VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated
1848
+ VALUE unknown_keywords;
1849
+ };
1850
+
1851
+ static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value)
1852
+ {
1853
+ if (RTEST(data->vstate)) {
1854
+ RB_OBJ_WRITE(data->vstate, field, value);
1855
+ } else {
1856
+ *field = value;
1857
+ }
1858
+ }
1859
+
1959
1860
  static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1960
1861
  {
1961
- JSON_Generator_State *state = (JSON_Generator_State *)_arg;
1862
+ struct configure_state_data *data = (struct configure_state_data *)_arg;
1863
+ JSON_Generator_State *state = data->state;
1962
1864
 
1963
- if (key == sym_indent) { state->indent = string_config(val); }
1964
- else if (key == sym_space) { state->space = string_config(val); }
1965
- else if (key == sym_space_before) { state->space_before = string_config(val); }
1966
- else if (key == sym_object_nl) { state->object_nl = string_config(val); }
1967
- else if (key == sym_array_nl) { state->array_nl = string_config(val); }
1865
+ if (key == sym_indent) { state_write_value(data, &state->indent, string_config(val)); }
1866
+ else if (key == sym_space) { state_write_value(data, &state->space, string_config(val)); }
1867
+ else if (key == sym_space_before) { state_write_value(data, &state->space_before, string_config(val)); }
1868
+ else if (key == sym_object_nl) { state_write_value(data, &state->object_nl, string_config(val)); }
1869
+ else if (key == sym_array_nl) { state_write_value(data, &state->array_nl, string_config(val)); }
1968
1870
  else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
1969
1871
  else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
1970
1872
  else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
1971
- else if (key == sym_depth) { state->depth = long_config(val); }
1873
+ else if (key == sym_depth) { state->depth = depth_config(val); }
1972
1874
  else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
1973
1875
  else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
1974
- else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
1975
1876
  else if (key == sym_strict) { state->strict = RTEST(val); }
1976
- else if (key == sym_as_json) { state->as_json = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse; }
1877
+ else if (key == sym_allow_duplicate_key) { state->allow_duplicate_key = RTEST(val); }
1878
+ else if (key == sym_as_json) {
1879
+ VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
1880
+ state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
1881
+ state_write_value(data, &state->as_json, proc);
1882
+ }
1883
+ else if (key == sym_sort_keys) {
1884
+ state_write_value(data, &state->sort_keys, normalize_sort_keys(val));
1885
+ }
1886
+ else {
1887
+ if (!data->unknown_keywords) {
1888
+ data->unknown_keywords = rb_obj_hide(rb_ary_new());
1889
+ }
1890
+ rb_ary_push(data->unknown_keywords, key);
1891
+ }
1977
1892
  return ST_CONTINUE;
1978
1893
  }
1979
1894
 
1980
- static void configure_state(JSON_Generator_State *state, VALUE config)
1895
+ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE config)
1981
1896
  {
1982
1897
  if (!RTEST(config)) return;
1983
1898
 
@@ -1985,45 +1900,58 @@ static void configure_state(JSON_Generator_State *state, VALUE config)
1985
1900
 
1986
1901
  if (!RHASH_SIZE(config)) return;
1987
1902
 
1903
+ struct configure_state_data data = {
1904
+ .state = state,
1905
+ .vstate = vstate,
1906
+ .unknown_keywords = Qfalse,
1907
+ };
1908
+
1988
1909
  // We assume in most cases few keys are set so it's faster to go over
1989
1910
  // the provided keys than to check all possible keys.
1990
- rb_hash_foreach(config, configure_state_i, (VALUE)state);
1911
+ rb_hash_foreach(config, configure_state_i, (VALUE)&data);
1912
+
1913
+ raise_argument_error_on_unknown_keywords(data.unknown_keywords);
1991
1914
  }
1992
1915
 
1993
1916
  static VALUE cState_configure(VALUE self, VALUE opts)
1994
1917
  {
1918
+ rb_check_frozen(self);
1995
1919
  GET_STATE(self);
1996
- configure_state(state, opts);
1920
+ configure_state(state, self, opts);
1997
1921
  return self;
1998
1922
  }
1999
1923
 
2000
- static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1924
+ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
2001
1925
  {
2002
1926
  JSON_Generator_State state = {0};
2003
1927
  state_init(&state);
2004
- configure_state(&state, opts);
1928
+ configure_state(&state, Qfalse, opts);
2005
1929
 
2006
1930
  char stack_buffer[FBUFFER_STACK_SIZE];
2007
- FBuffer buffer = {
2008
- .io = RTEST(io) ? io : Qfalse,
2009
- };
2010
- fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1931
+ FBuffer buffer = { 0 };
1932
+ fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
2011
1933
 
2012
1934
  struct generate_json_data data = {
2013
1935
  .buffer = &buffer,
2014
1936
  .vstate = Qfalse,
2015
1937
  .state = &state,
1938
+ .depth = state.depth,
2016
1939
  .obj = obj,
2017
- .func = generate_json,
1940
+ .func = func,
2018
1941
  };
2019
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1942
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1943
+ }
2020
1944
 
2021
- return fbuffer_finalize(&buffer);
1945
+ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1946
+ {
1947
+ return cState_m_do_generate(klass, obj, opts, io, generate_json);
1948
+ }
1949
+
1950
+ static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1951
+ {
1952
+ return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
2022
1953
  }
2023
1954
 
2024
- /*
2025
- *
2026
- */
2027
1955
  void Init_generator(void)
2028
1956
  {
2029
1957
  #ifdef HAVE_RB_EXT_RACTOR_SAFE
@@ -2041,6 +1969,8 @@ void Init_generator(void)
2041
1969
  VALUE mExt = rb_define_module_under(mJSON, "Ext");
2042
1970
  VALUE mGenerator = rb_define_module_under(mExt, "Generator");
2043
1971
 
1972
+ rb_global_variable(&default_sort_keys_proc);
1973
+
2044
1974
  rb_global_variable(&eGeneratorError);
2045
1975
  eGeneratorError = rb_path2class("JSON::GeneratorError");
2046
1976
 
@@ -2050,6 +1980,8 @@ void Init_generator(void)
2050
1980
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
2051
1981
  rb_define_alloc_func(cState, cState_s_allocate);
2052
1982
  rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
1983
+ rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1);
1984
+
2053
1985
  rb_define_method(cState, "initialize", cState_initialize, -1);
2054
1986
  rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
2055
1987
  rb_define_private_method(cState, "_configure", cState_configure, 1);
@@ -2072,9 +2004,6 @@ void Init_generator(void)
2072
2004
  rb_define_method(cState, "script_safe", cState_script_safe, 0);
2073
2005
  rb_define_method(cState, "script_safe?", cState_script_safe, 0);
2074
2006
  rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
2075
- rb_define_alias(cState, "escape_slash", "script_safe");
2076
- rb_define_alias(cState, "escape_slash?", "script_safe?");
2077
- rb_define_alias(cState, "escape_slash=", "script_safe=");
2078
2007
  rb_define_method(cState, "strict", cState_strict, 0);
2079
2008
  rb_define_method(cState, "strict?", cState_strict, 0);
2080
2009
  rb_define_method(cState, "strict=", cState_strict_set, 1);
@@ -2088,51 +2017,14 @@ void Init_generator(void)
2088
2017
  rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
2089
2018
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
2090
2019
  rb_define_method(cState, "generate", cState_generate, -1);
2091
- rb_define_alias(cState, "generate_new", "generate"); // :nodoc:
2092
-
2093
- rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
2094
-
2095
- VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
2096
-
2097
- VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object");
2098
- rb_define_method(mObject, "to_json", mObject_to_json, -1);
2099
-
2100
- VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash");
2101
- rb_define_method(mHash, "to_json", mHash_to_json, -1);
2102
-
2103
- VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array");
2104
- rb_define_method(mArray, "to_json", mArray_to_json, -1);
2105
-
2106
- #ifdef RUBY_INTEGER_UNIFICATION
2107
- VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
2108
- rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
2109
- #else
2110
- VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
2111
- rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
2112
-
2113
- VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
2114
- rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
2115
- #endif
2116
- VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float");
2117
- rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
2118
-
2119
- VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
2120
- rb_define_singleton_method(mString, "included", mString_included_s, 1);
2121
- rb_define_method(mString, "to_json", mString_to_json, -1);
2122
- rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
2123
- rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
2020
+ rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
2021
+ rb_define_method(cState, "sort_keys", cState_sort_keys_p, 0);
2022
+ rb_define_method(cState, "sort_keys=", cState_sort_keys_set, 1);
2124
2023
 
2125
- mString_Extend = rb_define_module_under(mString, "Extend");
2126
- rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
2024
+ rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
2127
2025
 
2128
- VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
2129
- rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
2130
-
2131
- VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
2132
- rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
2133
-
2134
- VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
2135
- rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
2026
+ rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
2027
+ rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
2136
2028
 
2137
2029
  rb_global_variable(&Encoding_UTF_8);
2138
2030
  Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
@@ -2140,10 +2032,6 @@ void Init_generator(void)
2140
2032
  i_to_s = rb_intern("to_s");
2141
2033
  i_to_json = rb_intern("to_json");
2142
2034
  i_new = rb_intern("new");
2143
- i_pack = rb_intern("pack");
2144
- i_unpack = rb_intern("unpack");
2145
- i_create_id = rb_intern("create_id");
2146
- i_extend = rb_intern("extend");
2147
2035
  i_encode = rb_intern("encode");
2148
2036
 
2149
2037
  sym_indent = ID2SYM(rb_intern("indent"));
@@ -2157,9 +2045,10 @@ void Init_generator(void)
2157
2045
  sym_depth = ID2SYM(rb_intern("depth"));
2158
2046
  sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length"));
2159
2047
  sym_script_safe = ID2SYM(rb_intern("script_safe"));
2160
- sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
2161
2048
  sym_strict = ID2SYM(rb_intern("strict"));
2162
2049
  sym_as_json = ID2SYM(rb_intern("as_json"));
2050
+ sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
2051
+ sym_sort_keys = ID2SYM(rb_intern("sort_keys"));
2163
2052
 
2164
2053
  usascii_encindex = rb_usascii_encindex();
2165
2054
  utf8_encindex = rb_utf8_encindex();
@@ -2167,22 +2056,5 @@ void Init_generator(void)
2167
2056
 
2168
2057
  rb_require("json/ext/generator/state");
2169
2058
 
2170
-
2171
- switch(find_simd_implementation()) {
2172
- #ifdef HAVE_SIMD
2173
- #ifdef HAVE_SIMD_NEON
2174
- case SIMD_NEON:
2175
- search_escape_basic_impl = search_escape_basic_neon;
2176
- break;
2177
- #endif /* HAVE_SIMD_NEON */
2178
- #ifdef HAVE_SIMD_SSE2
2179
- case SIMD_SSE2:
2180
- search_escape_basic_impl = search_escape_basic_sse2;
2181
- break;
2182
- #endif /* HAVE_SIMD_SSE2 */
2183
- #endif /* HAVE_SIMD */
2184
- default:
2185
- search_escape_basic_impl = search_escape_basic;
2186
- break;
2187
- }
2059
+ simd_impl = find_simd_implementation();
2188
2060
  }