json 2.10.2 → 2.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b909ed90787afa31835c9926529808ca3c6b161755e1f6126b7fe203231fa0c2
4
- data.tar.gz: efd6a873e98f9e4b2b8c078210f141dfde5f3773884bd5851f228c184ff94ff7
3
+ metadata.gz: 1919e2040a180b81eba1f475c511ace075b32015997b7d58098f93103941f8b2
4
+ data.tar.gz: d958784bea1136d935835d3e602fae96f97d25208cafec8a68db03619e6d34d0
5
5
  SHA512:
6
- metadata.gz: bff39363d5d7fa2f209d5bf97a5728738058bc83600b732d95487e263a5a3901b1e18d5c42d7d2be019ac55ef069a50cedc1d9a8d22b11e6061f2eef747dc94d
7
- data.tar.gz: 693e480f8f2489e26c43eef56ad8977ab3f00fada03318a48b88ac55e2b82b15ffae92fd05dd47d86686fae39169b079f59a68fffc6efaf44ee3503a45060e30
6
+ metadata.gz: 742da3e909b2b6d8c1c9de5833b11be0f80e3b50f5296973b57f03cd45ae584162ac33bcbeb5b99fa767714b8531fef71b6d7ff559da40c3b04e75026ba3158f
7
+ data.tar.gz: e55ae407cc5b0da66922a41119b000da925391b58ea9da154a058b15f034334fae9e9c813cda12f4db75936c50e281df00acf8a4053808923d89d5efaa6927af
data/CHANGES.md CHANGED
@@ -1,10 +1,48 @@
1
1
  # Changes
2
2
 
3
+ ### 2025-04-25 (2.11.3)
4
+
5
+ * Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
6
+
7
+ ### 2025-04-24 (2.11.2)
8
+
9
+ * Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
10
+ It now emits a deprecation warning.
11
+
12
+ ### 2025-04-24 (2.11.1)
13
+
14
+ * Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
15
+ These were deprecated 16 years ago, but never emited warnings, only undocumented, so are
16
+ still used by a few gems.
17
+
18
+ ### 2025-04-24 (2.11.0)
19
+
20
+ * Optimize Integer generation to be ~1.8x faster.
21
+ * Optimize Float generation to be ~10x faster.
22
+ * Fix `JSON.load` proc argument to substitute the parsed object with the return value.
23
+ This better match `Marshal.load` behavior.
24
+ * Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
25
+ * Deprecate `JSON.load_default_options`.
26
+ * Deprecate `JSON.unsafe_load_default_options`.
27
+ * Deprecate `JSON.dump_default_options`.
28
+ * Deprecate `Kernel#j`
29
+ * Deprecate `Kernel#jj`
30
+ * Remove outdated `JSON.iconv`.
31
+ * Remove `Class#json_creatable?` monkey patch.
32
+ * Remove deprecated `JSON.restore` method.
33
+ * Remove deprecated `JSON.unparse` method.
34
+ * Remove deprecated `JSON.fast_unparse` method.
35
+ * Remove deprecated `JSON.pretty_unparse` method.
36
+ * Remove deprecated `JSON::UnparserError` constant.
37
+ * Remove outdated `JSON::MissingUnicodeSupport` constant.
38
+
3
39
  ### 2025-03-12 (2.10.2)
4
40
 
5
41
  * Fix a potential crash in the C extension parser.
6
42
  * Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` unadvertently changed it.
7
43
  * Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
44
+ * Ensure parser error snippets are valid UTF-8.
45
+ * Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
8
46
 
9
47
  ### 2025-02-10 (2.10.1)
10
48
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include "ruby.h"
5
5
  #include "ruby/encoding.h"
6
+ #include "../vendor/jeaiii-ltoa.h"
6
7
 
7
8
  /* shims */
8
9
  /* This is the fallback definition from Ruby 3.4 */
@@ -150,6 +151,13 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
150
151
  }
151
152
  }
152
153
 
154
+ /* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
155
+ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
156
+ {
157
+ fb->ptr[fb->len] = chr;
158
+ fb->len += 1;
159
+ }
160
+
153
161
  static void fbuffer_append_str(FBuffer *fb, VALUE str)
154
162
  {
155
163
  const char *newstr = StringValuePtr(str);
@@ -167,25 +175,48 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
167
175
  fb->len++;
168
176
  }
169
177
 
170
- static long fltoa(long number, char *buf)
178
+ static inline char *fbuffer_cursor(FBuffer *fb)
179
+ {
180
+ return fb->ptr + fb->len;
181
+ }
182
+
183
+ static inline void fbuffer_advance_to(FBuffer *fb, char *end)
171
184
  {
172
- static const char digits[] = "0123456789";
173
- long sign = number;
174
- char* tmp = buf;
175
-
176
- if (sign < 0) number = -number;
177
- do *tmp-- = digits[number % 10]; while (number /= 10);
178
- if (sign < 0) *tmp-- = '-';
179
- return buf - tmp;
185
+ fb->len = end - fb->ptr;
180
186
  }
181
187
 
182
- #define LONG_BUFFER_SIZE 20
188
+ /*
189
+ * Appends the decimal string representation of \a number into the buffer.
190
+ */
183
191
  static void fbuffer_append_long(FBuffer *fb, long number)
184
192
  {
185
- char buf[LONG_BUFFER_SIZE];
186
- char *buffer_end = buf + LONG_BUFFER_SIZE;
187
- long len = fltoa(number, buffer_end - 1);
188
- fbuffer_append(fb, buffer_end - len, len);
193
+ /*
194
+ * The jeaiii_ultoa() function produces digits left-to-right,
195
+ * allowing us to write directly into the buffer, but we don't know
196
+ * the number of resulting characters.
197
+ *
198
+ * We do know, however, that the `number` argument is always in the
199
+ * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
200
+ * -4611686018427387904 to 4611686018427387903. The max number of chars
201
+ * generated is therefore 20 (including a potential sign character).
202
+ */
203
+
204
+ static const int MAX_CHARS_FOR_LONG = 20;
205
+
206
+ fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
207
+
208
+ if (number < 0) {
209
+ fbuffer_append_reserved_char(fb, '-');
210
+
211
+ /*
212
+ * Since number is always > LONG_MIN, `-number` will not overflow
213
+ * and is always the positive abs() value.
214
+ */
215
+ number = -number;
216
+ }
217
+
218
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
219
+ fbuffer_advance_to(fb, end);
189
220
  }
190
221
 
191
222
  static VALUE fbuffer_finalize(FBuffer *fb)
@@ -1,5 +1,6 @@
1
1
  #include "ruby.h"
2
2
  #include "../fbuffer/fbuffer.h"
3
+ #include "../vendor/fpconv.c"
3
4
 
4
5
  #include <math.h>
5
6
  #include <ctype.h>
@@ -44,7 +45,7 @@ static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_n
44
45
 
45
46
  struct generate_json_data;
46
47
 
47
- typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
48
+ typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
48
49
 
49
50
  struct generate_json_data {
50
51
  FBuffer *buffer;
@@ -56,20 +57,20 @@ struct generate_json_data {
56
57
 
57
58
  static VALUE cState_from_state_s(VALUE self, VALUE opts);
58
59
  static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
59
- static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
60
- static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
61
- static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
62
- static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
63
- static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
64
- static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
65
- static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
60
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
61
+ static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
62
+ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
63
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
64
+ static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
65
+ static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
66
+ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
66
67
  #ifdef RUBY_INTEGER_UNIFICATION
67
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
68
+ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
68
69
  #endif
69
- static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
70
- static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
71
- static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
72
- static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
70
+ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
71
+ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
72
+ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
73
+ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
73
74
 
74
75
  static int usascii_encindex, utf8_encindex, binary_encindex;
75
76
 
@@ -801,12 +802,12 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
801
802
  int j;
802
803
 
803
804
  if (arg->iter > 0) fbuffer_append_char(buffer, ',');
804
- if (RB_UNLIKELY(state->object_nl)) {
805
- fbuffer_append_str(buffer, state->object_nl);
805
+ if (RB_UNLIKELY(data->state->object_nl)) {
806
+ fbuffer_append_str(buffer, data->state->object_nl);
806
807
  }
807
- if (RB_UNLIKELY(state->indent)) {
808
+ if (RB_UNLIKELY(data->state->indent)) {
808
809
  for (j = 0; j < depth; j++) {
809
- fbuffer_append_str(buffer, state->indent);
810
+ fbuffer_append_str(buffer, data->state->indent);
810
811
  }
811
812
  }
812
813
 
@@ -828,21 +829,22 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
828
829
  }
829
830
 
830
831
  if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
831
- generate_json_string(buffer, data, state, key_to_s);
832
+ generate_json_string(buffer, data, key_to_s);
832
833
  } else {
833
- generate_json(buffer, data, state, key_to_s);
834
+ generate_json(buffer, data, key_to_s);
834
835
  }
835
- if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before);
836
+ if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before);
836
837
  fbuffer_append_char(buffer, ':');
837
- if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
838
- generate_json(buffer, data, state, val);
838
+ if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
839
+ generate_json(buffer, data, val);
839
840
 
840
841
  arg->iter++;
841
842
  return ST_CONTINUE;
842
843
  }
843
844
 
844
- static inline long increase_depth(JSON_Generator_State *state)
845
+ static inline long increase_depth(struct generate_json_data *data)
845
846
  {
847
+ JSON_Generator_State *state = data->state;
846
848
  long depth = ++state->depth;
847
849
  if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
848
850
  rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
@@ -850,14 +852,14 @@ static inline long increase_depth(JSON_Generator_State *state)
850
852
  return depth;
851
853
  }
852
854
 
853
- static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
855
+ static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
854
856
  {
855
857
  int j;
856
- long depth = increase_depth(state);
858
+ long depth = increase_depth(data);
857
859
 
858
860
  if (RHASH_SIZE(obj) == 0) {
859
861
  fbuffer_append(buffer, "{}", 2);
860
- --state->depth;
862
+ --data->state->depth;
861
863
  return;
862
864
  }
863
865
 
@@ -869,49 +871,49 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
869
871
  };
870
872
  rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
871
873
 
872
- depth = --state->depth;
873
- if (RB_UNLIKELY(state->object_nl)) {
874
- fbuffer_append_str(buffer, state->object_nl);
875
- if (RB_UNLIKELY(state->indent)) {
874
+ depth = --data->state->depth;
875
+ if (RB_UNLIKELY(data->state->object_nl)) {
876
+ fbuffer_append_str(buffer, data->state->object_nl);
877
+ if (RB_UNLIKELY(data->state->indent)) {
876
878
  for (j = 0; j < depth; j++) {
877
- fbuffer_append_str(buffer, state->indent);
879
+ fbuffer_append_str(buffer, data->state->indent);
878
880
  }
879
881
  }
880
882
  }
881
883
  fbuffer_append_char(buffer, '}');
882
884
  }
883
885
 
884
- static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
886
+ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
885
887
  {
886
888
  int i, j;
887
- long depth = increase_depth(state);
889
+ long depth = increase_depth(data);
888
890
 
889
891
  if (RARRAY_LEN(obj) == 0) {
890
892
  fbuffer_append(buffer, "[]", 2);
891
- --state->depth;
893
+ --data->state->depth;
892
894
  return;
893
895
  }
894
896
 
895
897
  fbuffer_append_char(buffer, '[');
896
- if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
898
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
897
899
  for(i = 0; i < RARRAY_LEN(obj); i++) {
898
900
  if (i > 0) {
899
901
  fbuffer_append_char(buffer, ',');
900
- if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
902
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
901
903
  }
902
- if (RB_UNLIKELY(state->indent)) {
904
+ if (RB_UNLIKELY(data->state->indent)) {
903
905
  for (j = 0; j < depth; j++) {
904
- fbuffer_append_str(buffer, state->indent);
906
+ fbuffer_append_str(buffer, data->state->indent);
905
907
  }
906
908
  }
907
- generate_json(buffer, data, state, RARRAY_AREF(obj, i));
909
+ generate_json(buffer, data, RARRAY_AREF(obj, i));
908
910
  }
909
- state->depth = --depth;
910
- if (RB_UNLIKELY(state->array_nl)) {
911
- fbuffer_append_str(buffer, state->array_nl);
912
- if (RB_UNLIKELY(state->indent)) {
911
+ data->state->depth = --depth;
912
+ if (RB_UNLIKELY(data->state->array_nl)) {
913
+ fbuffer_append_str(buffer, data->state->array_nl);
914
+ if (RB_UNLIKELY(data->state->indent)) {
913
915
  for (j = 0; j < depth; j++) {
914
- fbuffer_append_str(buffer, state->indent);
916
+ fbuffer_append_str(buffer, data->state->indent);
915
917
  }
916
918
  }
917
919
  }
@@ -960,7 +962,7 @@ static inline VALUE ensure_valid_encoding(VALUE str)
960
962
  return str;
961
963
  }
962
964
 
963
- static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
965
+ static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
964
966
  {
965
967
  obj = ensure_valid_encoding(obj);
966
968
 
@@ -976,9 +978,9 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
976
978
  switch(rb_enc_str_coderange(obj)) {
977
979
  case ENC_CODERANGE_7BIT:
978
980
  case ENC_CODERANGE_VALID:
979
- if (RB_UNLIKELY(state->ascii_only)) {
980
- convert_UTF8_to_ASCII_only_JSON(&search, state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
981
- } else if (RB_UNLIKELY(state->script_safe)) {
981
+ if (RB_UNLIKELY(data->state->ascii_only)) {
982
+ convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
983
+ } else if (RB_UNLIKELY(data->state->script_safe)) {
982
984
  convert_UTF8_to_script_safe_JSON(&search);
983
985
  } else {
984
986
  convert_UTF8_to_JSON(&search);
@@ -991,7 +993,7 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
991
993
  fbuffer_append_char(buffer, '"');
992
994
  }
993
995
 
994
- static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
996
+ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
995
997
  {
996
998
  VALUE tmp;
997
999
  if (rb_respond_to(obj, i_to_json)) {
@@ -1001,100 +1003,117 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d
1001
1003
  } else {
1002
1004
  tmp = rb_funcall(obj, i_to_s, 0);
1003
1005
  Check_Type(tmp, T_STRING);
1004
- generate_json_string(buffer, data, state, tmp);
1006
+ generate_json_string(buffer, data, tmp);
1005
1007
  }
1006
1008
  }
1007
1009
 
1008
- static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1010
+ static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1009
1011
  {
1010
- if (state->strict) {
1011
- generate_json_string(buffer, data, state, rb_sym2str(obj));
1012
+ if (data->state->strict) {
1013
+ generate_json_string(buffer, data, rb_sym2str(obj));
1012
1014
  } else {
1013
- generate_json_fallback(buffer, data, state, obj);
1015
+ generate_json_fallback(buffer, data, obj);
1014
1016
  }
1015
1017
  }
1016
1018
 
1017
- static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1019
+ static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1018
1020
  {
1019
1021
  fbuffer_append(buffer, "null", 4);
1020
1022
  }
1021
1023
 
1022
- static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1024
+ static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1023
1025
  {
1024
1026
  fbuffer_append(buffer, "false", 5);
1025
1027
  }
1026
1028
 
1027
- static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1029
+ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1028
1030
  {
1029
1031
  fbuffer_append(buffer, "true", 4);
1030
1032
  }
1031
1033
 
1032
- static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1034
+ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1033
1035
  {
1034
1036
  fbuffer_append_long(buffer, FIX2LONG(obj));
1035
1037
  }
1036
1038
 
1037
- static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1039
+ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1038
1040
  {
1039
1041
  VALUE tmp = rb_funcall(obj, i_to_s, 0);
1040
1042
  fbuffer_append_str(buffer, tmp);
1041
1043
  }
1042
1044
 
1043
1045
  #ifdef RUBY_INTEGER_UNIFICATION
1044
- static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1046
+ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1045
1047
  {
1046
1048
  if (FIXNUM_P(obj))
1047
- generate_json_fixnum(buffer, data, state, obj);
1049
+ generate_json_fixnum(buffer, data, obj);
1048
1050
  else
1049
- generate_json_bignum(buffer, data, state, obj);
1051
+ generate_json_bignum(buffer, data, obj);
1050
1052
  }
1051
1053
  #endif
1052
1054
 
1053
- static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1055
+ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1054
1056
  {
1055
1057
  double value = RFLOAT_VALUE(obj);
1056
- char allow_nan = state->allow_nan;
1057
- if (!allow_nan) {
1058
- if (isinf(value) || isnan(value)) {
1059
- if (state->strict && state->as_json) {
1060
- VALUE casted_obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
1058
+ char allow_nan = data->state->allow_nan;
1059
+ if (isinf(value) || isnan(value)) {
1060
+ /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
1061
+ if (!allow_nan) {
1062
+ if (data->state->strict && data->state->as_json) {
1063
+ VALUE casted_obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1061
1064
  if (casted_obj != obj) {
1062
- increase_depth(state);
1063
- generate_json(buffer, data, state, casted_obj);
1064
- state->depth--;
1065
+ increase_depth(data);
1066
+ generate_json(buffer, data, casted_obj);
1067
+ data->state->depth--;
1065
1068
  return;
1066
1069
  }
1067
1070
  }
1068
1071
  raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
1069
1072
  }
1073
+
1074
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
1075
+ fbuffer_append_str(buffer, tmp);
1076
+ return;
1070
1077
  }
1071
- fbuffer_append_str(buffer, rb_funcall(obj, i_to_s, 0));
1078
+
1079
+ /* This implementation writes directly into the buffer. We reserve
1080
+ * the 24 characters that fpconv_dtoa states as its maximum, plus
1081
+ * 2 more characters for the potential ".0" suffix.
1082
+ */
1083
+ fbuffer_inc_capa(buffer, 26);
1084
+ char* d = buffer->ptr + buffer->len;
1085
+ int len = fpconv_dtoa(value, d);
1086
+
1087
+ /* fpconv_dtoa converts a float to its shortest string representation,
1088
+ * but it adds a ".0" if this is a plain integer.
1089
+ */
1090
+ buffer->len += len;
1072
1091
  }
1073
1092
 
1074
- static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1093
+ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1075
1094
  {
1076
1095
  VALUE fragment = RSTRUCT_GET(obj, 0);
1077
1096
  Check_Type(fragment, T_STRING);
1078
1097
  fbuffer_append_str(buffer, fragment);
1079
1098
  }
1080
1099
 
1081
- static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
1100
+ static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1082
1101
  {
1083
1102
  bool as_json_called = false;
1084
1103
  start:
1085
1104
  if (obj == Qnil) {
1086
- generate_json_null(buffer, data, state, obj);
1105
+ generate_json_null(buffer, data, obj);
1087
1106
  } else if (obj == Qfalse) {
1088
- generate_json_false(buffer, data, state, obj);
1107
+ generate_json_false(buffer, data, obj);
1089
1108
  } else if (obj == Qtrue) {
1090
- generate_json_true(buffer, data, state, obj);
1109
+ generate_json_true(buffer, data, obj);
1091
1110
  } else if (RB_SPECIAL_CONST_P(obj)) {
1092
1111
  if (RB_FIXNUM_P(obj)) {
1093
- generate_json_fixnum(buffer, data, state, obj);
1112
+ generate_json_fixnum(buffer, data, obj);
1094
1113
  } else if (RB_FLONUM_P(obj)) {
1095
- generate_json_float(buffer, data, state, obj);
1114
+ generate_json_float(buffer, data, obj);
1096
1115
  } else if (RB_STATIC_SYM_P(obj)) {
1097
- generate_json_symbol(buffer, data, state, obj);
1116
+ generate_json_symbol(buffer, data, obj);
1098
1117
  } else {
1099
1118
  goto general;
1100
1119
  }
@@ -1102,43 +1121,43 @@ start:
1102
1121
  VALUE klass = RBASIC_CLASS(obj);
1103
1122
  switch (RB_BUILTIN_TYPE(obj)) {
1104
1123
  case T_BIGNUM:
1105
- generate_json_bignum(buffer, data, state, obj);
1124
+ generate_json_bignum(buffer, data, obj);
1106
1125
  break;
1107
1126
  case T_HASH:
1108
1127
  if (klass != rb_cHash) goto general;
1109
- generate_json_object(buffer, data, state, obj);
1128
+ generate_json_object(buffer, data, obj);
1110
1129
  break;
1111
1130
  case T_ARRAY:
1112
1131
  if (klass != rb_cArray) goto general;
1113
- generate_json_array(buffer, data, state, obj);
1132
+ generate_json_array(buffer, data, obj);
1114
1133
  break;
1115
1134
  case T_STRING:
1116
1135
  if (klass != rb_cString) goto general;
1117
- generate_json_string(buffer, data, state, obj);
1136
+ generate_json_string(buffer, data, obj);
1118
1137
  break;
1119
1138
  case T_SYMBOL:
1120
- generate_json_symbol(buffer, data, state, obj);
1139
+ generate_json_symbol(buffer, data, obj);
1121
1140
  break;
1122
1141
  case T_FLOAT:
1123
1142
  if (klass != rb_cFloat) goto general;
1124
- generate_json_float(buffer, data, state, obj);
1143
+ generate_json_float(buffer, data, obj);
1125
1144
  break;
1126
1145
  case T_STRUCT:
1127
1146
  if (klass != cFragment) goto general;
1128
- generate_json_fragment(buffer, data, state, obj);
1147
+ generate_json_fragment(buffer, data, obj);
1129
1148
  break;
1130
1149
  default:
1131
1150
  general:
1132
- if (state->strict) {
1133
- if (RTEST(state->as_json) && !as_json_called) {
1134
- obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
1151
+ if (data->state->strict) {
1152
+ if (RTEST(data->state->as_json) && !as_json_called) {
1153
+ obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
1135
1154
  as_json_called = true;
1136
1155
  goto start;
1137
1156
  } else {
1138
1157
  raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
1139
1158
  }
1140
1159
  } else {
1141
- generate_json_fallback(buffer, data, state, obj);
1160
+ generate_json_fallback(buffer, data, obj);
1142
1161
  }
1143
1162
  }
1144
1163
  }
@@ -1148,7 +1167,7 @@ static VALUE generate_json_try(VALUE d)
1148
1167
  {
1149
1168
  struct generate_json_data *data = (struct generate_json_data *)d;
1150
1169
 
1151
- data->func(data->buffer, data, data->state, data->obj);
1170
+ data->func(data->buffer, data, data->obj);
1152
1171
 
1153
1172
  return Qnil;
1154
1173
  }
@@ -4,7 +4,6 @@ require 'mkmf'
4
4
  have_func("rb_enc_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
5
5
  have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
6
6
  have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
7
- have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby
8
7
  have_func("strnlen", "string.h") # Missing on Solaris 10
9
8
 
10
9
  append_cflags("-std=c99")