json 2.13.1 → 2.19.2

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,4 +1,4 @@
1
- #include "ruby.h"
1
+ #include "../json.h"
2
2
  #include "../fbuffer/fbuffer.h"
3
3
  #include "../vendor/fpconv.c"
4
4
 
@@ -9,6 +9,12 @@
9
9
 
10
10
  /* ruby api and some helpers */
11
11
 
12
+ enum duplicate_key_action {
13
+ JSON_DEPRECATED = 0,
14
+ JSON_IGNORE,
15
+ JSON_RAISE,
16
+ };
17
+
12
18
  typedef struct JSON_Generator_StateStruct {
13
19
  VALUE indent;
14
20
  VALUE space;
@@ -21,20 +27,19 @@ typedef struct JSON_Generator_StateStruct {
21
27
  long depth;
22
28
  long buffer_initial_length;
23
29
 
30
+ enum duplicate_key_action on_duplicate_key;
31
+
32
+ bool as_json_single_arg;
24
33
  bool allow_nan;
25
34
  bool ascii_only;
26
35
  bool script_safe;
27
36
  bool strict;
28
37
  } JSON_Generator_State;
29
38
 
30
- #ifndef RB_UNLIKELY
31
- #define RB_UNLIKELY(cond) (cond)
32
- #endif
39
+ static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
33
40
 
34
- static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
35
-
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,
41
+ static ID i_to_s, i_to_json, i_new, i_encode;
42
+ 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,
38
43
  sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
39
44
 
40
45
 
@@ -55,8 +60,11 @@ struct generate_json_data {
55
60
  JSON_Generator_State *state;
56
61
  VALUE obj;
57
62
  generator_func func;
63
+ long depth;
58
64
  };
59
65
 
66
+ static SIMD_Implementation simd_impl;
67
+
60
68
  static VALUE cState_from_state_s(VALUE self, VALUE opts);
61
69
  static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
62
70
  static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
@@ -66,9 +74,6 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
66
74
  static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
67
75
  static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
68
76
  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
77
  static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
73
78
  static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
74
79
  static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
@@ -76,23 +81,18 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
76
81
 
77
82
  static int usascii_encindex, utf8_encindex, binary_encindex;
78
83
 
79
- #ifdef RBIMPL_ATTR_NORETURN
80
- RBIMPL_ATTR_NORETURN()
81
- #endif
82
- static void raise_generator_error_str(VALUE invalid_object, VALUE str)
84
+ NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
83
85
  {
86
+ rb_enc_associate_index(str, utf8_encindex);
84
87
  VALUE exc = rb_exc_new_str(eGeneratorError, str);
85
88
  rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
86
89
  rb_exc_raise(exc);
87
90
  }
88
91
 
89
- #ifdef RBIMPL_ATTR_NORETURN
90
- RBIMPL_ATTR_NORETURN()
91
- #endif
92
92
  #ifdef RBIMPL_ATTR_FORMAT
93
93
  RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
94
94
  #endif
95
- static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
95
+ NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
96
96
  {
97
97
  va_list args;
98
98
  va_start(args, fmt);
@@ -127,18 +127,12 @@ typedef struct _search_state {
127
127
  #endif /* HAVE_SIMD */
128
128
  } search_state;
129
129
 
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)
130
+ ALWAYS_INLINE(static) void search_flush(search_state *search)
137
131
  {
138
132
  // Do not remove this conditional without profiling, specifically escape-heavy text.
139
133
  // 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
134
+ // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
135
+ // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
142
136
  // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
143
137
  // nothing needs to be flushed, we can save a few memory references with this conditional.
144
138
  if (search->ptr > search->cursor) {
@@ -160,8 +154,6 @@ static const unsigned char escape_table_basic[256] = {
160
154
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161
155
  };
162
156
 
163
- static unsigned char (*search_escape_basic_impl)(search_state *);
164
-
165
157
  static inline unsigned char search_escape_basic(search_state *search)
166
158
  {
167
159
  while (search->ptr < search->end) {
@@ -176,7 +168,7 @@ static inline unsigned char search_escape_basic(search_state *search)
176
168
  return 0;
177
169
  }
178
170
 
179
- static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
171
+ ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
180
172
  {
181
173
  const unsigned char ch = (unsigned char)*search->ptr;
182
174
  switch (ch) {
@@ -217,11 +209,39 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
217
209
  * Everything else (should be UTF-8) is just passed through and
218
210
  * appended to the result.
219
211
  */
212
+
213
+
214
+ #if defined(HAVE_SIMD_NEON)
215
+ static inline unsigned char search_escape_basic_neon(search_state *search);
216
+ #elif defined(HAVE_SIMD_SSE2)
217
+ static inline unsigned char search_escape_basic_sse2(search_state *search);
218
+ #endif
219
+
220
+ static inline unsigned char search_escape_basic(search_state *search);
221
+
220
222
  static inline void convert_UTF8_to_JSON(search_state *search)
221
223
  {
222
- while (search_escape_basic_impl(search)) {
224
+ #ifdef HAVE_SIMD
225
+ #if defined(HAVE_SIMD_NEON)
226
+ while (search_escape_basic_neon(search)) {
223
227
  escape_UTF8_char_basic(search);
224
228
  }
229
+ #elif defined(HAVE_SIMD_SSE2)
230
+ if (simd_impl == SIMD_SSE2) {
231
+ while (search_escape_basic_sse2(search)) {
232
+ escape_UTF8_char_basic(search);
233
+ }
234
+ return;
235
+ }
236
+ while (search_escape_basic(search)) {
237
+ escape_UTF8_char_basic(search);
238
+ }
239
+ #endif
240
+ #else
241
+ while (search_escape_basic(search)) {
242
+ escape_UTF8_char_basic(search);
243
+ }
244
+ #endif /* HAVE_SIMD */
225
245
  }
226
246
 
227
247
  static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
@@ -263,8 +283,10 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
263
283
 
264
284
  #ifdef HAVE_SIMD
265
285
 
266
- static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
286
+ ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
267
287
  {
288
+ RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
289
+
268
290
  // Flush the buffer so everything up until the last 'len' characters are unflushed.
269
291
  search_flush(search);
270
292
 
@@ -274,19 +296,25 @@ static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsi
274
296
  char *s = (buf->ptr + buf->len);
275
297
 
276
298
  // 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);
299
+ // This seem wasteful at first sight, but memset of vector length is very fast.
300
+ // This is a space as it can be directly represented as an immediate on AArch64.
301
+ memset(s, ' ', vec_len);
279
302
 
280
303
  // Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
281
304
  // to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
282
- MEMCPY(s, search->ptr, char, len);
305
+ if (vec_len == 16) {
306
+ RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
307
+ json_fast_memcpy16(s, search->ptr, len);
308
+ } else {
309
+ MEMCPY(s, search->ptr, char, len);
310
+ }
283
311
 
284
312
  return s;
285
313
  }
286
314
 
287
315
  #ifdef HAVE_SIMD_NEON
288
316
 
289
- static inline FORCE_INLINE unsigned char neon_next_match(search_state *search)
317
+ ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
290
318
  {
291
319
  uint64_t mask = search->matches_mask;
292
320
  uint32_t index = trailing_zeros64(mask) >> 2;
@@ -400,7 +428,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
400
428
 
401
429
  #ifdef HAVE_SIMD_SSE2
402
430
 
403
- static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
431
+ ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
404
432
  {
405
433
  int mask = search->matches_mask;
406
434
  int index = trailing_zeros(mask);
@@ -424,7 +452,7 @@ static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
424
452
  #define TARGET_SSE2
425
453
  #endif
426
454
 
427
- static inline TARGET_SSE2 FORCE_INLINE unsigned char search_escape_basic_sse2(search_state *search)
455
+ ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
428
456
  {
429
457
  if (RB_UNLIKELY(search->has_matches)) {
430
458
  // There are more matches if search->matches_mask > 0.
@@ -672,290 +700,6 @@ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned
672
700
  }
673
701
  }
674
702
 
675
- /*
676
- * Document-module: JSON::Ext::Generator
677
- *
678
- * This is the JSON generator implemented as a C extension. It can be
679
- * configured to be used by setting
680
- *
681
- * JSON.generator = JSON::Ext::Generator
682
- *
683
- * with the method generator= in JSON.
684
- *
685
- */
686
-
687
- /* Explanation of the following: that's the only way to not pollute
688
- * standard library's docs with GeneratorMethods::<ClassName> which
689
- * are uninformative and take a large place in a list of classes
690
- */
691
-
692
- /*
693
- * Document-module: JSON::Ext::Generator::GeneratorMethods
694
- * :nodoc:
695
- */
696
-
697
- /*
698
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Array
699
- * :nodoc:
700
- */
701
-
702
- /*
703
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
704
- * :nodoc:
705
- */
706
-
707
- /*
708
- * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
709
- * :nodoc:
710
- */
711
-
712
- /*
713
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
714
- * :nodoc:
715
- */
716
-
717
- /*
718
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Float
719
- * :nodoc:
720
- */
721
-
722
- /*
723
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
724
- * :nodoc:
725
- */
726
-
727
- /*
728
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
729
- * :nodoc:
730
- */
731
-
732
- /*
733
- * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
734
- * :nodoc:
735
- */
736
-
737
- /*
738
- * Document-module: JSON::Ext::Generator::GeneratorMethods::Object
739
- * :nodoc:
740
- */
741
-
742
- /*
743
- * Document-module: JSON::Ext::Generator::GeneratorMethods::String
744
- * :nodoc:
745
- */
746
-
747
- /*
748
- * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
749
- * :nodoc:
750
- */
751
-
752
- /*
753
- * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
754
- * :nodoc:
755
- */
756
-
757
- /*
758
- * call-seq: to_json(state = nil)
759
- *
760
- * Returns a JSON string containing a JSON object, that is generated from
761
- * this Hash instance.
762
- * _state_ is a JSON::State object, that can also be used to configure the
763
- * produced JSON string output further.
764
- */
765
- static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
766
- {
767
- rb_check_arity(argc, 0, 1);
768
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
769
- return cState_partial_generate(Vstate, self, generate_json_object, Qfalse);
770
- }
771
-
772
- /*
773
- * call-seq: to_json(state = nil)
774
- *
775
- * Returns a JSON string containing a JSON array, that is generated from
776
- * this Array instance.
777
- * _state_ is a JSON::State object, that can also be used to configure the
778
- * produced JSON string output further.
779
- */
780
- static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self)
781
- {
782
- rb_check_arity(argc, 0, 1);
783
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
784
- return cState_partial_generate(Vstate, self, generate_json_array, Qfalse);
785
- }
786
-
787
- #ifdef RUBY_INTEGER_UNIFICATION
788
- /*
789
- * call-seq: to_json(*)
790
- *
791
- * Returns a JSON string representation for this Integer number.
792
- */
793
- static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
794
- {
795
- rb_check_arity(argc, 0, 1);
796
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
797
- return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse);
798
- }
799
-
800
- #else
801
- /*
802
- * call-seq: to_json(*)
803
- *
804
- * Returns a JSON string representation for this Integer number.
805
- */
806
- static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
807
- {
808
- rb_check_arity(argc, 0, 1);
809
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
810
- return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse);
811
- }
812
-
813
- /*
814
- * call-seq: to_json(*)
815
- *
816
- * Returns a JSON string representation for this Integer number.
817
- */
818
- static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
819
- {
820
- rb_check_arity(argc, 0, 1);
821
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
822
- return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse);
823
- }
824
- #endif
825
-
826
- /*
827
- * call-seq: to_json(*)
828
- *
829
- * Returns a JSON string representation for this Float number.
830
- */
831
- static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
832
- {
833
- rb_check_arity(argc, 0, 1);
834
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
835
- return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
836
- }
837
-
838
- /*
839
- * call-seq: String.included(modul)
840
- *
841
- * Extends _modul_ with the String::Extend module.
842
- */
843
- static VALUE mString_included_s(VALUE self, VALUE modul)
844
- {
845
- VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
846
- rb_call_super(1, &modul);
847
- return result;
848
- }
849
-
850
- /*
851
- * call-seq: to_json(*)
852
- *
853
- * This string should be encoded with UTF-8 A call to this method
854
- * returns a JSON string encoded with UTF16 big endian characters as
855
- * \u????.
856
- */
857
- static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
858
- {
859
- rb_check_arity(argc, 0, 1);
860
- VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
861
- return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
862
- }
863
-
864
- /*
865
- * call-seq: to_json_raw_object()
866
- *
867
- * This method creates a raw object hash, that can be nested into
868
- * other data structures and will be generated as a raw string. This
869
- * method should be used, if you want to convert raw strings to JSON
870
- * instead of UTF-8 strings, e. g. binary data.
871
- */
872
- static VALUE mString_to_json_raw_object(VALUE self)
873
- {
874
- VALUE ary;
875
- VALUE result = rb_hash_new();
876
- rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
877
- ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
878
- rb_hash_aset(result, rb_utf8_str_new_lit("raw"), ary);
879
- return result;
880
- }
881
-
882
- /*
883
- * call-seq: to_json_raw(*args)
884
- *
885
- * This method creates a JSON text from the result of a call to
886
- * to_json_raw_object of this String.
887
- */
888
- static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
889
- {
890
- VALUE obj = mString_to_json_raw_object(self);
891
- Check_Type(obj, T_HASH);
892
- return mHash_to_json(argc, argv, obj);
893
- }
894
-
895
- /*
896
- * call-seq: json_create(o)
897
- *
898
- * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
899
- * key "raw"). The Ruby String can be created by this module method.
900
- */
901
- static VALUE mString_Extend_json_create(VALUE self, VALUE o)
902
- {
903
- VALUE ary;
904
- Check_Type(o, T_HASH);
905
- ary = rb_hash_aref(o, rb_str_new2("raw"));
906
- return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
907
- }
908
-
909
- /*
910
- * call-seq: to_json(*)
911
- *
912
- * Returns a JSON string for true: 'true'.
913
- */
914
- static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
915
- {
916
- rb_check_arity(argc, 0, 1);
917
- return rb_utf8_str_new("true", 4);
918
- }
919
-
920
- /*
921
- * call-seq: to_json(*)
922
- *
923
- * Returns a JSON string for false: 'false'.
924
- */
925
- static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
926
- {
927
- rb_check_arity(argc, 0, 1);
928
- return rb_utf8_str_new("false", 5);
929
- }
930
-
931
- /*
932
- * call-seq: to_json(*)
933
- *
934
- * Returns a JSON string for nil: 'null'.
935
- */
936
- static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
937
- {
938
- rb_check_arity(argc, 0, 1);
939
- return rb_utf8_str_new("null", 4);
940
- }
941
-
942
- /*
943
- * call-seq: to_json(*)
944
- *
945
- * Converts this object to a string (calling #to_s), converts
946
- * it to a JSON string, and returns the result. This is a fallback, if no
947
- * special method #to_json was defined for some object.
948
- */
949
- static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
950
- {
951
- VALUE state;
952
- VALUE string = rb_funcall(self, i_to_s, 0);
953
- rb_scan_args(argc, argv, "01", &state);
954
- Check_Type(string, T_STRING);
955
- state = cState_from_state_s(cState, state);
956
- return cState_partial_generate(state, string, generate_json_string, Qfalse);
957
- }
958
-
959
703
  static void State_mark(void *ptr)
960
704
  {
961
705
  JSON_Generator_State *state = ptr;
@@ -989,11 +733,6 @@ static size_t State_memsize(const void *ptr)
989
733
  return sizeof(JSON_Generator_State);
990
734
  }
991
735
 
992
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
993
- # undef RUBY_TYPED_FROZEN_SHAREABLE
994
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
995
- #endif
996
-
997
736
  static const rb_data_type_t JSON_Generator_State_type = {
998
737
  "JSON/Generator/State",
999
738
  {
@@ -1035,18 +774,24 @@ static void vstate_spill(struct generate_json_data *data)
1035
774
  RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
1036
775
  }
1037
776
 
1038
- static inline VALUE vstate_get(struct generate_json_data *data)
777
+ static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
1039
778
  {
1040
779
  if (RB_UNLIKELY(!data->vstate)) {
1041
780
  vstate_spill(data);
1042
781
  }
1043
- return data->vstate;
782
+ GET_STATE(data->vstate);
783
+ state->depth = data->depth;
784
+ VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
785
+ // no need to restore state->depth, vstate is just a temporary State
786
+ return tmp;
1044
787
  }
1045
788
 
1046
- struct hash_foreach_arg {
1047
- struct generate_json_data *data;
1048
- int iter;
1049
- };
789
+ static VALUE
790
+ json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
791
+ {
792
+ VALUE proc_args[2] = {object, is_key};
793
+ return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
794
+ }
1050
795
 
1051
796
  static VALUE
1052
797
  convert_string_subclass(VALUE key)
@@ -1063,6 +808,145 @@ convert_string_subclass(VALUE key)
1063
808
  return key_to_s;
1064
809
  }
1065
810
 
811
+ static bool enc_utf8_compatible_p(int enc_idx)
812
+ {
813
+ if (enc_idx == usascii_encindex) return true;
814
+ if (enc_idx == utf8_encindex) return true;
815
+ return false;
816
+ }
817
+
818
+ static VALUE encode_json_string_try(VALUE str)
819
+ {
820
+ return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
821
+ }
822
+
823
+ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
824
+ {
825
+ raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
826
+ return Qundef;
827
+ }
828
+
829
+ static inline bool valid_json_string_p(VALUE str)
830
+ {
831
+ int coderange = rb_enc_str_coderange(str);
832
+
833
+ if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
834
+ return true;
835
+ }
836
+
837
+ if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
838
+ return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
839
+ }
840
+
841
+ return false;
842
+ }
843
+
844
+ static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
845
+ {
846
+ if (RB_LIKELY(valid_json_string_p(str))) {
847
+ return str;
848
+ }
849
+
850
+ if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
851
+ VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
852
+ if (coerced_str != str) {
853
+ if (RB_TYPE_P(coerced_str, T_STRING)) {
854
+ if (!valid_json_string_p(coerced_str)) {
855
+ raise_generator_error(str, "source sequence is illegal/malformed utf-8");
856
+ }
857
+ } else {
858
+ // as_json could return another type than T_STRING
859
+ if (is_key) {
860
+ raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
861
+ }
862
+ }
863
+
864
+ return coerced_str;
865
+ }
866
+ }
867
+
868
+ if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
869
+ VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
870
+ switch (rb_enc_str_coderange(utf8_string)) {
871
+ case ENC_CODERANGE_7BIT:
872
+ return utf8_string;
873
+ case ENC_CODERANGE_VALID:
874
+ // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
875
+ // TODO: Raise in 3.0.0
876
+ rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
877
+ return utf8_string;
878
+ break;
879
+ }
880
+ }
881
+
882
+ return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
883
+ }
884
+
885
+ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
886
+ {
887
+ fbuffer_append_char(buffer, '"');
888
+
889
+ long len;
890
+ search_state search;
891
+ search.buffer = buffer;
892
+ RSTRING_GETMEM(obj, search.ptr, len);
893
+ search.cursor = search.ptr;
894
+ search.end = search.ptr + len;
895
+
896
+ #ifdef HAVE_SIMD
897
+ search.matches_mask = 0;
898
+ search.has_matches = false;
899
+ search.chunk_base = NULL;
900
+ search.chunk_end = NULL;
901
+ #endif /* HAVE_SIMD */
902
+
903
+ switch (rb_enc_str_coderange(obj)) {
904
+ case ENC_CODERANGE_7BIT:
905
+ case ENC_CODERANGE_VALID:
906
+ if (RB_UNLIKELY(data->state->ascii_only)) {
907
+ convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
908
+ } else if (RB_UNLIKELY(data->state->script_safe)) {
909
+ convert_UTF8_to_script_safe_JSON(&search);
910
+ } else {
911
+ convert_UTF8_to_JSON(&search);
912
+ }
913
+ break;
914
+ default:
915
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
916
+ break;
917
+ }
918
+ fbuffer_append_char(buffer, '"');
919
+ }
920
+
921
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
922
+ {
923
+ obj = ensure_valid_encoding(data, obj, false, false);
924
+ raw_generate_json_string(buffer, data, obj);
925
+ }
926
+
927
+ struct hash_foreach_arg {
928
+ VALUE hash;
929
+ struct generate_json_data *data;
930
+ int first_key_type;
931
+ bool first;
932
+ bool mixed_keys_encountered;
933
+ };
934
+
935
+ NOINLINE(static) void
936
+ json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
937
+ {
938
+ if (arg->mixed_keys_encountered) {
939
+ return;
940
+ }
941
+ arg->mixed_keys_encountered = true;
942
+
943
+ JSON_Generator_State *state = arg->data->state;
944
+ if (state->on_duplicate_key != JSON_IGNORE) {
945
+ VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
946
+ rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
947
+ }
948
+ }
949
+
1066
950
  static int
1067
951
  json_object_i(VALUE key, VALUE val, VALUE _arg)
1068
952
  {
@@ -1072,22 +956,34 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1072
956
  FBuffer *buffer = data->buffer;
1073
957
  JSON_Generator_State *state = data->state;
1074
958
 
1075
- long depth = state->depth;
1076
- int j;
959
+ long depth = data->depth;
960
+ int key_type = rb_type(key);
961
+
962
+ if (arg->first) {
963
+ arg->first = false;
964
+ arg->first_key_type = key_type;
965
+ }
966
+ else {
967
+ fbuffer_append_char(buffer, ',');
968
+ }
1077
969
 
1078
- if (arg->iter > 0) fbuffer_append_char(buffer, ',');
1079
970
  if (RB_UNLIKELY(data->state->object_nl)) {
1080
971
  fbuffer_append_str(buffer, data->state->object_nl);
1081
972
  }
1082
973
  if (RB_UNLIKELY(data->state->indent)) {
1083
- for (j = 0; j < depth; j++) {
1084
- fbuffer_append_str(buffer, data->state->indent);
1085
- }
974
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1086
975
  }
1087
976
 
1088
977
  VALUE key_to_s;
1089
- switch (rb_type(key)) {
978
+ bool as_json_called = false;
979
+
980
+ start:
981
+ switch (key_type) {
1090
982
  case T_STRING:
983
+ if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
984
+ json_inspect_hash_with_mixed_keys(arg);
985
+ }
986
+
1091
987
  if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
1092
988
  key_to_s = key;
1093
989
  } else {
@@ -1095,15 +991,31 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1095
991
  }
1096
992
  break;
1097
993
  case T_SYMBOL:
994
+ if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
995
+ json_inspect_hash_with_mixed_keys(arg);
996
+ }
997
+
1098
998
  key_to_s = rb_sym2str(key);
1099
999
  break;
1100
1000
  default:
1001
+ if (data->state->strict) {
1002
+ if (RTEST(data->state->as_json) && !as_json_called) {
1003
+ key = json_call_as_json(data->state, key, Qtrue);
1004
+ key_type = rb_type(key);
1005
+ as_json_called = true;
1006
+ goto start;
1007
+ } else {
1008
+ raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
1009
+ }
1010
+ }
1101
1011
  key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
1102
1012
  break;
1103
1013
  }
1104
1014
 
1015
+ key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
1016
+
1105
1017
  if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
1106
- generate_json_string(buffer, data, key_to_s);
1018
+ raw_generate_json_string(buffer, data, key_to_s);
1107
1019
  } else {
1108
1020
  generate_json(buffer, data, key_to_s);
1109
1021
  }
@@ -1112,46 +1024,43 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1112
1024
  if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
1113
1025
  generate_json(buffer, data, val);
1114
1026
 
1115
- arg->iter++;
1116
1027
  return ST_CONTINUE;
1117
1028
  }
1118
1029
 
1119
1030
  static inline long increase_depth(struct generate_json_data *data)
1120
1031
  {
1121
1032
  JSON_Generator_State *state = data->state;
1122
- long depth = ++state->depth;
1033
+ long depth = ++data->depth;
1123
1034
  if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
1124
- rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
1035
+ rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
1125
1036
  }
1126
1037
  return depth;
1127
1038
  }
1128
1039
 
1129
1040
  static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1130
1041
  {
1131
- int j;
1132
1042
  long depth = increase_depth(data);
1133
1043
 
1134
1044
  if (RHASH_SIZE(obj) == 0) {
1135
1045
  fbuffer_append(buffer, "{}", 2);
1136
- --data->state->depth;
1046
+ --data->depth;
1137
1047
  return;
1138
1048
  }
1139
1049
 
1140
1050
  fbuffer_append_char(buffer, '{');
1141
1051
 
1142
1052
  struct hash_foreach_arg arg = {
1053
+ .hash = obj,
1143
1054
  .data = data,
1144
- .iter = 0,
1055
+ .first = true,
1145
1056
  };
1146
1057
  rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
1147
1058
 
1148
- depth = --data->state->depth;
1059
+ depth = --data->depth;
1149
1060
  if (RB_UNLIKELY(data->state->object_nl)) {
1150
1061
  fbuffer_append_str(buffer, data->state->object_nl);
1151
1062
  if (RB_UNLIKELY(data->state->indent)) {
1152
- for (j = 0; j < depth; j++) {
1153
- fbuffer_append_str(buffer, data->state->indent);
1154
- }
1063
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1155
1064
  }
1156
1065
  }
1157
1066
  fbuffer_append_char(buffer, '}');
@@ -1159,125 +1068,41 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
1159
1068
 
1160
1069
  static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1161
1070
  {
1162
- int i, j;
1163
1071
  long depth = increase_depth(data);
1164
1072
 
1165
1073
  if (RARRAY_LEN(obj) == 0) {
1166
1074
  fbuffer_append(buffer, "[]", 2);
1167
- --data->state->depth;
1075
+ --data->depth;
1168
1076
  return;
1169
1077
  }
1170
1078
 
1171
1079
  fbuffer_append_char(buffer, '[');
1172
1080
  if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
1173
- for (i = 0; i < RARRAY_LEN(obj); i++) {
1081
+ for (int i = 0; i < RARRAY_LEN(obj); i++) {
1174
1082
  if (i > 0) {
1175
1083
  fbuffer_append_char(buffer, ',');
1176
1084
  if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
1177
1085
  }
1178
1086
  if (RB_UNLIKELY(data->state->indent)) {
1179
- for (j = 0; j < depth; j++) {
1180
- fbuffer_append_str(buffer, data->state->indent);
1181
- }
1087
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1182
1088
  }
1183
1089
  generate_json(buffer, data, RARRAY_AREF(obj, i));
1184
1090
  }
1185
- data->state->depth = --depth;
1091
+ data->depth = --depth;
1186
1092
  if (RB_UNLIKELY(data->state->array_nl)) {
1187
1093
  fbuffer_append_str(buffer, data->state->array_nl);
1188
1094
  if (RB_UNLIKELY(data->state->indent)) {
1189
- for (j = 0; j < depth; j++) {
1190
- fbuffer_append_str(buffer, data->state->indent);
1191
- }
1095
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
1192
1096
  }
1193
1097
  }
1194
1098
  fbuffer_append_char(buffer, ']');
1195
1099
  }
1196
1100
 
1197
- static inline int enc_utf8_compatible_p(int enc_idx)
1198
- {
1199
- if (enc_idx == usascii_encindex) return 1;
1200
- if (enc_idx == utf8_encindex) return 1;
1201
- return 0;
1202
- }
1203
-
1204
- static VALUE encode_json_string_try(VALUE str)
1205
- {
1206
- return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
1207
- }
1208
-
1209
- static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
1210
- {
1211
- raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
1212
- return Qundef;
1213
- }
1214
-
1215
- static inline VALUE ensure_valid_encoding(VALUE str)
1216
- {
1217
- int encindex = RB_ENCODING_GET(str);
1218
- VALUE utf8_string;
1219
- if (RB_UNLIKELY(!enc_utf8_compatible_p(encindex))) {
1220
- if (encindex == binary_encindex) {
1221
- utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
1222
- switch (rb_enc_str_coderange(utf8_string)) {
1223
- case ENC_CODERANGE_7BIT:
1224
- return utf8_string;
1225
- case ENC_CODERANGE_VALID:
1226
- // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
1227
- // TODO: Raise in 3.0.0
1228
- rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
1229
- return utf8_string;
1230
- break;
1231
- }
1232
- }
1233
-
1234
- str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
1235
- }
1236
- return str;
1237
- }
1238
-
1239
- static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1240
- {
1241
- obj = ensure_valid_encoding(obj);
1242
-
1243
- fbuffer_append_char(buffer, '"');
1244
-
1245
- long len;
1246
- search_state search;
1247
- search.buffer = buffer;
1248
- RSTRING_GETMEM(obj, search.ptr, len);
1249
- search.cursor = search.ptr;
1250
- search.end = search.ptr + len;
1251
-
1252
- #ifdef HAVE_SIMD
1253
- search.matches_mask = 0;
1254
- search.has_matches = false;
1255
- search.chunk_base = NULL;
1256
- #endif /* HAVE_SIMD */
1257
-
1258
- switch (rb_enc_str_coderange(obj)) {
1259
- case ENC_CODERANGE_7BIT:
1260
- case ENC_CODERANGE_VALID:
1261
- if (RB_UNLIKELY(data->state->ascii_only)) {
1262
- convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
1263
- } else if (RB_UNLIKELY(data->state->script_safe)) {
1264
- convert_UTF8_to_script_safe_JSON(&search);
1265
- } else {
1266
- convert_UTF8_to_JSON(&search);
1267
- }
1268
- break;
1269
- default:
1270
- raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
1271
- break;
1272
- }
1273
- fbuffer_append_char(buffer, '"');
1274
- }
1275
-
1276
1101
  static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1277
1102
  {
1278
1103
  VALUE tmp;
1279
1104
  if (rb_respond_to(obj, i_to_json)) {
1280
- tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
1105
+ tmp = json_call_to_json(data, obj);
1281
1106
  Check_Type(tmp, T_STRING);
1282
1107
  fbuffer_append_str(buffer, tmp);
1283
1108
  } else {
@@ -1319,19 +1144,9 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
1319
1144
  static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1320
1145
  {
1321
1146
  VALUE tmp = rb_funcall(obj, i_to_s, 0);
1322
- fbuffer_append_str(buffer, tmp);
1147
+ fbuffer_append_str(buffer, StringValue(tmp));
1323
1148
  }
1324
1149
 
1325
- #ifdef RUBY_INTEGER_UNIFICATION
1326
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1327
- {
1328
- if (FIXNUM_P(obj))
1329
- generate_json_fixnum(buffer, data, obj);
1330
- else
1331
- generate_json_bignum(buffer, data, obj);
1332
- }
1333
- #endif
1334
-
1335
1150
  static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1336
1151
  {
1337
1152
  double value = RFLOAT_VALUE(obj);
@@ -1340,11 +1155,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
1340
1155
  /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
1341
1156
  if (!allow_nan) {
1342
1157
  if (data->state->strict && data->state->as_json) {
1343
- VALUE casted_obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1158
+ VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
1344
1159
  if (casted_obj != obj) {
1345
1160
  increase_depth(data);
1346
1161
  generate_json(buffer, data, casted_obj);
1347
- data->state->depth--;
1162
+ data->depth--;
1348
1163
  return;
1349
1164
  }
1350
1165
  }
@@ -1357,12 +1172,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
1357
1172
  }
1358
1173
 
1359
1174
  /* This implementation writes directly into the buffer. We reserve
1360
- * the 28 characters that fpconv_dtoa states as its maximum.
1175
+ * the 32 characters that fpconv_dtoa states as its maximum.
1361
1176
  */
1362
- fbuffer_inc_capa(buffer, 28);
1177
+ fbuffer_inc_capa(buffer, 32);
1363
1178
  char* d = buffer->ptr + buffer->len;
1364
1179
  int len = fpconv_dtoa(value, d);
1365
-
1366
1180
  /* fpconv_dtoa converts a float to its shortest string representation,
1367
1181
  * but it adds a ".0" if this is a plain integer.
1368
1182
  */
@@ -1376,7 +1190,7 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
1376
1190
  fbuffer_append_str(buffer, fragment);
1377
1191
  }
1378
1192
 
1379
- static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1193
+ static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
1380
1194
  {
1381
1195
  bool as_json_called = false;
1382
1196
  start:
@@ -1403,22 +1217,31 @@ start:
1403
1217
  generate_json_bignum(buffer, data, obj);
1404
1218
  break;
1405
1219
  case T_HASH:
1406
- if (klass != rb_cHash) goto general;
1220
+ if (fallback && klass != rb_cHash) goto general;
1407
1221
  generate_json_object(buffer, data, obj);
1408
1222
  break;
1409
1223
  case T_ARRAY:
1410
- if (klass != rb_cArray) goto general;
1224
+ if (fallback && klass != rb_cArray) goto general;
1411
1225
  generate_json_array(buffer, data, obj);
1412
1226
  break;
1413
1227
  case T_STRING:
1414
- if (klass != rb_cString) goto general;
1415
- generate_json_string(buffer, data, obj);
1228
+ if (fallback && klass != rb_cString) goto general;
1229
+
1230
+ if (RB_LIKELY(valid_json_string_p(obj))) {
1231
+ raw_generate_json_string(buffer, data, obj);
1232
+ } else if (as_json_called) {
1233
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
1234
+ } else {
1235
+ obj = ensure_valid_encoding(data, obj, false, false);
1236
+ as_json_called = true;
1237
+ goto start;
1238
+ }
1416
1239
  break;
1417
1240
  case T_SYMBOL:
1418
1241
  generate_json_symbol(buffer, data, obj);
1419
1242
  break;
1420
1243
  case T_FLOAT:
1421
- if (klass != rb_cFloat) goto general;
1244
+ if (fallback && klass != rb_cFloat) goto general;
1422
1245
  generate_json_float(buffer, data, obj);
1423
1246
  break;
1424
1247
  case T_STRUCT:
@@ -1429,7 +1252,7 @@ start:
1429
1252
  general:
1430
1253
  if (data->state->strict) {
1431
1254
  if (RTEST(data->state->as_json) && !as_json_called) {
1432
- obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1255
+ obj = json_call_as_json(data->state, obj, Qfalse);
1433
1256
  as_json_called = true;
1434
1257
  goto start;
1435
1258
  } else {
@@ -1442,26 +1265,34 @@ start:
1442
1265
  }
1443
1266
  }
1444
1267
 
1268
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1269
+ {
1270
+ generate_json_general(buffer, data, obj, true);
1271
+ }
1272
+
1273
+ static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1274
+ {
1275
+ generate_json_general(buffer, data, obj, false);
1276
+ }
1277
+
1445
1278
  static VALUE generate_json_try(VALUE d)
1446
1279
  {
1447
1280
  struct generate_json_data *data = (struct generate_json_data *)d;
1448
1281
 
1449
1282
  data->func(data->buffer, data, data->obj);
1450
1283
 
1451
- return Qnil;
1284
+ return fbuffer_finalize(data->buffer);
1452
1285
  }
1453
1286
 
1454
- static VALUE generate_json_rescue(VALUE d, VALUE exc)
1287
+ static VALUE generate_json_ensure(VALUE d)
1455
1288
  {
1456
1289
  struct generate_json_data *data = (struct generate_json_data *)d;
1457
1290
  fbuffer_free(data->buffer);
1458
1291
 
1459
- rb_exc_raise(exc);
1460
-
1461
1292
  return Qundef;
1462
1293
  }
1463
1294
 
1464
- static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
1295
+ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
1465
1296
  {
1466
1297
  GET_STATE(self);
1467
1298
 
@@ -1473,14 +1304,13 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
1473
1304
 
1474
1305
  struct generate_json_data data = {
1475
1306
  .buffer = &buffer,
1476
- .vstate = self,
1307
+ .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
1477
1308
  .state = state,
1309
+ .depth = state->depth,
1478
1310
  .obj = obj,
1479
1311
  .func = func
1480
1312
  };
1481
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1482
-
1483
- return fbuffer_finalize(&buffer);
1313
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1484
1314
  }
1485
1315
 
1486
1316
  /* call-seq:
@@ -1496,10 +1326,16 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
1496
1326
  rb_check_arity(argc, 1, 2);
1497
1327
  VALUE obj = argv[0];
1498
1328
  VALUE io = argc > 1 ? argv[1] : Qnil;
1499
- VALUE result = cState_partial_generate(self, obj, generate_json, io);
1500
- GET_STATE(self);
1501
- (void)state;
1502
- return result;
1329
+ return cState_partial_generate(self, obj, generate_json, io);
1330
+ }
1331
+
1332
+ /* :nodoc: */
1333
+ static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
1334
+ {
1335
+ rb_check_arity(argc, 1, 2);
1336
+ VALUE obj = argv[0];
1337
+ VALUE io = argc > 1 ? argv[1] : Qnil;
1338
+ return cState_partial_generate(self, obj, generate_json_no_fallback, io);
1503
1339
  }
1504
1340
 
1505
1341
  static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
@@ -1580,6 +1416,7 @@ static VALUE string_config(VALUE config)
1580
1416
  */
1581
1417
  static VALUE cState_indent_set(VALUE self, VALUE indent)
1582
1418
  {
1419
+ rb_check_frozen(self);
1583
1420
  GET_STATE(self);
1584
1421
  RB_OBJ_WRITE(self, &state->indent, string_config(indent));
1585
1422
  return Qnil;
@@ -1605,6 +1442,7 @@ static VALUE cState_space(VALUE self)
1605
1442
  */
1606
1443
  static VALUE cState_space_set(VALUE self, VALUE space)
1607
1444
  {
1445
+ rb_check_frozen(self);
1608
1446
  GET_STATE(self);
1609
1447
  RB_OBJ_WRITE(self, &state->space, string_config(space));
1610
1448
  return Qnil;
@@ -1628,6 +1466,7 @@ static VALUE cState_space_before(VALUE self)
1628
1466
  */
1629
1467
  static VALUE cState_space_before_set(VALUE self, VALUE space_before)
1630
1468
  {
1469
+ rb_check_frozen(self);
1631
1470
  GET_STATE(self);
1632
1471
  RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
1633
1472
  return Qnil;
@@ -1653,6 +1492,7 @@ static VALUE cState_object_nl(VALUE self)
1653
1492
  */
1654
1493
  static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
1655
1494
  {
1495
+ rb_check_frozen(self);
1656
1496
  GET_STATE(self);
1657
1497
  RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
1658
1498
  return Qnil;
@@ -1676,6 +1516,7 @@ static VALUE cState_array_nl(VALUE self)
1676
1516
  */
1677
1517
  static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
1678
1518
  {
1519
+ rb_check_frozen(self);
1679
1520
  GET_STATE(self);
1680
1521
  RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
1681
1522
  return Qnil;
@@ -1699,6 +1540,7 @@ static VALUE cState_as_json(VALUE self)
1699
1540
  */
1700
1541
  static VALUE cState_as_json_set(VALUE self, VALUE as_json)
1701
1542
  {
1543
+ rb_check_frozen(self);
1702
1544
  GET_STATE(self);
1703
1545
  RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
1704
1546
  return Qnil;
@@ -1733,6 +1575,17 @@ static long long_config(VALUE num)
1733
1575
  return RTEST(num) ? FIX2LONG(num) : 0;
1734
1576
  }
1735
1577
 
1578
+ // depth must never be negative; reject early with a clear error.
1579
+ static long depth_config(VALUE num)
1580
+ {
1581
+ if (!RTEST(num)) return 0;
1582
+ long d = NUM2LONG(num);
1583
+ if (RB_UNLIKELY(d < 0)) {
1584
+ rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
1585
+ }
1586
+ return d;
1587
+ }
1588
+
1736
1589
  /*
1737
1590
  * call-seq: max_nesting=(depth)
1738
1591
  *
@@ -1741,6 +1594,7 @@ static long long_config(VALUE num)
1741
1594
  */
1742
1595
  static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
1743
1596
  {
1597
+ rb_check_frozen(self);
1744
1598
  GET_STATE(self);
1745
1599
  state->max_nesting = long_config(depth);
1746
1600
  return Qnil;
@@ -1766,6 +1620,7 @@ static VALUE cState_script_safe(VALUE self)
1766
1620
  */
1767
1621
  static VALUE cState_script_safe_set(VALUE self, VALUE enable)
1768
1622
  {
1623
+ rb_check_frozen(self);
1769
1624
  GET_STATE(self);
1770
1625
  state->script_safe = RTEST(enable);
1771
1626
  return Qnil;
@@ -1797,6 +1652,7 @@ static VALUE cState_strict(VALUE self)
1797
1652
  */
1798
1653
  static VALUE cState_strict_set(VALUE self, VALUE enable)
1799
1654
  {
1655
+ rb_check_frozen(self);
1800
1656
  GET_STATE(self);
1801
1657
  state->strict = RTEST(enable);
1802
1658
  return Qnil;
@@ -1821,6 +1677,7 @@ static VALUE cState_allow_nan_p(VALUE self)
1821
1677
  */
1822
1678
  static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
1823
1679
  {
1680
+ rb_check_frozen(self);
1824
1681
  GET_STATE(self);
1825
1682
  state->allow_nan = RTEST(enable);
1826
1683
  return Qnil;
@@ -1845,11 +1702,25 @@ static VALUE cState_ascii_only_p(VALUE self)
1845
1702
  */
1846
1703
  static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
1847
1704
  {
1705
+ rb_check_frozen(self);
1848
1706
  GET_STATE(self);
1849
1707
  state->ascii_only = RTEST(enable);
1850
1708
  return Qnil;
1851
1709
  }
1852
1710
 
1711
+ static VALUE cState_allow_duplicate_key_p(VALUE self)
1712
+ {
1713
+ GET_STATE(self);
1714
+ switch (state->on_duplicate_key) {
1715
+ case JSON_IGNORE:
1716
+ return Qtrue;
1717
+ case JSON_DEPRECATED:
1718
+ return Qnil;
1719
+ default:
1720
+ return Qfalse;
1721
+ }
1722
+ }
1723
+
1853
1724
  /*
1854
1725
  * call-seq: depth
1855
1726
  *
@@ -1869,8 +1740,9 @@ static VALUE cState_depth(VALUE self)
1869
1740
  */
1870
1741
  static VALUE cState_depth_set(VALUE self, VALUE depth)
1871
1742
  {
1743
+ rb_check_frozen(self);
1872
1744
  GET_STATE(self);
1873
- state->depth = long_config(depth);
1745
+ state->depth = depth_config(depth);
1874
1746
  return Qnil;
1875
1747
  }
1876
1748
 
@@ -1902,6 +1774,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
1902
1774
  */
1903
1775
  static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
1904
1776
  {
1777
+ rb_check_frozen(self);
1905
1778
  GET_STATE(self);
1906
1779
  buffer_initial_length_set(state, buffer_initial_length);
1907
1780
  return Qnil;
@@ -1934,13 +1807,15 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1934
1807
  else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
1935
1808
  else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
1936
1809
  else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
1937
- else if (key == sym_depth) { state->depth = long_config(val); }
1810
+ else if (key == sym_depth) { state->depth = depth_config(val); }
1938
1811
  else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
1939
1812
  else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
1940
1813
  else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
1941
1814
  else if (key == sym_strict) { state->strict = RTEST(val); }
1815
+ else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1942
1816
  else if (key == sym_as_json) {
1943
1817
  VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
1818
+ state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
1944
1819
  state_write_value(data, &state->as_json, proc);
1945
1820
  }
1946
1821
  return ST_CONTINUE;
@@ -1966,12 +1841,13 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
1966
1841
 
1967
1842
  static VALUE cState_configure(VALUE self, VALUE opts)
1968
1843
  {
1844
+ rb_check_frozen(self);
1969
1845
  GET_STATE(self);
1970
1846
  configure_state(state, self, opts);
1971
1847
  return self;
1972
1848
  }
1973
1849
 
1974
- static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1850
+ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
1975
1851
  {
1976
1852
  JSON_Generator_State state = {0};
1977
1853
  state_init(&state);
@@ -1987,17 +1863,23 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1987
1863
  .buffer = &buffer,
1988
1864
  .vstate = Qfalse,
1989
1865
  .state = &state,
1866
+ .depth = state.depth,
1990
1867
  .obj = obj,
1991
- .func = generate_json,
1868
+ .func = func,
1992
1869
  };
1993
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1870
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1871
+ }
1994
1872
 
1995
- return fbuffer_finalize(&buffer);
1873
+ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1874
+ {
1875
+ return cState_m_do_generate(klass, obj, opts, io, generate_json);
1876
+ }
1877
+
1878
+ static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1879
+ {
1880
+ return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
1996
1881
  }
1997
1882
 
1998
- /*
1999
- *
2000
- */
2001
1883
  void Init_generator(void)
2002
1884
  {
2003
1885
  #ifdef HAVE_RB_EXT_RACTOR_SAFE
@@ -2062,51 +1944,12 @@ void Init_generator(void)
2062
1944
  rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
2063
1945
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
2064
1946
  rb_define_method(cState, "generate", cState_generate, -1);
2065
- rb_define_alias(cState, "generate_new", "generate"); // :nodoc:
1947
+ rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
2066
1948
 
2067
- rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
2068
-
2069
- VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
2070
-
2071
- VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object");
2072
- rb_define_method(mObject, "to_json", mObject_to_json, -1);
2073
-
2074
- VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash");
2075
- rb_define_method(mHash, "to_json", mHash_to_json, -1);
2076
-
2077
- VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array");
2078
- rb_define_method(mArray, "to_json", mArray_to_json, -1);
2079
-
2080
- #ifdef RUBY_INTEGER_UNIFICATION
2081
- VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
2082
- rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
2083
- #else
2084
- VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
2085
- rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
2086
-
2087
- VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
2088
- rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
2089
- #endif
2090
- VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float");
2091
- rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
2092
-
2093
- VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
2094
- rb_define_singleton_method(mString, "included", mString_included_s, 1);
2095
- rb_define_method(mString, "to_json", mString_to_json, -1);
2096
- rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
2097
- rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
2098
-
2099
- mString_Extend = rb_define_module_under(mString, "Extend");
2100
- rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
2101
-
2102
- VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
2103
- rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
2104
-
2105
- VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
2106
- rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
1949
+ rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
2107
1950
 
2108
- VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
2109
- rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
1951
+ rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
1952
+ rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
2110
1953
 
2111
1954
  rb_global_variable(&Encoding_UTF_8);
2112
1955
  Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
@@ -2114,10 +1957,6 @@ void Init_generator(void)
2114
1957
  i_to_s = rb_intern("to_s");
2115
1958
  i_to_json = rb_intern("to_json");
2116
1959
  i_new = rb_intern("new");
2117
- i_pack = rb_intern("pack");
2118
- i_unpack = rb_intern("unpack");
2119
- i_create_id = rb_intern("create_id");
2120
- i_extend = rb_intern("extend");
2121
1960
  i_encode = rb_intern("encode");
2122
1961
 
2123
1962
  sym_indent = ID2SYM(rb_intern("indent"));
@@ -2134,6 +1973,7 @@ void Init_generator(void)
2134
1973
  sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
2135
1974
  sym_strict = ID2SYM(rb_intern("strict"));
2136
1975
  sym_as_json = ID2SYM(rb_intern("as_json"));
1976
+ sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
2137
1977
 
2138
1978
  usascii_encindex = rb_usascii_encindex();
2139
1979
  utf8_encindex = rb_utf8_encindex();
@@ -2141,22 +1981,5 @@ void Init_generator(void)
2141
1981
 
2142
1982
  rb_require("json/ext/generator/state");
2143
1983
 
2144
-
2145
- switch (find_simd_implementation()) {
2146
- #ifdef HAVE_SIMD
2147
- #ifdef HAVE_SIMD_NEON
2148
- case SIMD_NEON:
2149
- search_escape_basic_impl = search_escape_basic_neon;
2150
- break;
2151
- #endif /* HAVE_SIMD_NEON */
2152
- #ifdef HAVE_SIMD_SSE2
2153
- case SIMD_SSE2:
2154
- search_escape_basic_impl = search_escape_basic_sse2;
2155
- break;
2156
- #endif /* HAVE_SIMD_SSE2 */
2157
- #endif /* HAVE_SIMD */
2158
- default:
2159
- search_escape_basic_impl = search_escape_basic;
2160
- break;
2161
- }
1984
+ simd_impl = find_simd_implementation();
2162
1985
  }