json 2.19.8 → 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 +15 -0
- data/LEGAL +3 -3
- data/README.md +0 -2
- data/ext/json/ext/fbuffer/fbuffer.h +22 -23
- data/ext/json/ext/generator/extconf.rb +1 -0
- data/ext/json/ext/generator/generator.c +8 -8
- data/ext/json/ext/json.h +63 -0
- data/ext/json/ext/parser/extconf.rb +33 -0
- data/ext/json/ext/parser/parser.c +1393 -323
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +16 -2
- metadata +2 -2
- 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,21 @@
|
|
|
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
|
+
|
|
5
20
|
### 2026-06-03 (2.19.8)
|
|
6
21
|
|
|
7
22
|
* 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/
|
|
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
|
@@ -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)
|
|
@@ -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"
|
|
@@ -724,7 +724,11 @@ static void State_compact(void *ptr)
|
|
|
724
724
|
|
|
725
725
|
static size_t State_memsize(const void *ptr)
|
|
726
726
|
{
|
|
727
|
+
#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
|
|
728
|
+
return 0;
|
|
729
|
+
#else
|
|
727
730
|
return sizeof(JSON_Generator_State);
|
|
731
|
+
#endif
|
|
728
732
|
}
|
|
729
733
|
|
|
730
734
|
static const rb_data_type_t JSON_Generator_State_type = {
|
|
@@ -1304,10 +1308,8 @@ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_fun
|
|
|
1304
1308
|
GET_STATE(self);
|
|
1305
1309
|
|
|
1306
1310
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1307
|
-
FBuffer buffer = {
|
|
1308
|
-
|
|
1309
|
-
};
|
|
1310
|
-
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);
|
|
1311
1313
|
|
|
1312
1314
|
struct generate_json_data data = {
|
|
1313
1315
|
.buffer = &buffer,
|
|
@@ -1866,10 +1868,8 @@ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io,
|
|
|
1866
1868
|
configure_state(&state, Qfalse, opts);
|
|
1867
1869
|
|
|
1868
1870
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1869
|
-
FBuffer buffer = {
|
|
1870
|
-
|
|
1871
|
-
};
|
|
1872
|
-
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);
|
|
1873
1873
|
|
|
1874
1874
|
struct generate_json_data data = {
|
|
1875
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,6 +62,24 @@ 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
|
|
@@ -113,4 +144,36 @@ typedef unsigned char _Bool;
|
|
|
113
144
|
#define JSON_CPU_LITTLE_ENDIAN_64BITS 0
|
|
114
145
|
#endif
|
|
115
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
|
+
|
|
116
179
|
#endif // _JSON_H_
|
|
@@ -2,10 +2,43 @@
|
|
|
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
|
|
9
42
|
|
|
10
43
|
if RUBY_ENGINE == "ruby"
|
|
11
44
|
have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
|