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 +4 -4
- data/CHANGES.md +27 -0
- data/LEGAL +3 -3
- data/README.md +5 -7
- 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 +80 -11
- data/ext/json/ext/json.h +67 -0
- data/ext/json/ext/parser/extconf.rb +17 -0
- data/ext/json/ext/parser/parser.c +1486 -326
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/lib/json/common.rb +9 -0
- data/lib/json/ext/generator/state.rb +1 -0
- data/lib/json/ext.rb +26 -0
- data/lib/json/truffle_ruby/generator.rb +36 -1
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +21 -3
- 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: bd45f22d143e0edbca7ee9ba8209d10fd58f1b21f5e095d7a4c2938d12ea1041
|
|
4
|
+
data.tar.gz: 7104d723fe198b21508897a5583fcb513407595df0fdb0240c0343481cac7e06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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/
|
|
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
|
@@ -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::
|
|
120
|
+
coder = JSON::Coder.new do |object, is_object_key|
|
|
121
121
|
case object
|
|
122
122
|
when String
|
|
123
|
-
if !
|
|
124
|
-
Base64.encode64(
|
|
123
|
+
if !object.valid_encoding? || object.encoding != Encoding::UTF_8
|
|
124
|
+
Base64.encode64(object)
|
|
125
125
|
else
|
|
126
|
-
|
|
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
|
|
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"
|
|
@@ -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 |
|
|
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
|
-
|
|
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
|
-
|
|
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
|