json 2.19.1 → 2.20.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 +4 -4
- data/CHANGES.md +56 -0
- data/LEGAL +3 -3
- data/README.md +11 -2
- data/ext/json/ext/fbuffer/fbuffer.h +32 -24
- data/ext/json/ext/generator/extconf.rb +3 -0
- data/ext/json/ext/generator/generator.c +45 -33
- data/ext/json/ext/json.h +74 -0
- data/ext/json/ext/parser/extconf.rb +37 -0
- data/ext/json/ext/parser/parser.c +1496 -349
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/lib/json/truffle_ruby/generator.rb +11 -8
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +18 -4
- metadata +3 -3
- data/ext/json/ext/vendor/ryu.h +0 -819
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24e3bc40d587f5a4c001b5f4c7da30170bdf06f5401f2aa1928469c8fb3860e2
|
|
4
|
+
data.tar.gz: b668789b5f3a56d7db311eb4ec9d10b6d236debab23016fabd1682316d50ae50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aaa62a65724e3caa24797ce93ed3ac710a0186a1fd2db990a20fddb10eb99c71d92a967d0bed9d5086cb5b203c5dda0793faa461fda1e5c6963c62da5b4db3dc
|
|
7
|
+
data.tar.gz: fc44e571dcb662a35a36165d7f7afbb94b11ab8d5660ab1a65905961b7548260152afbb3286fd233223ea5435b6bb62135a3b67b30bd26ca0cd8114e6921c980
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-06-23 (2.20.0)
|
|
6
|
+
|
|
7
|
+
* Both C and Java parsers are no longer recursive, so parsing very deep documents with `max_nesting: false` will no longer
|
|
8
|
+
result in `SystemStackError stack level too deep` errors.
|
|
9
|
+
* The `:max_nesting` option still defaults to `100`.
|
|
10
|
+
* Optimized floating point number parsing further by replacing the ryu algorithm by a port of Eisel-Lemire Fast Float.
|
|
11
|
+
* Added `JSON::ResumableParser` to parse streams of JSON documents. Not yet available on JRuby.
|
|
12
|
+
* Deprecate default support of JavaScript comments in the parser and add `allow_comments: true` parsing option.
|
|
13
|
+
* Integrate with Ruby 4.1 `ruby_sized_xfree`.
|
|
14
|
+
|
|
15
|
+
### 2026-06-11 (2.19.9)
|
|
16
|
+
|
|
17
|
+
* Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
|
|
18
|
+
with `JSON.generate(object, io)`. [CVE-2026-54696].
|
|
19
|
+
|
|
20
|
+
### 2026-06-03 (2.19.8)
|
|
21
|
+
|
|
22
|
+
* Fix 1-byte buffer overread on EOS errors.
|
|
23
|
+
* Handle invalid types passed as `max_nesting` option.
|
|
24
|
+
|
|
25
|
+
### 2026-05-28 (2.19.7)
|
|
26
|
+
|
|
27
|
+
* Fix some more edge cases with out of range floats.
|
|
28
|
+
* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
|
|
29
|
+
* Add missing write barriers in `State#dup`.
|
|
30
|
+
* Further validate generator `depth` config.
|
|
31
|
+
|
|
32
|
+
### 2026-05-28 (2.19.6)
|
|
33
|
+
|
|
34
|
+
* Cleanly handle overly large `depth` generator argument.
|
|
35
|
+
* Add missing write barrier in `ParserConfig`.
|
|
36
|
+
|
|
37
|
+
### 2026-05-04 (2.19.5)
|
|
38
|
+
|
|
39
|
+
* Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
|
|
40
|
+
|
|
41
|
+
### 2026-04-19 (2.19.4)
|
|
42
|
+
|
|
43
|
+
* Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
|
|
44
|
+
|
|
45
|
+
### 2026-03-25 (2.19.3)
|
|
46
|
+
|
|
47
|
+
* Fix handling of unescaped control characters preceeded by a backslash.
|
|
48
|
+
|
|
49
|
+
### 2026-03-18 (2.19.2)
|
|
50
|
+
|
|
51
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
52
|
+
|
|
5
53
|
### 2026-03-08 (2.19.1)
|
|
6
54
|
|
|
7
55
|
* Fix a compiler dependent GC bug introduced in `2.18.0`.
|
|
@@ -20,6 +68,10 @@
|
|
|
20
68
|
|
|
21
69
|
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
22
70
|
|
|
71
|
+
### 2026-03-18 (2.17.1.2) - Security Backport
|
|
72
|
+
|
|
73
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
74
|
+
|
|
23
75
|
### 2025-12-04 (2.17.1)
|
|
24
76
|
|
|
25
77
|
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
@@ -46,6 +98,10 @@
|
|
|
46
98
|
* Optimized numbers parsing using SWAR (thanks to Scott Myron).
|
|
47
99
|
* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
|
|
48
100
|
|
|
101
|
+
### 2026-03-18 (2.15.2.1) - Security Backport
|
|
102
|
+
|
|
103
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
104
|
+
|
|
49
105
|
### 2025-10-25 (2.15.2)
|
|
50
106
|
|
|
51
107
|
* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
|
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/
|
|
19
|
-
This file is adapted from the
|
|
20
|
-
It is
|
|
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
|
@@ -249,6 +249,17 @@ There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
|
|
|
249
249
|
`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
|
|
250
250
|
the `pp` library's `pp` methods.
|
|
251
251
|
|
|
252
|
+
## Security
|
|
253
|
+
|
|
254
|
+
When parsing or serializing untrusted input, parser and generator options should never be user controlled.
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
# Dangerous, DO NOT DO THIS.
|
|
258
|
+
JSON.generate(params[:data], params[:options])
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Security vulnerability reports relying on attacker controlled parsing or generator options will be handled as regular bug fixes.
|
|
262
|
+
|
|
252
263
|
## Development
|
|
253
264
|
|
|
254
265
|
### Prerequisites
|
|
@@ -295,5 +306,3 @@ The latest version of this library can be downloaded at
|
|
|
295
306
|
Online Documentation should be located at
|
|
296
307
|
|
|
297
308
|
* https://www.rubydoc.info/gems/json
|
|
298
|
-
|
|
299
|
-
[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
|
|
40
|
+
static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
|
|
41
41
|
{
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
|
86
|
+
static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
|
|
83
87
|
{
|
|
84
|
-
if (
|
|
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,
|
|
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
|
-
|
|
95
|
+
JSON_SIZED_REALLOC_N(fb->ptr, char, new_capa, fb->capa);
|
|
92
96
|
}
|
|
93
|
-
fb->capa =
|
|
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
|
|
101
|
-
fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
|
|
102
|
-
} else {
|
|
104
|
+
if (fb->capa != 0) {
|
|
103
105
|
fbuffer_flush(fb);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return;
|
|
106
|
+
if (RB_LIKELY(requested < fb->capa)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
size_t
|
|
112
|
+
size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
|
|
113
|
+
size_t needed_capa = requested + fb->len;
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
fb->capa = fb->initial_length;
|
|
115
|
+
while (new_capa < needed_capa) {
|
|
116
|
+
new_capa *= 2;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
|
|
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)
|
|
@@ -131,6 +130,15 @@ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
|
|
133
|
+
static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
|
|
134
|
+
{
|
|
135
|
+
size_t result = a * b;
|
|
136
|
+
if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
|
|
137
|
+
rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
|
|
135
143
|
{
|
|
136
144
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
@@ -175,7 +183,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
|
175
183
|
size_t len;
|
|
176
184
|
RSTRING_GETMEM(str, ptr, len);
|
|
177
185
|
|
|
178
|
-
fbuffer_inc_capa(fb, repeat
|
|
186
|
+
fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
|
|
179
187
|
while (repeat) {
|
|
180
188
|
#if JSON_DEBUG
|
|
181
189
|
fb->requested = len;
|
|
@@ -5,6 +5,9 @@ if RUBY_ENGINE == 'truffleruby'
|
|
|
5
5
|
File.write('Makefile', dummy_makefile("").join)
|
|
6
6
|
else
|
|
7
7
|
append_cflags("-std=c99")
|
|
8
|
+
have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
|
|
9
|
+
have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
|
|
10
|
+
|
|
8
11
|
$defs << "-DJSON_GENERATOR"
|
|
9
12
|
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
|
|
10
13
|
|
|
@@ -722,27 +722,24 @@ static void State_compact(void *ptr)
|
|
|
722
722
|
state->as_json = rb_gc_location(state->as_json);
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
-
static void State_free(void *ptr)
|
|
726
|
-
{
|
|
727
|
-
JSON_Generator_State *state = ptr;
|
|
728
|
-
ruby_xfree(state);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
725
|
static size_t State_memsize(const void *ptr)
|
|
732
726
|
{
|
|
727
|
+
#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
|
|
728
|
+
return 0;
|
|
729
|
+
#else
|
|
733
730
|
return sizeof(JSON_Generator_State);
|
|
731
|
+
#endif
|
|
734
732
|
}
|
|
735
733
|
|
|
736
734
|
static const rb_data_type_t JSON_Generator_State_type = {
|
|
737
|
-
"JSON/Generator/State",
|
|
738
|
-
{
|
|
735
|
+
.wrap_struct_name = "JSON/Generator/State",
|
|
736
|
+
.function = {
|
|
739
737
|
.dmark = State_mark,
|
|
740
|
-
.dfree =
|
|
738
|
+
.dfree = RUBY_DEFAULT_FREE,
|
|
741
739
|
.dsize = State_memsize,
|
|
742
740
|
.dcompact = State_compact,
|
|
743
741
|
},
|
|
744
|
-
|
|
745
|
-
RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
742
|
+
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
|
|
746
743
|
};
|
|
747
744
|
|
|
748
745
|
static void state_init(JSON_Generator_State *state)
|
|
@@ -826,9 +823,17 @@ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
|
826
823
|
return Qundef;
|
|
827
824
|
}
|
|
828
825
|
|
|
826
|
+
static inline int json_str_coderange(VALUE str) {
|
|
827
|
+
int coderange = RB_ENC_CODERANGE(str);
|
|
828
|
+
if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
|
|
829
|
+
coderange = rb_enc_str_coderange(str);
|
|
830
|
+
}
|
|
831
|
+
return coderange;
|
|
832
|
+
}
|
|
833
|
+
|
|
829
834
|
static inline bool valid_json_string_p(VALUE str)
|
|
830
835
|
{
|
|
831
|
-
int coderange =
|
|
836
|
+
int coderange = json_str_coderange(str);
|
|
832
837
|
|
|
833
838
|
if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
|
|
834
839
|
return true;
|
|
@@ -841,12 +846,8 @@ static inline bool valid_json_string_p(VALUE str)
|
|
|
841
846
|
return false;
|
|
842
847
|
}
|
|
843
848
|
|
|
844
|
-
static
|
|
849
|
+
NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
845
850
|
{
|
|
846
|
-
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
847
|
-
return str;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
851
|
if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
|
|
851
852
|
VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
|
|
852
853
|
if (coerced_str != str) {
|
|
@@ -882,6 +883,16 @@ static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE
|
|
|
882
883
|
return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
883
884
|
}
|
|
884
885
|
|
|
886
|
+
ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
887
|
+
{
|
|
888
|
+
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
889
|
+
return str;
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
return convert_invalid_encoding(data, str, as_json_called, is_key);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
885
896
|
static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
886
897
|
{
|
|
887
898
|
fbuffer_append_char(buffer, '"');
|
|
@@ -900,7 +911,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data
|
|
|
900
911
|
search.chunk_end = NULL;
|
|
901
912
|
#endif /* HAVE_SIMD */
|
|
902
913
|
|
|
903
|
-
switch (
|
|
914
|
+
switch (json_str_coderange(obj)) {
|
|
904
915
|
case ENC_CODERANGE_7BIT:
|
|
905
916
|
case ENC_CODERANGE_VALID:
|
|
906
917
|
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
@@ -1297,10 +1308,8 @@ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_fun
|
|
|
1297
1308
|
GET_STATE(self);
|
|
1298
1309
|
|
|
1299
1310
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1300
|
-
FBuffer buffer = {
|
|
1301
|
-
|
|
1302
|
-
};
|
|
1303
|
-
fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1311
|
+
FBuffer buffer = { 0 };
|
|
1312
|
+
fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1304
1313
|
|
|
1305
1314
|
struct generate_json_data data = {
|
|
1306
1315
|
.buffer = &buffer,
|
|
@@ -1360,12 +1369,14 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
|
1360
1369
|
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
|
|
1361
1370
|
|
|
1362
1371
|
MEMCPY(objState, origState, JSON_Generator_State, 1);
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1372
|
+
|
|
1373
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
|
|
1374
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space);
|
|
1375
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
|
|
1376
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
|
|
1377
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
|
|
1378
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
|
|
1379
|
+
|
|
1369
1380
|
return obj;
|
|
1370
1381
|
}
|
|
1371
1382
|
|
|
@@ -1572,7 +1583,7 @@ static VALUE cState_max_nesting(VALUE self)
|
|
|
1572
1583
|
|
|
1573
1584
|
static long long_config(VALUE num)
|
|
1574
1585
|
{
|
|
1575
|
-
return RTEST(num) ?
|
|
1586
|
+
return RTEST(num) ? NUM2LONG(num) : 0;
|
|
1576
1587
|
}
|
|
1577
1588
|
|
|
1578
1589
|
// depth must never be negative; reject early with a clear error.
|
|
@@ -1583,6 +1594,9 @@ static long depth_config(VALUE num)
|
|
|
1583
1594
|
if (RB_UNLIKELY(d < 0)) {
|
|
1584
1595
|
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1585
1596
|
}
|
|
1597
|
+
if (RB_UNLIKELY(d > INT_MAX)) {
|
|
1598
|
+
rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
|
|
1599
|
+
}
|
|
1586
1600
|
return d;
|
|
1587
1601
|
}
|
|
1588
1602
|
|
|
@@ -1854,10 +1868,8 @@ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io,
|
|
|
1854
1868
|
configure_state(&state, Qfalse, opts);
|
|
1855
1869
|
|
|
1856
1870
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1857
|
-
FBuffer buffer = {
|
|
1858
|
-
|
|
1859
|
-
};
|
|
1860
|
-
fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1871
|
+
FBuffer buffer = { 0 };
|
|
1872
|
+
fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1861
1873
|
|
|
1862
1874
|
struct generate_json_data data = {
|
|
1863
1875
|
.buffer = &buffer,
|
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,18 @@
|
|
|
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 UNDEF_P
|
|
35
|
+
#define UNDEF_P(val) (val == Qundef)
|
|
36
|
+
#endif
|
|
37
|
+
|
|
25
38
|
#if SIZEOF_UINT64_T == SIZEOF_LONG_LONG
|
|
26
39
|
# define INT64T2NUM(x) LL2NUM(x)
|
|
27
40
|
# define UINT64T2NUM(x) ULL2NUM(x)
|
|
@@ -49,11 +62,40 @@ typedef unsigned char _Bool;
|
|
|
49
62
|
#endif
|
|
50
63
|
#endif
|
|
51
64
|
|
|
65
|
+
#ifndef HAVE_RUBY_XFREE_SIZED
|
|
66
|
+
static inline void ruby_xfree_sized(void *ptr, size_t oldsize)
|
|
67
|
+
{
|
|
68
|
+
ruby_xfree(ptr);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static inline void *ruby_xrealloc2_sized(void *ptr, size_t new_elems, size_t elem_size, size_t old_elems)
|
|
72
|
+
{
|
|
73
|
+
return ruby_xrealloc2(ptr, new_elems, elem_size);
|
|
74
|
+
}
|
|
75
|
+
#endif
|
|
76
|
+
|
|
77
|
+
# define JSON_SIZED_REALLOC_N(v, T, m, n) \
|
|
78
|
+
((v) = (T *)ruby_xrealloc2_sized((void *)(v), (m), sizeof(T), (n)))
|
|
79
|
+
|
|
80
|
+
# define JSON_SIZED_FREE(v) ruby_xfree_sized((void *)(v), sizeof(*(v)))
|
|
81
|
+
# define JSON_SIZED_FREE_N(v, n) ruby_xfree_sized((void *)(v), sizeof(*(v)) * (n))
|
|
82
|
+
|
|
52
83
|
#ifndef HAVE_RB_EXT_RACTOR_SAFE
|
|
53
84
|
# undef RUBY_TYPED_FROZEN_SHAREABLE
|
|
54
85
|
# define RUBY_TYPED_FROZEN_SHAREABLE 0
|
|
55
86
|
#endif
|
|
56
87
|
|
|
88
|
+
#ifdef RUBY_TYPED_EMBEDDABLE
|
|
89
|
+
# define HAVE_RUBY_TYPED_EMBEDDABLE 1
|
|
90
|
+
#else
|
|
91
|
+
# ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE
|
|
92
|
+
# define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE
|
|
93
|
+
# define HAVE_RUBY_TYPED_EMBEDDABLE 1
|
|
94
|
+
# else
|
|
95
|
+
# define RUBY_TYPED_EMBEDDABLE 0
|
|
96
|
+
# endif
|
|
97
|
+
#endif
|
|
98
|
+
|
|
57
99
|
#ifndef NORETURN
|
|
58
100
|
#if defined(__has_attribute) && __has_attribute(noreturn)
|
|
59
101
|
#define NORETURN(x) __attribute__((noreturn)) x
|
|
@@ -102,4 +144,36 @@ typedef unsigned char _Bool;
|
|
|
102
144
|
#define JSON_CPU_LITTLE_ENDIAN_64BITS 0
|
|
103
145
|
#endif
|
|
104
146
|
|
|
147
|
+
#ifdef JSON_TRUFFLERUBY_RB_CATCH_BUG
|
|
148
|
+
|
|
149
|
+
#undef RB_BLOCK_CALL_FUNC_ARGLIST
|
|
150
|
+
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, func_args) VALUE func_args
|
|
151
|
+
|
|
152
|
+
NORETURN(static inline) void json_rb_throw_obj(VALUE tag, VALUE obj)
|
|
153
|
+
{
|
|
154
|
+
VALUE exc = rb_exc_new_str(rb_eException, rb_utf8_str_new_cstr("throw_workaround"));
|
|
155
|
+
rb_ivar_set(exc, rb_intern("@throw_tag"), tag);
|
|
156
|
+
rb_ivar_set(exc, rb_intern("@throw_obj"), obj);
|
|
157
|
+
rb_exc_raise(exc);
|
|
158
|
+
}
|
|
159
|
+
#define rb_throw_obj json_rb_throw_obj
|
|
160
|
+
|
|
161
|
+
static inline VALUE json_rb_catch_obj(VALUE tag, VALUE (*func)(VALUE args), VALUE func_args)
|
|
162
|
+
{
|
|
163
|
+
int status;
|
|
164
|
+
VALUE result = rb_protect(func, func_args, &status);
|
|
165
|
+
if (status) {
|
|
166
|
+
VALUE exc = rb_errinfo();
|
|
167
|
+
if (tag == rb_ivar_get(exc, rb_intern("@throw_tag"))) {
|
|
168
|
+
rb_set_errinfo(Qnil);
|
|
169
|
+
return rb_ivar_get(exc, rb_intern("@throw_obj"));
|
|
170
|
+
}
|
|
171
|
+
rb_jump_tag(status);
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
#define rb_catch_obj json_rb_catch_obj
|
|
176
|
+
|
|
177
|
+
#endif // JSON_TRUFFLERUBY_RB_CATCH_BUG
|
|
178
|
+
|
|
105
179
|
#endif // _JSON_H_
|
|
@@ -2,10 +2,47 @@
|
|
|
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
|
+
def have_builtin_func(name, check_expr, opt = "", &b)
|
|
19
|
+
checking_for checking_message(name.funcall_style, nil, opt) do
|
|
20
|
+
if try_compile(<<SRC, opt, &b)
|
|
21
|
+
int foo;
|
|
22
|
+
int main() { #{check_expr}; return 0; }
|
|
23
|
+
SRC
|
|
24
|
+
$defs.push(format("-DHAVE_BUILTIN_%s", name.tr_cpp))
|
|
25
|
+
true
|
|
26
|
+
else
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
have_builtin_func("__builtin_clzll", "__builtin_clzll(0)")
|
|
33
|
+
|
|
34
|
+
if have_header("x86intrin.h")
|
|
35
|
+
have_func("_lzcnt_u64", "x86intrin.h")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if have_header("intrin.h")
|
|
39
|
+
have_func("__lzcnt64", "intrin.h")
|
|
40
|
+
have_func("_BitScanReverse64", "intrin.h")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if RUBY_ENGINE == "ruby"
|
|
44
|
+
have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
|
|
45
|
+
end
|
|
9
46
|
|
|
10
47
|
append_cflags("-std=c99")
|
|
11
48
|
|