json 2.8.2 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61a5759c78160e412505a53cc6579e3259689fe69adbd74d5e4aefb21b7ef64c
4
- data.tar.gz: 072f37b6964a498df827e85e2038e4692c8452d5365a6c8794536508b1a95e9d
3
+ metadata.gz: 583ef6b3c8caf7e7fc9ad5ce6e60b1336ca23438cbd2ed998bea27ffbe8fbf38
4
+ data.tar.gz: 4ee0a25a7f0854aa99c0c79f9cdbd4038ac1a00884a840e49d119ab21b229ca1
5
5
  SHA512:
6
- metadata.gz: e65781ec7eb7ab0c5b48b12eaf8e988fd5632874d60c6c35450ba934c586ceb47b622adb5b6f2652dcd7a1641dedbb0a674d1b458839d45944c862671d74d33f
7
- data.tar.gz: 19b1ccd3cdf1126ac3825993a3c4aa06eff96b5ab80e0391ee3a150c4dd164d53052fa89aea0aeabd53e1bfbf4f513fc5276471c7eff1231e38f1ad89f1df1d7
6
+ metadata.gz: 6ab5ed9d48b51602d00ace1657947c82bf3bb233eb163baa93b582c32c9eb1cc23dec69e88ed94cb26fcabf6dbb7c355f0b67696186345e911beb9736e504aa0
7
+ data.tar.gz: 7fb0db017a55c3598004d7e5f640959ff50dc37a7e1ebe9a9e04462ad76f806bd599f7468891b612eaa4648a0c33ca05c3c9183c24489204128f49b2ef149652
data/CHANGES.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changes
2
2
 
3
+ ### 2024-12-03 (2.9.0)
4
+
5
+ * Fix C implementation of `script_safe` escaping to not confuse some other 3 wide characters with `\u2028` and `\u2029`.
6
+ e.g. `JSON.generate(["倩", "瀨"], script_safe: true)` would generate the wrong JSON.
7
+ * `JSON.dump(object, some_io)` now write into the IO in chunks while previously it would buffer the entire JSON before writing.
8
+ * `JSON::GeneratorError` now has a `#invalid_object` attribute, making it easier to understand why an object tree cannot be serialized.
9
+ * Numerous improvements to the JRuby extension.
10
+
3
11
  ### 2024-11-14 (2.8.2)
4
12
 
5
13
  * `JSON.load_file` explictly read the file as UTF-8.
@@ -18,6 +26,7 @@
18
26
  pre-existing support for comments, make it suitable to parse `jsonc` documents.
19
27
  * Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
20
28
  * Some minor performance improvements to `JSON.dump` and `JSON.generate`.
29
+ * `JSON.pretty_generate` no longer include newline inside empty object and arrays.
21
30
 
22
31
  ### 2024-11-04 (2.7.6)
23
32
 
@@ -46,9 +46,11 @@ typedef struct FBufferStruct {
46
46
  unsigned long len;
47
47
  unsigned long capa;
48
48
  char *ptr;
49
+ VALUE io;
49
50
  } FBuffer;
50
51
 
51
52
  #define FBUFFER_STACK_SIZE 512
53
+ #define FBUFFER_IO_BUFFER_SIZE (16384 - 1)
52
54
  #define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
53
55
 
54
56
  #define FBUFFER_PTR(fb) ((fb)->ptr)
@@ -66,7 +68,7 @@ static void fbuffer_append_long(FBuffer *fb, long number);
66
68
  #endif
67
69
  static inline void fbuffer_append_char(FBuffer *fb, char newchr);
68
70
  #ifdef JSON_GENERATOR
69
- static VALUE fbuffer_to_s(FBuffer *fb);
71
+ static VALUE fbuffer_finalize(FBuffer *fb);
70
72
  #endif
71
73
 
72
74
  static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
@@ -86,24 +88,19 @@ static void fbuffer_free(FBuffer *fb)
86
88
  }
87
89
  }
88
90
 
89
- #ifndef JSON_GENERATOR
90
91
  static void fbuffer_clear(FBuffer *fb)
91
92
  {
92
93
  fb->len = 0;
93
94
  }
94
- #endif
95
95
 
96
- static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
96
+ static void fbuffer_flush(FBuffer *fb)
97
97
  {
98
- unsigned long required;
99
-
100
- if (RB_UNLIKELY(!fb->ptr)) {
101
- fb->ptr = ALLOC_N(char, fb->initial_length);
102
- fb->capa = fb->initial_length;
103
- }
104
-
105
- for (required = fb->capa; requested > required - fb->len; required <<= 1);
98
+ rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len));
99
+ fbuffer_clear(fb);
100
+ }
106
101
 
102
+ static void fbuffer_realloc(FBuffer *fb, unsigned long required)
103
+ {
107
104
  if (required > fb->capa) {
108
105
  if (fb->type == FBUFFER_STACK_ALLOCATED) {
109
106
  const char *old_buffer = fb->ptr;
@@ -117,6 +114,32 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
117
114
  }
118
115
  }
119
116
 
117
+ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
118
+ {
119
+ if (RB_UNLIKELY(fb->io)) {
120
+ if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
121
+ fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
122
+ } else {
123
+ fbuffer_flush(fb);
124
+ }
125
+
126
+ if (RB_LIKELY(requested < fb->capa)) {
127
+ return;
128
+ }
129
+ }
130
+
131
+ unsigned long required;
132
+
133
+ if (RB_UNLIKELY(!fb->ptr)) {
134
+ fb->ptr = ALLOC_N(char, fb->initial_length);
135
+ fb->capa = fb->initial_length;
136
+ }
137
+
138
+ for (required = fb->capa; requested > required - fb->len; required <<= 1);
139
+
140
+ fbuffer_realloc(fb, required);
141
+ }
142
+
120
143
  static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
121
144
  {
122
145
  if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
@@ -174,11 +197,18 @@ static void fbuffer_append_long(FBuffer *fb, long number)
174
197
  fbuffer_append(fb, buffer_end - len, len);
175
198
  }
176
199
 
177
- static VALUE fbuffer_to_s(FBuffer *fb)
200
+ static VALUE fbuffer_finalize(FBuffer *fb)
178
201
  {
179
- VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
180
- fbuffer_free(fb);
181
- return result;
202
+ if (fb->io) {
203
+ fbuffer_flush(fb);
204
+ fbuffer_free(fb);
205
+ rb_io_flush(fb->io);
206
+ return fb->io;
207
+ } else {
208
+ VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
209
+ fbuffer_free(fb);
210
+ return result;
211
+ }
182
212
  }
183
213
  #endif
184
214
  #endif
@@ -54,7 +54,7 @@ struct generate_json_data {
54
54
  };
55
55
 
56
56
  static VALUE cState_from_state_s(VALUE self, VALUE opts);
57
- static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func);
57
+ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
58
58
  static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
59
59
  static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
60
60
  static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
@@ -71,6 +71,31 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
71
71
 
72
72
  static int usascii_encindex, utf8_encindex, binary_encindex;
73
73
 
74
+ #ifdef RBIMPL_ATTR_NORETURN
75
+ RBIMPL_ATTR_NORETURN()
76
+ #endif
77
+ static void raise_generator_error_str(VALUE invalid_object, VALUE str)
78
+ {
79
+ VALUE exc = rb_exc_new_str(eGeneratorError, str);
80
+ rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
81
+ rb_exc_raise(exc);
82
+ }
83
+
84
+ #ifdef RBIMPL_ATTR_NORETURN
85
+ RBIMPL_ATTR_NORETURN()
86
+ #endif
87
+ #ifdef RBIMPL_ATTR_FORMAT
88
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
89
+ #endif
90
+ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
91
+ {
92
+ va_list args;
93
+ va_start(args, fmt);
94
+ VALUE str = rb_vsprintf(fmt, args);
95
+ va_end(args);
96
+ raise_generator_error_str(invalid_object, str);
97
+ }
98
+
74
99
  /* Converts in_string to a JSON string (without the wrapping '"'
75
100
  * characters) in FBuffer out_buffer.
76
101
  *
@@ -130,7 +155,7 @@ static void convert_UTF8_to_JSON(FBuffer *out_buffer, VALUE str, const char esca
130
155
  }
131
156
  case 3: {
132
157
  unsigned char b2 = ptr[pos + 1];
133
- if (RB_UNLIKELY(out_script_safe && b2 == 0x80)) {
158
+ if (RB_UNLIKELY(out_script_safe && ch == 0xE2 && b2 == 0x80)) {
134
159
  unsigned char b3 = ptr[pos + 2];
135
160
  if (b3 == 0xA8) {
136
161
  FLUSH_POS(3);
@@ -453,7 +478,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
453
478
  {
454
479
  rb_check_arity(argc, 0, 1);
455
480
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
456
- return cState_partial_generate(Vstate, self, generate_json_object);
481
+ return cState_partial_generate(Vstate, self, generate_json_object, Qfalse);
457
482
  }
458
483
 
459
484
  /*
@@ -467,7 +492,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
467
492
  static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
468
493
  rb_check_arity(argc, 0, 1);
469
494
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
470
- return cState_partial_generate(Vstate, self, generate_json_array);
495
+ return cState_partial_generate(Vstate, self, generate_json_array, Qfalse);
471
496
  }
472
497
 
473
498
  #ifdef RUBY_INTEGER_UNIFICATION
@@ -480,7 +505,7 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
480
505
  {
481
506
  rb_check_arity(argc, 0, 1);
482
507
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
483
- return cState_partial_generate(Vstate, self, generate_json_integer);
508
+ return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse);
484
509
  }
485
510
 
486
511
  #else
@@ -493,7 +518,7 @@ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
493
518
  {
494
519
  rb_check_arity(argc, 0, 1);
495
520
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
496
- return cState_partial_generate(Vstate, self, generate_json_fixnum);
521
+ return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse);
497
522
  }
498
523
 
499
524
  /*
@@ -505,7 +530,7 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
505
530
  {
506
531
  rb_check_arity(argc, 0, 1);
507
532
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
508
- return cState_partial_generate(Vstate, self, generate_json_bignum);
533
+ return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse);
509
534
  }
510
535
  #endif
511
536
 
@@ -518,7 +543,7 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
518
543
  {
519
544
  rb_check_arity(argc, 0, 1);
520
545
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
521
- return cState_partial_generate(Vstate, self, generate_json_float);
546
+ return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
522
547
  }
523
548
 
524
549
  /*
@@ -543,7 +568,7 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
543
568
  {
544
569
  rb_check_arity(argc, 0, 1);
545
570
  VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
546
- return cState_partial_generate(Vstate, self, generate_json_string);
571
+ return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
547
572
  }
548
573
 
549
574
  /*
@@ -638,7 +663,7 @@ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
638
663
  rb_scan_args(argc, argv, "01", &state);
639
664
  Check_Type(string, T_STRING);
640
665
  state = cState_from_state_s(cState, state);
641
- return cState_partial_generate(state, string, generate_json_string);
666
+ return cState_partial_generate(state, string, generate_json_string, Qfalse);
642
667
  }
643
668
 
644
669
  static void State_mark(void *ptr)
@@ -867,6 +892,17 @@ static inline int enc_utf8_compatible_p(int enc_idx)
867
892
  return 0;
868
893
  }
869
894
 
895
+ static VALUE encode_json_string_try(VALUE str)
896
+ {
897
+ return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
898
+ }
899
+
900
+ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
901
+ {
902
+ raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
903
+ return Qundef;
904
+ }
905
+
870
906
  static inline VALUE ensure_valid_encoding(VALUE str)
871
907
  {
872
908
  int encindex = RB_ENCODING_GET(str);
@@ -886,7 +922,7 @@ static inline VALUE ensure_valid_encoding(VALUE str)
886
922
  }
887
923
  }
888
924
 
889
- str = rb_funcall(str, i_encode, 1, Encoding_UTF_8);
925
+ str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
890
926
  }
891
927
  return str;
892
928
  }
@@ -909,7 +945,7 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
909
945
  }
910
946
  break;
911
947
  default:
912
- rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed utf-8");
948
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
913
949
  break;
914
950
  }
915
951
  fbuffer_append_char(buffer, '"');
@@ -957,10 +993,8 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
957
993
  char allow_nan = state->allow_nan;
958
994
  VALUE tmp = rb_funcall(obj, i_to_s, 0);
959
995
  if (!allow_nan) {
960
- if (isinf(value)) {
961
- rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", tmp);
962
- } else if (isnan(value)) {
963
- rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", tmp);
996
+ if (isinf(value) || isnan(value)) {
997
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", tmp);
964
998
  }
965
999
  }
966
1000
  fbuffer_append_str(buffer, tmp);
@@ -1008,7 +1042,7 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
1008
1042
  default:
1009
1043
  general:
1010
1044
  if (state->strict) {
1011
- rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
1045
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
1012
1046
  } else if (rb_respond_to(obj, i_to_json)) {
1013
1047
  tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
1014
1048
  Check_Type(tmp, T_STRING);
@@ -1036,21 +1070,19 @@ static VALUE generate_json_rescue(VALUE d, VALUE exc)
1036
1070
  struct generate_json_data *data = (struct generate_json_data *)d;
1037
1071
  fbuffer_free(data->buffer);
1038
1072
 
1039
- if (RBASIC_CLASS(exc) == rb_path2class("Encoding::UndefinedConversionError")) {
1040
- exc = rb_exc_new_str(eGeneratorError, rb_funcall(exc, rb_intern("message"), 0));
1041
- }
1042
-
1043
1073
  rb_exc_raise(exc);
1044
1074
 
1045
1075
  return Qundef;
1046
1076
  }
1047
1077
 
1048
- static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func)
1078
+ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
1049
1079
  {
1050
1080
  GET_STATE(self);
1051
1081
 
1052
1082
  char stack_buffer[FBUFFER_STACK_SIZE];
1053
- FBuffer buffer = {0};
1083
+ FBuffer buffer = {
1084
+ .io = RTEST(io) ? io : Qfalse,
1085
+ };
1054
1086
  fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1055
1087
 
1056
1088
  struct generate_json_data data = {
@@ -1062,19 +1094,12 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func)
1062
1094
  };
1063
1095
  rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1064
1096
 
1065
- return fbuffer_to_s(&buffer);
1097
+ return fbuffer_finalize(&buffer);
1066
1098
  }
1067
1099
 
1068
- /*
1069
- * call-seq: generate(obj)
1070
- *
1071
- * Generates a valid JSON document from object +obj+ and returns the
1072
- * result. If no valid JSON document can be created this method raises a
1073
- * GeneratorError exception.
1074
- */
1075
- static VALUE cState_generate(VALUE self, VALUE obj)
1100
+ static VALUE cState_generate(VALUE self, VALUE obj, VALUE io)
1076
1101
  {
1077
- VALUE result = cState_partial_generate(self, obj, generate_json);
1102
+ VALUE result = cState_partial_generate(self, obj, generate_json, io);
1078
1103
  GET_STATE(self);
1079
1104
  (void)state;
1080
1105
  return result;
@@ -1502,14 +1527,16 @@ static VALUE cState_configure(VALUE self, VALUE opts)
1502
1527
  return self;
1503
1528
  }
1504
1529
 
1505
- static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts)
1530
+ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
1506
1531
  {
1507
1532
  JSON_Generator_State state = {0};
1508
1533
  state_init(&state);
1509
1534
  configure_state(&state, opts);
1510
1535
 
1511
1536
  char stack_buffer[FBUFFER_STACK_SIZE];
1512
- FBuffer buffer = {0};
1537
+ FBuffer buffer = {
1538
+ .io = RTEST(io) ? io : Qfalse,
1539
+ };
1513
1540
  fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1514
1541
 
1515
1542
  struct generate_json_data data = {
@@ -1521,7 +1548,7 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts)
1521
1548
  };
1522
1549
  rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1523
1550
 
1524
- return fbuffer_to_s(&buffer);
1551
+ return fbuffer_finalize(&buffer);
1525
1552
  }
1526
1553
 
1527
1554
  /*
@@ -1540,10 +1567,11 @@ void Init_generator(void)
1540
1567
  VALUE mExt = rb_define_module_under(mJSON, "Ext");
1541
1568
  VALUE mGenerator = rb_define_module_under(mExt, "Generator");
1542
1569
 
1570
+ rb_global_variable(&eGeneratorError);
1543
1571
  eGeneratorError = rb_path2class("JSON::GeneratorError");
1572
+
1573
+ rb_global_variable(&eNestingError);
1544
1574
  eNestingError = rb_path2class("JSON::NestingError");
1545
- rb_gc_register_mark_object(eGeneratorError);
1546
- rb_gc_register_mark_object(eNestingError);
1547
1575
 
1548
1576
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
1549
1577
  rb_define_alloc_func(cState, cState_s_allocate);
@@ -1583,9 +1611,9 @@ void Init_generator(void)
1583
1611
  rb_define_method(cState, "depth=", cState_depth_set, 1);
1584
1612
  rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
1585
1613
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
1586
- rb_define_method(cState, "generate", cState_generate, 1);
1614
+ rb_define_private_method(cState, "_generate", cState_generate, 2);
1587
1615
 
1588
- rb_define_singleton_method(cState, "generate", cState_m_generate, 2);
1616
+ rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
1589
1617
 
1590
1618
  VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
1591
1619
 
@@ -3,7 +3,6 @@ require 'mkmf'
3
3
 
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
- have_func("rb_gc_mark_locations", "ruby.h") # Missing on TruffleRuby
7
6
  have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
8
7
  have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby
9
8