json 2.19.8 → 2.21.1

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: 5f07e2772537eccf97069c6a7d7290aceba9a18c6b635e8e822bc7b92137c678
4
- data.tar.gz: cbb25a9d0e3434eba5d9fa2be9af5a69e1fe062c5113c461d854d129122adeed
3
+ metadata.gz: bd45f22d143e0edbca7ee9ba8209d10fd58f1b21f5e095d7a4c2938d12ea1041
4
+ data.tar.gz: 7104d723fe198b21508897a5583fcb513407595df0fdb0240c0343481cac7e06
5
5
  SHA512:
6
- metadata.gz: 2783e5483b100728ae73451008d3d5f880cc6e9dc663d773ed9dcdf27c23c3523c345f7ca70ae0e3687ab3b11262b380a7ecf455906d72b996f07eedbb1b14f8
7
- data.tar.gz: 84ab5ff3feb2d961b4bd5d759d444b006e8ccaa6802465203ed04a8718db43b4473859fdd613c106b0cc53d4e96d1da8b4a16550361f455964a4a1a75ae3cb23
6
+ metadata.gz: 554e73a89035e122a9643ecdd611ca893dbcfee74d86b9dd1ed8ea1fb2d0ddfc84416458efeff5b71925922e8e59c1c60aa90a172a411c6a33e3069a41e328d5
7
+ data.tar.gz: 4746639e1e86fff01e23ecbc036bae1410e6e944687ad576daf297f4d50f792334d0bef5a2864b0f2fe7e32495d7c205be2b666c811985f7168d0c2c8b1892f8
data/CHANGES.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### 2026-07-13 (2.21.1)
6
+
7
+ * Fix a compilation issue on Window and Microsoft Visual C++.
8
+
9
+ ### 2026-07-12 (2.21.0)
10
+
11
+ * `JSON.generate` now accept a `sort_keys` option, which takes either a boolean or a block.
12
+ * Added `#empty?` and `#partial_value?` methods on `JSON::ResumableParser`.
13
+ * Numerous correctness and performance fixes for `JSON::ResumableParser`.
14
+ * Avoid triggering Ruby's `float out of range` warning when parsing out of range numbers.
15
+ * Declare C types with Ruby 4.1 `RUBY_TYPED_THREAD_SAFE_FREE`.
16
+
17
+ ### 2026-06-23 (2.20.0)
18
+
19
+ * Both C and Java parsers are no longer recursive, so parsing very deep documents with `max_nesting: false` will no longer
20
+ result in `SystemStackError stack level too deep` errors.
21
+ * The `:max_nesting` option still defaults to `100`.
22
+ * Optimized floating point number parsing further by replacing the ryu algorithm by a port of Eisel-Lemire Fast Float.
23
+ * Added `JSON::ResumableParser` to parse streams of JSON documents. Not yet available on JRuby.
24
+ * Deprecate default support of JavaScript comments in the parser and add `allow_comments: true` parsing option.
25
+ * Integrate with Ruby 4.1 `ruby_sized_xfree`.
26
+
27
+ ### 2026-06-11 (2.19.9)
28
+
29
+ * Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
30
+ with `JSON.generate(object, io)`. [CVE-2026-54696].
31
+
5
32
  ### 2026-06-03 (2.19.8)
6
33
 
7
34
  * Fix 1-byte buffer overread on EOS errors.
data/LEGAL CHANGED
@@ -15,6 +15,6 @@ ext/json/ext/vendor/jeaiii-ltoa.h::
15
15
  This file is adapted from https://github.com/jeaiii/itoa
16
16
  It is licensed under the MIT License
17
17
 
18
- ext/json/ext/vendor/ryu.h::
19
- This file is adapted from the Ryu algorithm by Ulf Adams https://github.com/ulfjack/ryu.
20
- It is dual-licensed under Apache License 2.0 OR Boost Software License 1.0.
18
+ ext/json/ext/vendor/fast_float_parser.h::
19
+ This file is adapted from the Fast Float C++ library by The fast_float authors https://github.com/fastfloat/fast_float
20
+ It is licensed under the MIT License
data/README.md CHANGED
@@ -85,7 +85,7 @@ Both of these behavior can be disabled using the `strict: true` option:
85
85
 
86
86
  ```ruby
87
87
  JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
88
- JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError)
88
+ JSON.generate(Position.new(1, 2), strict: true) # => Position not allowed in JSON (JSON::GeneratorError)
89
89
  ```
90
90
 
91
91
  ## JSON::Coder
@@ -117,13 +117,13 @@ It is also called for objects that do have a JSON equivalent, but are used as Ha
117
117
  as well as for strings that aren't valid UTF-8:
118
118
 
119
119
  ```ruby
120
- coder = JSON::Combining.new do |object, is_object_key|
120
+ coder = JSON::Coder.new do |object, is_object_key|
121
121
  case object
122
122
  when String
123
- if !string.valid_encoding? || string.encoding != Encoding::UTF_8
124
- Base64.encode64(string)
123
+ if !object.valid_encoding? || object.encoding != Encoding::UTF_8
124
+ Base64.encode64(object)
125
125
  else
126
- string
126
+ object
127
127
  end
128
128
  else
129
129
  object
@@ -306,5 +306,3 @@ The latest version of this library can be downloaded at
306
306
  Online Documentation should be located at
307
307
 
308
308
  * https://www.rubydoc.info/gems/json
309
-
310
- [Ragel]: http://www.colm.net/open-source/ragel/
@@ -37,13 +37,17 @@ static void fbuffer_append_long(FBuffer *fb, long number);
37
37
  static inline void fbuffer_append_char(FBuffer *fb, char newchr);
38
38
  static VALUE fbuffer_finalize(FBuffer *fb);
39
39
 
40
- static void fbuffer_stack_init(FBuffer *fb, size_t initial_length, char *stack_buffer, size_t stack_buffer_size)
40
+ static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
41
41
  {
42
- fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
43
- if (stack_buffer) {
42
+ if (RTEST(io)) {
43
+ JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED);
44
+ fb->io = io;
45
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE;
46
+ } else {
44
47
  fb->type = FBUFFER_STACK_ALLOCATED;
45
48
  fb->ptr = stack_buffer;
46
49
  fb->capa = stack_buffer_size;
50
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
47
51
  }
48
52
  #if JSON_DEBUG
49
53
  fb->requested = 0;
@@ -64,7 +68,7 @@ static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
64
68
  static void fbuffer_free(FBuffer *fb)
65
69
  {
66
70
  if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
67
- ruby_xfree(fb->ptr);
71
+ JSON_SIZED_FREE_N(fb->ptr, fb->capa);
68
72
  }
69
73
  }
70
74
 
@@ -79,45 +83,40 @@ static void fbuffer_flush(FBuffer *fb)
79
83
  fbuffer_clear(fb);
80
84
  }
81
85
 
82
- static void fbuffer_realloc(FBuffer *fb, size_t required)
86
+ static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
83
87
  {
84
- if (required > fb->capa) {
88
+ if (new_capa > fb->capa) {
85
89
  if (fb->type == FBUFFER_STACK_ALLOCATED) {
86
90
  const char *old_buffer = fb->ptr;
87
- fb->ptr = ALLOC_N(char, required);
91
+ fb->ptr = ALLOC_N(char, new_capa);
88
92
  fb->type = FBUFFER_HEAP_ALLOCATED;
89
93
  MEMCPY(fb->ptr, old_buffer, char, fb->len);
90
94
  } else {
91
- REALLOC_N(fb->ptr, char, required);
95
+ JSON_SIZED_REALLOC_N(fb->ptr, char, new_capa, fb->capa);
92
96
  }
93
- fb->capa = required;
97
+ fb->capa = new_capa;
94
98
  }
95
99
  }
96
100
 
97
101
  static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
98
102
  {
99
103
  if (RB_UNLIKELY(fb->io)) {
100
- if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
101
- fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
102
- } else {
104
+ if (fb->capa != 0) {
103
105
  fbuffer_flush(fb);
104
- }
105
-
106
- if (RB_LIKELY(requested < fb->capa)) {
107
- return;
106
+ if (RB_LIKELY(requested < fb->capa)) {
107
+ return;
108
+ }
108
109
  }
109
110
  }
110
111
 
111
- size_t required;
112
+ size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
113
+ size_t needed_capa = requested + fb->len;
112
114
 
113
- if (RB_UNLIKELY(!fb->ptr)) {
114
- fb->ptr = ALLOC_N(char, fb->initial_length);
115
- fb->capa = fb->initial_length;
115
+ while (new_capa < needed_capa) {
116
+ new_capa *= 2;
116
117
  }
117
118
 
118
- for (required = fb->capa; requested > required - fb->len; required <<= 1);
119
-
120
- fbuffer_realloc(fb, required);
119
+ fbuffer_realloc(fb, new_capa);
121
120
  }
122
121
 
123
122
  static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
@@ -6,6 +6,7 @@ if RUBY_ENGINE == 'truffleruby'
6
6
  else
7
7
  append_cflags("-std=c99")
8
8
  have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
9
+ have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
9
10
 
10
11
  $defs << "-DJSON_GENERATOR"
11
12
  $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
@@ -34,13 +34,14 @@ typedef struct JSON_Generator_StateStruct {
34
34
  bool ascii_only;
35
35
  bool script_safe;
36
36
  bool strict;
37
+ VALUE sort_keys;
37
38
  } JSON_Generator_State;
38
39
 
39
- static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
40
+ static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc;
40
41
 
41
42
  static ID i_to_s, i_to_json, i_new, i_encode;
42
43
  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,
43
- sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
44
+ sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json, sym_sort_keys;
44
45
 
45
46
 
46
47
  #define GET_STATE_TO(self, state) \
@@ -709,6 +710,7 @@ static void State_mark(void *ptr)
709
710
  rb_gc_mark_movable(state->object_nl);
710
711
  rb_gc_mark_movable(state->array_nl);
711
712
  rb_gc_mark_movable(state->as_json);
713
+ rb_gc_mark_movable(state->sort_keys);
712
714
  }
713
715
 
714
716
  static void State_compact(void *ptr)
@@ -720,11 +722,16 @@ static void State_compact(void *ptr)
720
722
  state->object_nl = rb_gc_location(state->object_nl);
721
723
  state->array_nl = rb_gc_location(state->array_nl);
722
724
  state->as_json = rb_gc_location(state->as_json);
725
+ state->sort_keys = rb_gc_location(state->sort_keys);
723
726
  }
724
727
 
725
728
  static size_t State_memsize(const void *ptr)
726
729
  {
730
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
731
+ return 0;
732
+ #else
727
733
  return sizeof(JSON_Generator_State);
734
+ #endif
728
735
  }
729
736
 
730
737
  static const rb_data_type_t JSON_Generator_State_type = {
@@ -735,7 +742,7 @@ static const rb_data_type_t JSON_Generator_State_type = {
735
742
  .dsize = State_memsize,
736
743
  .dcompact = State_compact,
737
744
  },
738
- .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
745
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
739
746
  };
740
747
 
741
748
  static void state_init(JSON_Generator_State *state)
@@ -765,6 +772,7 @@ static void vstate_spill(struct generate_json_data *data)
765
772
  RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
766
773
  RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
767
774
  RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
775
+ RB_OBJ_WRITTEN(vstate, Qundef, state->sort_keys);
768
776
  }
769
777
 
770
778
  static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
@@ -1046,6 +1054,11 @@ static inline long increase_depth(struct generate_json_data *data)
1046
1054
 
1047
1055
  static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
1048
1056
  {
1057
+ if (RB_UNLIKELY(data->state->sort_keys)) {
1058
+ obj = rb_proc_call_with_block(data->state->sort_keys, 1, &obj, Qnil);
1059
+ Check_Type(obj, T_HASH);
1060
+ }
1061
+
1049
1062
  long depth = increase_depth(data);
1050
1063
 
1051
1064
  if (RHASH_SIZE(obj) == 0) {
@@ -1304,10 +1317,8 @@ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_fun
1304
1317
  GET_STATE(self);
1305
1318
 
1306
1319
  char stack_buffer[FBUFFER_STACK_SIZE];
1307
- FBuffer buffer = {
1308
- .io = RTEST(io) ? io : Qfalse,
1309
- };
1310
- fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1320
+ FBuffer buffer = { 0 };
1321
+ fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1311
1322
 
1312
1323
  struct generate_json_data data = {
1313
1324
  .buffer = &buffer,
@@ -1374,6 +1385,7 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
1374
1385
  RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
1375
1386
  RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
1376
1387
  RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
1388
+ RB_OBJ_WRITTEN(obj, Qundef, objState->sort_keys);
1377
1389
 
1378
1390
  return obj;
1379
1391
  }
@@ -1720,6 +1732,55 @@ static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
1720
1732
  return Qnil;
1721
1733
  }
1722
1734
 
1735
+ static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc)
1736
+ {
1737
+ if (!rb_obj_is_proc(proc)) {
1738
+ rb_raise(rb_eTypeError, "sort_key_proc must be a Proc");
1739
+ }
1740
+ return default_sort_keys_proc = proc;
1741
+ }
1742
+
1743
+ static VALUE normalize_sort_keys(VALUE value)
1744
+ {
1745
+ if (rb_obj_is_proc(value)) {
1746
+ return value;
1747
+ } else if (value == Qtrue) {
1748
+ return default_sort_keys_proc;
1749
+ } else if (RTEST(value)) {
1750
+ rb_raise(rb_eTypeError, "The `sort_keys` argument must be a boolean or a Proc");
1751
+ } else {
1752
+ return Qfalse;
1753
+ }
1754
+ }
1755
+
1756
+ /*
1757
+ * call-seq: sort_keys
1758
+ *
1759
+ * Get the value of sort_keys.
1760
+ */
1761
+ static VALUE cState_sort_keys_p(VALUE self)
1762
+ {
1763
+ GET_STATE(self);
1764
+ return state->sort_keys;
1765
+ }
1766
+
1767
+ /*
1768
+ * call-seq: sort_keys=(value)
1769
+ *
1770
+ * value is a boolean or a proc. If the value is the boolean true, object keys
1771
+ * will be sorted lexicographically in ascending order.
1772
+ *
1773
+ * If the value is a proc, it receives the entire Hash and must return a Hash
1774
+ * with its pairs in the desired order, allowing for arbitrary sorting.
1775
+ */
1776
+ static VALUE cState_sort_keys_set(VALUE self, VALUE value)
1777
+ {
1778
+ rb_check_frozen(self);
1779
+ GET_STATE(self);
1780
+ RB_OBJ_WRITE(self, &state->sort_keys, normalize_sort_keys(value));
1781
+ return Qnil;
1782
+ }
1783
+
1723
1784
  static VALUE cState_allow_duplicate_key_p(VALUE self)
1724
1785
  {
1725
1786
  GET_STATE(self);
@@ -1830,6 +1891,9 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1830
1891
  state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
1831
1892
  state_write_value(data, &state->as_json, proc);
1832
1893
  }
1894
+ else if (key == sym_sort_keys) {
1895
+ state_write_value(data, &state->sort_keys, normalize_sort_keys(val));
1896
+ }
1833
1897
  return ST_CONTINUE;
1834
1898
  }
1835
1899
 
@@ -1866,10 +1930,8 @@ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io,
1866
1930
  configure_state(&state, Qfalse, opts);
1867
1931
 
1868
1932
  char stack_buffer[FBUFFER_STACK_SIZE];
1869
- FBuffer buffer = {
1870
- .io = RTEST(io) ? io : Qfalse,
1871
- };
1872
- fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1933
+ FBuffer buffer = { 0 };
1934
+ fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1873
1935
 
1874
1936
  struct generate_json_data data = {
1875
1937
  .buffer = &buffer,
@@ -1909,6 +1971,8 @@ void Init_generator(void)
1909
1971
  VALUE mExt = rb_define_module_under(mJSON, "Ext");
1910
1972
  VALUE mGenerator = rb_define_module_under(mExt, "Generator");
1911
1973
 
1974
+ rb_global_variable(&default_sort_keys_proc);
1975
+
1912
1976
  rb_global_variable(&eGeneratorError);
1913
1977
  eGeneratorError = rb_path2class("JSON::GeneratorError");
1914
1978
 
@@ -1918,6 +1982,8 @@ void Init_generator(void)
1918
1982
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
1919
1983
  rb_define_alloc_func(cState, cState_s_allocate);
1920
1984
  rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
1985
+ rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1);
1986
+
1921
1987
  rb_define_method(cState, "initialize", cState_initialize, -1);
1922
1988
  rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
1923
1989
  rb_define_private_method(cState, "_configure", cState_configure, 1);
@@ -1957,6 +2023,8 @@ void Init_generator(void)
1957
2023
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
1958
2024
  rb_define_method(cState, "generate", cState_generate, -1);
1959
2025
  rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
2026
+ rb_define_method(cState, "sort_keys", cState_sort_keys_p, 0);
2027
+ rb_define_method(cState, "sort_keys=", cState_sort_keys_set, 1);
1960
2028
 
1961
2029
  rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
1962
2030
 
@@ -1986,6 +2054,7 @@ void Init_generator(void)
1986
2054
  sym_strict = ID2SYM(rb_intern("strict"));
1987
2055
  sym_as_json = ID2SYM(rb_intern("as_json"));
1988
2056
  sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
2057
+ sym_sort_keys = ID2SYM(rb_intern("sort_keys"));
1989
2058
 
1990
2059
  usascii_encindex = rb_usascii_encindex();
1991
2060
  utf8_encindex = rb_utf8_encindex();
data/ext/json/ext/json.h CHANGED
@@ -11,6 +11,9 @@
11
11
 
12
12
  #if defined(RUBY_DEBUG) && RUBY_DEBUG
13
13
  # define JSON_ASSERT RUBY_ASSERT
14
+ # ifndef JSON_DEBUG
15
+ # define JSON_DEBUG 1
16
+ # endif
14
17
  #else
15
18
  # ifdef JSON_DEBUG
16
19
  # include <assert.h>
@@ -20,8 +23,22 @@
20
23
  # endif
21
24
  #endif
22
25
 
26
+ #ifdef JSON_DEBUG
27
+ # define JSON_UNREACHABLE_RETURN(val) rb_bug("Unreachable")
28
+ #else
29
+ # define JSON_UNREACHABLE_RETURN UNREACHABLE_RETURN
30
+ #endif
31
+
23
32
  /* shims */
24
33
 
34
+ #ifndef RUBY_TYPED_THREAD_SAFE_FREE
35
+ #define RUBY_TYPED_THREAD_SAFE_FREE RUBY_TYPED_FREE_IMMEDIATELY
36
+ #endif
37
+
38
+ #ifndef UNDEF_P
39
+ #define UNDEF_P(val) (val == Qundef)
40
+ #endif
41
+
25
42
  #if SIZEOF_UINT64_T == SIZEOF_LONG_LONG
26
43
  # define INT64T2NUM(x) LL2NUM(x)
27
44
  # define UINT64T2NUM(x) ULL2NUM(x)
@@ -49,6 +66,24 @@ typedef unsigned char _Bool;
49
66
  #endif
50
67
  #endif
51
68
 
69
+ #ifndef HAVE_RUBY_XFREE_SIZED
70
+ static inline void ruby_xfree_sized(void *ptr, size_t oldsize)
71
+ {
72
+ ruby_xfree(ptr);
73
+ }
74
+
75
+ static inline void *ruby_xrealloc2_sized(void *ptr, size_t new_elems, size_t elem_size, size_t old_elems)
76
+ {
77
+ return ruby_xrealloc2(ptr, new_elems, elem_size);
78
+ }
79
+ #endif
80
+
81
+ # define JSON_SIZED_REALLOC_N(v, T, m, n) \
82
+ ((v) = (T *)ruby_xrealloc2_sized((void *)(v), (m), sizeof(T), (n)))
83
+
84
+ # define JSON_SIZED_FREE(v) ruby_xfree_sized((void *)(v), sizeof(*(v)))
85
+ # define JSON_SIZED_FREE_N(v, n) ruby_xfree_sized((void *)(v), sizeof(*(v)) * (n))
86
+
52
87
  #ifndef HAVE_RB_EXT_RACTOR_SAFE
53
88
  # undef RUBY_TYPED_FROZEN_SHAREABLE
54
89
  # define RUBY_TYPED_FROZEN_SHAREABLE 0
@@ -113,4 +148,36 @@ typedef unsigned char _Bool;
113
148
  #define JSON_CPU_LITTLE_ENDIAN_64BITS 0
114
149
  #endif
115
150
 
151
+ #ifdef JSON_TRUFFLERUBY_RB_CATCH_BUG
152
+
153
+ #undef RB_BLOCK_CALL_FUNC_ARGLIST
154
+ #define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, func_args) VALUE func_args
155
+
156
+ NORETURN(static inline) void json_rb_throw_obj(VALUE tag, VALUE obj)
157
+ {
158
+ VALUE exc = rb_exc_new_str(rb_eException, rb_utf8_str_new_cstr("throw_workaround"));
159
+ rb_ivar_set(exc, rb_intern("@throw_tag"), tag);
160
+ rb_ivar_set(exc, rb_intern("@throw_obj"), obj);
161
+ rb_exc_raise(exc);
162
+ }
163
+ #define rb_throw_obj json_rb_throw_obj
164
+
165
+ static inline VALUE json_rb_catch_obj(VALUE tag, VALUE (*func)(VALUE args), VALUE func_args)
166
+ {
167
+ int status;
168
+ VALUE result = rb_protect(func, func_args, &status);
169
+ if (status) {
170
+ VALUE exc = rb_errinfo();
171
+ if (tag == rb_ivar_get(exc, rb_intern("@throw_tag"))) {
172
+ rb_set_errinfo(Qnil);
173
+ return rb_ivar_get(exc, rb_intern("@throw_obj"));
174
+ }
175
+ rb_jump_tag(status);
176
+ }
177
+ return result;
178
+ }
179
+ #define rb_catch_obj json_rb_catch_obj
180
+
181
+ #endif // JSON_TRUFFLERUBY_RB_CATCH_BUG
182
+
116
183
  #endif // _JSON_H_
@@ -2,10 +2,27 @@
2
2
  require 'mkmf'
3
3
 
4
4
  $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
5
+
6
+ if RUBY_ENGINE == 'truffleruby' && RUBY_ENGINE_VERSION < '40.0'
7
+ # Ref: https://github.com/truffleruby/truffleruby/issues/4329
8
+ # Ref: https://github.com/truffleruby/truffleruby/pull/4333
9
+ $defs << "-DJSON_TRUFFLERUBY_RB_CATCH_BUG"
10
+ end
11
+
5
12
  have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0
6
13
  have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
7
14
  have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
8
15
  have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
16
+ have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
17
+
18
+ if have_header("x86intrin.h")
19
+ have_func("_lzcnt_u64", "x86intrin.h")
20
+ end
21
+
22
+ if have_header("intrin.h")
23
+ have_func("__lzcnt64", "intrin.h")
24
+ have_func("_BitScanReverse64", "intrin.h")
25
+ end
9
26
 
10
27
  if RUBY_ENGINE == "ruby"
11
28
  have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3