json 2.19.4 → 2.19.6

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: 48a111e63867f6bb2c0cb1b2740a898f3102bc07ac196136e26ca9b68f510f89
4
- data.tar.gz: 2f760d8afae80555c483afaaa8bead26f045d26c394cc07843a85deaff7fdd40
3
+ metadata.gz: d2f786d8fccfe7906f9ec68f2a8f1cb9b4fb458029b46e3bba7904a6e22e443f
4
+ data.tar.gz: 3fe8d97828cc573a98f4a0ccd88c2dc1fb008437dc115a1cd4d631b7a16f9df1
5
5
  SHA512:
6
- metadata.gz: 6387b2c483c8f4d7ed304f33a3553a2fde78c04efeff7ae32a1e952380df92d6df87b543c22832907b007f758f09bd2f80ef4db0b5753e46fdf73f0e264e9e02
7
- data.tar.gz: c3c855b9e460768f83516914580c10854ea2b28ee73976e311ce55a0d2265576f65a23c41cccc47c7443d79883658962d1cf923880685d555b3af1ba0ebcb056
6
+ metadata.gz: 6a39aec470a7ec7da944a8361ea219352f1216b3a4c4b2de7dd35fc87d4c791286f63b7744349ca4da607c37e53b1fecf450e62ef6cd02d0147eba0fb1edf5c2
7
+ data.tar.gz: 8cd952ae28ad7bc52f92ce49cd5cc6411835e3801a2c5cfc4a4b6d503a4bdb3dea3d726a4c4b2f7a75f0d3ec68f2d948bec52588cda37669c7d80ced0d527bb8
data/CHANGES.md CHANGED
@@ -2,9 +2,18 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### 2026-05-28 (2.19.6)
6
+
7
+ * Cleanly handle overly large `depth` generator argument.
8
+ * Add missing write barrier in `ParserConfig`.
9
+
10
+ ### 2026-05-04 (2.19.5)
11
+
12
+ * Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
13
+
5
14
  ### 2026-04-19 (2.19.4)
6
15
 
7
- * Fix parsing of out of range floats (very large exponents that lead ot either `0.0` or `Inf`).
16
+ * Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
8
17
 
9
18
  ### 2026-03-25 (2.19.3)
10
19
 
@@ -131,6 +131,15 @@ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
131
131
  }
132
132
  }
133
133
 
134
+ static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
135
+ {
136
+ size_t result = a * b;
137
+ if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
138
+ rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
139
+ }
140
+ return result;
141
+ }
142
+
134
143
  static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
135
144
  {
136
145
  MEMCPY(fb->ptr + fb->len, newstr, char, len);
@@ -175,7 +184,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
175
184
  size_t len;
176
185
  RSTRING_GETMEM(str, ptr, len);
177
186
 
178
- fbuffer_inc_capa(fb, repeat * len);
187
+ fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
179
188
  while (repeat) {
180
189
  #if JSON_DEBUG
181
190
  fb->requested = len;
@@ -819,9 +819,17 @@ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
819
819
  return Qundef;
820
820
  }
821
821
 
822
+ static inline int json_str_coderange(VALUE str) {
823
+ int coderange = RB_ENC_CODERANGE(str);
824
+ if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
825
+ coderange = rb_enc_str_coderange(str);
826
+ }
827
+ return coderange;
828
+ }
829
+
822
830
  static inline bool valid_json_string_p(VALUE str)
823
831
  {
824
- int coderange = rb_enc_str_coderange(str);
832
+ int coderange = json_str_coderange(str);
825
833
 
826
834
  if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
827
835
  return true;
@@ -834,12 +842,8 @@ static inline bool valid_json_string_p(VALUE str)
834
842
  return false;
835
843
  }
836
844
 
837
- static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
845
+ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
838
846
  {
839
- if (RB_LIKELY(valid_json_string_p(str))) {
840
- return str;
841
- }
842
-
843
847
  if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
844
848
  VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
845
849
  if (coerced_str != str) {
@@ -875,6 +879,16 @@ static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE
875
879
  return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
876
880
  }
877
881
 
882
+ ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
883
+ {
884
+ if (RB_LIKELY(valid_json_string_p(str))) {
885
+ return str;
886
+ }
887
+ else {
888
+ return convert_invalid_encoding(data, str, as_json_called, is_key);
889
+ }
890
+ }
891
+
878
892
  static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
879
893
  {
880
894
  fbuffer_append_char(buffer, '"');
@@ -893,7 +907,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data
893
907
  search.chunk_end = NULL;
894
908
  #endif /* HAVE_SIMD */
895
909
 
896
- switch (rb_enc_str_coderange(obj)) {
910
+ switch (json_str_coderange(obj)) {
897
911
  case ENC_CODERANGE_7BIT:
898
912
  case ENC_CODERANGE_VALID:
899
913
  if (RB_UNLIKELY(data->state->ascii_only)) {
@@ -364,6 +364,7 @@ typedef struct JSON_ParserStateStruct {
364
364
  rvalue_cache name_cache;
365
365
  int in_array;
366
366
  int current_nesting;
367
+ unsigned int emitted_deprecations;
367
368
  } JSON_ParserState;
368
369
 
369
370
  static inline size_t rest(JSON_ParserState *state) {
@@ -945,7 +946,12 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
945
946
  case JSON_IGNORE:
946
947
  break;
947
948
  case JSON_DEPRECATED:
948
- emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
949
+ // Only emit the first few deprecations to avoid spamming.
950
+ if (state->emitted_deprecations < 5) {
951
+ emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
952
+ state->emitted_deprecations++;
953
+ }
954
+
949
955
  break;
950
956
  case JSON_RAISE:
951
957
  raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
@@ -1465,9 +1471,22 @@ static VALUE convert_encoding(VALUE source)
1465
1471
  return rb_funcall(source, i_encode, 1, Encoding_UTF_8);
1466
1472
  }
1467
1473
 
1474
+ struct parser_config_init_args {
1475
+ JSON_ParserConfig *config;
1476
+ VALUE self;
1477
+ };
1478
+
1479
+ static void parser_config_wb_write(VALUE self, VALUE *dest, VALUE val)
1480
+ {
1481
+ *dest = val;
1482
+ if (self) RB_OBJ_WRITTEN(self, Qundef, val);
1483
+ }
1484
+
1468
1485
  static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1469
1486
  {
1470
- JSON_ParserConfig *config = (JSON_ParserConfig *)data;
1487
+ struct parser_config_init_args *args = (struct parser_config_init_args *)data;
1488
+ JSON_ParserConfig *config = args->config;
1489
+ VALUE self = args->self;
1471
1490
 
1472
1491
  if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
1473
1492
  else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
@@ -1476,15 +1495,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1476
1495
  else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
1477
1496
  else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
1478
1497
  else if (key == sym_freeze) { config->freeze = RTEST(val); }
1479
- else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
1498
+ else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
1480
1499
  else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1481
1500
  else if (key == sym_decimal_class) {
1482
1501
  if (RTEST(val)) {
1483
1502
  if (rb_respond_to(val, i_try_convert)) {
1484
- config->decimal_class = val;
1503
+ parser_config_wb_write(self, &config->decimal_class, val);
1485
1504
  config->decimal_method_id = i_try_convert;
1486
1505
  } else if (rb_respond_to(val, i_new)) {
1487
- config->decimal_class = val;
1506
+ parser_config_wb_write(self, &config->decimal_class, val);
1488
1507
  config->decimal_method_id = i_new;
1489
1508
  } else if (RB_TYPE_P(val, T_CLASS)) {
1490
1509
  VALUE name = rb_class_name(val);
@@ -1493,7 +1512,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1493
1512
  if (last_colon) {
1494
1513
  const char *mod_path_end = last_colon - 1;
1495
1514
  VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
1496
- config->decimal_class = rb_path_to_class(mod_path);
1515
+ parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path));
1497
1516
 
1498
1517
  const char *method_name_beg = last_colon + 1;
1499
1518
  long before_len = method_name_beg - name_cstr;
@@ -1501,7 +1520,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1501
1520
  VALUE method_name = rb_str_substr(name, before_len, len);
1502
1521
  config->decimal_method_id = SYM2ID(rb_str_intern(method_name));
1503
1522
  } else {
1504
- config->decimal_class = rb_mKernel;
1523
+ parser_config_wb_write(self, &config->decimal_class, rb_mKernel);
1505
1524
  config->decimal_method_id = SYM2ID(rb_str_intern(name));
1506
1525
  }
1507
1526
  }
@@ -1511,16 +1530,21 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1511
1530
  return ST_CONTINUE;
1512
1531
  }
1513
1532
 
1514
- static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
1533
+ static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self)
1515
1534
  {
1516
1535
  config->max_nesting = 100;
1517
1536
 
1537
+ struct parser_config_init_args args = {
1538
+ .config = config,
1539
+ .self = self,
1540
+ };
1541
+
1518
1542
  if (!NIL_P(opts)) {
1519
1543
  Check_Type(opts, T_HASH);
1520
1544
  if (RHASH_SIZE(opts) > 0) {
1521
1545
  // We assume in most cases few keys are set so it's faster to go over
1522
1546
  // the provided keys than to check all possible keys.
1523
- rb_hash_foreach(opts, parser_config_init_i, (VALUE)config);
1547
+ rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
1524
1548
  }
1525
1549
 
1526
1550
  }
@@ -1554,9 +1578,7 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
1554
1578
  rb_check_frozen(self);
1555
1579
  GET_PARSER_CONFIG;
1556
1580
 
1557
- parser_config_init(config, opts);
1558
-
1559
- RB_OBJ_WRITTEN(self, Qundef, config->decimal_class);
1581
+ parser_config_init(config, opts, self);
1560
1582
 
1561
1583
  return self;
1562
1584
  }
@@ -1618,7 +1640,7 @@ static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
1618
1640
 
1619
1641
  JSON_ParserConfig _config = {0};
1620
1642
  JSON_ParserConfig *config = &_config;
1621
- parser_config_init(config, opts);
1643
+ parser_config_init(config, opts, false);
1622
1644
 
1623
1645
  return cParser_parse(config, Vsource);
1624
1646
  }
data/lib/json/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSON
4
- VERSION = '2.19.4'
4
+ VERSION = '2.19.6'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.19.4
4
+ version: 2.19.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
86
  requirements: []
87
- rubygems_version: 4.0.6
87
+ rubygems_version: 4.0.10
88
88
  specification_version: 4
89
89
  summary: JSON Implementation for Ruby
90
90
  test_files: []