json 2.8.2 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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