json 2.16.0 → 2.18.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 +20 -0
- data/ext/json/ext/fbuffer/fbuffer.h +6 -6
- data/ext/json/ext/generator/extconf.rb +1 -1
- data/ext/json/ext/generator/generator.c +41 -61
- data/ext/json/ext/json.h +5 -0
- data/ext/json/ext/parser/extconf.rb +1 -2
- data/ext/json/ext/parser/parser.c +125 -87
- data/ext/json/ext/simd/simd.h +5 -5
- data/ext/json/ext/vendor/fpconv.c +2 -2
- data/lib/json/common.rb +21 -4
- data/lib/json/truffle_ruby/generator.rb +21 -17
- data/lib/json/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51eab66896e862b679d424133f11e1367d5d8e71add943e67cf0673d0d562fcd
|
|
4
|
+
data.tar.gz: 7b69d4a42137897fe9a45bd60a21b759d133c112cb4ba16020099f27074ac2fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ea3b026c8ccd6cb477858bf06f07f8b5adc5bcf7b52a175487c19dc2835ef63db2e4f87074a00ec2fe2c70e588c205f679116536da40f15e767f35351a52fc5c
|
|
7
|
+
data.tar.gz: f58144a5329ad95128e00bbc5670280f6a699e04cf05060bdfc7acaf112e62eb44c14c36328a9683d86e138c6eb2f3599f5ec35347867ac99ab9f0e16813c4df
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2025-12-11 (2.18.0)
|
|
6
|
+
|
|
7
|
+
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
8
|
+
|
|
9
|
+
### 2025-12-04 (2.17.1)
|
|
10
|
+
|
|
11
|
+
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
12
|
+
|
|
13
|
+
### 2025-12-03 (2.17.0)
|
|
14
|
+
|
|
15
|
+
* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
|
|
16
|
+
* Fix the parser to no longer ignore invalid escapes in strings.
|
|
17
|
+
Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
|
|
18
|
+
* Fixed `JSON::Coder` to use the depth it was initialized with.
|
|
19
|
+
* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
|
|
20
|
+
* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
|
|
21
|
+
automatically to its initial value.
|
|
22
|
+
In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
|
|
23
|
+
generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
|
|
24
|
+
|
|
5
25
|
### 2025-11-07 (2.16.0)
|
|
6
26
|
|
|
7
27
|
* Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
|
|
@@ -14,7 +14,7 @@ typedef struct FBufferStruct {
|
|
|
14
14
|
unsigned long initial_length;
|
|
15
15
|
unsigned long len;
|
|
16
16
|
unsigned long capa;
|
|
17
|
-
#
|
|
17
|
+
#if JSON_DEBUG
|
|
18
18
|
unsigned long requested;
|
|
19
19
|
#endif
|
|
20
20
|
char *ptr;
|
|
@@ -45,14 +45,14 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
|
|
|
45
45
|
fb->ptr = stack_buffer;
|
|
46
46
|
fb->capa = stack_buffer_size;
|
|
47
47
|
}
|
|
48
|
-
#
|
|
48
|
+
#if JSON_DEBUG
|
|
49
49
|
fb->requested = 0;
|
|
50
50
|
#endif
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
|
|
54
54
|
{
|
|
55
|
-
#
|
|
55
|
+
#if JSON_DEBUG
|
|
56
56
|
if (consumed > fb->requested) {
|
|
57
57
|
rb_bug("fbuffer: Out of bound write");
|
|
58
58
|
}
|
|
@@ -122,7 +122,7 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
122
122
|
|
|
123
123
|
static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|
124
124
|
{
|
|
125
|
-
#
|
|
125
|
+
#if JSON_DEBUG
|
|
126
126
|
fb->requested = requested;
|
|
127
127
|
#endif
|
|
128
128
|
|
|
@@ -148,7 +148,7 @@ static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long
|
|
|
148
148
|
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
|
149
149
|
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
150
150
|
{
|
|
151
|
-
#
|
|
151
|
+
#if JSON_DEBUG
|
|
152
152
|
if (fb->requested < 1) {
|
|
153
153
|
rb_bug("fbuffer: unreserved write");
|
|
154
154
|
}
|
|
@@ -174,7 +174,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
|
174
174
|
|
|
175
175
|
fbuffer_inc_capa(fb, repeat * len);
|
|
176
176
|
while (repeat) {
|
|
177
|
-
#
|
|
177
|
+
#if JSON_DEBUG
|
|
178
178
|
fb->requested = len;
|
|
179
179
|
#endif
|
|
180
180
|
fbuffer_append_reserved(fb, newstr, len);
|
|
@@ -6,7 +6,7 @@ if RUBY_ENGINE == 'truffleruby'
|
|
|
6
6
|
else
|
|
7
7
|
append_cflags("-std=c99")
|
|
8
8
|
$defs << "-DJSON_GENERATOR"
|
|
9
|
-
$defs << "-DJSON_DEBUG" if ENV
|
|
9
|
+
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
|
|
10
10
|
|
|
11
11
|
if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
|
|
12
12
|
load __dir__ + "/../simd/conf.rb"
|
|
@@ -38,7 +38,7 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
38
38
|
|
|
39
39
|
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
40
40
|
|
|
41
|
-
static ID i_to_s, i_to_json, i_new,
|
|
41
|
+
static ID i_to_s, i_to_json, i_new, i_encode;
|
|
42
42
|
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
43
|
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
|
44
44
|
|
|
@@ -60,6 +60,7 @@ struct generate_json_data {
|
|
|
60
60
|
JSON_Generator_State *state;
|
|
61
61
|
VALUE obj;
|
|
62
62
|
generator_func func;
|
|
63
|
+
long depth;
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
@@ -127,7 +128,7 @@ typedef struct _search_state {
|
|
|
127
128
|
#endif /* HAVE_SIMD */
|
|
128
129
|
} search_state;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
ALWAYS_INLINE(static) void search_flush(search_state *search)
|
|
131
132
|
{
|
|
132
133
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
|
133
134
|
// escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
|
|
@@ -170,7 +171,7 @@ static inline unsigned char search_escape_basic(search_state *search)
|
|
|
170
171
|
return 0;
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
|
|
174
|
+
ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
174
175
|
{
|
|
175
176
|
const unsigned char ch = (unsigned char)*search->ptr;
|
|
176
177
|
switch (ch) {
|
|
@@ -257,7 +258,7 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
257
258
|
|
|
258
259
|
#ifdef HAVE_SIMD
|
|
259
260
|
|
|
260
|
-
|
|
261
|
+
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
261
262
|
{
|
|
262
263
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
263
264
|
search_flush(search);
|
|
@@ -280,7 +281,7 @@ static ALWAYS_INLINE() char *copy_remaining_bytes(search_state *search, unsigned
|
|
|
280
281
|
|
|
281
282
|
#ifdef HAVE_SIMD_NEON
|
|
282
283
|
|
|
283
|
-
|
|
284
|
+
ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
|
|
284
285
|
{
|
|
285
286
|
uint64_t mask = search->matches_mask;
|
|
286
287
|
uint32_t index = trailing_zeros64(mask) >> 2;
|
|
@@ -394,7 +395,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
|
|
|
394
395
|
|
|
395
396
|
#ifdef HAVE_SIMD_SSE2
|
|
396
397
|
|
|
397
|
-
|
|
398
|
+
ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
|
|
398
399
|
{
|
|
399
400
|
int mask = search->matches_mask;
|
|
400
401
|
int index = trailing_zeros(mask);
|
|
@@ -418,7 +419,7 @@ static ALWAYS_INLINE() unsigned char sse2_next_match(search_state *search)
|
|
|
418
419
|
#define TARGET_SSE2
|
|
419
420
|
#endif
|
|
420
421
|
|
|
421
|
-
static TARGET_SSE2
|
|
422
|
+
ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
|
|
422
423
|
{
|
|
423
424
|
if (RB_UNLIKELY(search->has_matches)) {
|
|
424
425
|
// There are more matches if search->matches_mask > 0.
|
|
@@ -926,11 +927,6 @@ static size_t State_memsize(const void *ptr)
|
|
|
926
927
|
return sizeof(JSON_Generator_State);
|
|
927
928
|
}
|
|
928
929
|
|
|
929
|
-
#ifndef HAVE_RB_EXT_RACTOR_SAFE
|
|
930
|
-
# undef RUBY_TYPED_FROZEN_SHAREABLE
|
|
931
|
-
# define RUBY_TYPED_FROZEN_SHAREABLE 0
|
|
932
|
-
#endif
|
|
933
|
-
|
|
934
930
|
static const rb_data_type_t JSON_Generator_State_type = {
|
|
935
931
|
"JSON/Generator/State",
|
|
936
932
|
{
|
|
@@ -972,12 +968,16 @@ static void vstate_spill(struct generate_json_data *data)
|
|
|
972
968
|
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
|
973
969
|
}
|
|
974
970
|
|
|
975
|
-
static inline VALUE
|
|
971
|
+
static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
|
|
976
972
|
{
|
|
977
973
|
if (RB_UNLIKELY(!data->vstate)) {
|
|
978
974
|
vstate_spill(data);
|
|
979
975
|
}
|
|
980
|
-
|
|
976
|
+
GET_STATE(data->vstate);
|
|
977
|
+
state->depth = data->depth;
|
|
978
|
+
VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
|
|
979
|
+
// no need to restore state->depth, vstate is just a temporary State
|
|
980
|
+
return tmp;
|
|
981
981
|
}
|
|
982
982
|
|
|
983
983
|
static VALUE
|
|
@@ -1125,8 +1125,7 @@ struct hash_foreach_arg {
|
|
|
1125
1125
|
bool mixed_keys_encountered;
|
|
1126
1126
|
};
|
|
1127
1127
|
|
|
1128
|
-
NOINLINE()
|
|
1129
|
-
static void
|
|
1128
|
+
NOINLINE(static) void
|
|
1130
1129
|
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
1131
1130
|
{
|
|
1132
1131
|
if (arg->mixed_keys_encountered) {
|
|
@@ -1150,7 +1149,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1150
1149
|
FBuffer *buffer = data->buffer;
|
|
1151
1150
|
JSON_Generator_State *state = data->state;
|
|
1152
1151
|
|
|
1153
|
-
long depth =
|
|
1152
|
+
long depth = data->depth;
|
|
1154
1153
|
int key_type = rb_type(key);
|
|
1155
1154
|
|
|
1156
1155
|
if (arg->first) {
|
|
@@ -1224,9 +1223,9 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1224
1223
|
static inline long increase_depth(struct generate_json_data *data)
|
|
1225
1224
|
{
|
|
1226
1225
|
JSON_Generator_State *state = data->state;
|
|
1227
|
-
long depth = ++
|
|
1226
|
+
long depth = ++data->depth;
|
|
1228
1227
|
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
|
1229
|
-
rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --
|
|
1228
|
+
rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
|
|
1230
1229
|
}
|
|
1231
1230
|
return depth;
|
|
1232
1231
|
}
|
|
@@ -1237,7 +1236,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1237
1236
|
|
|
1238
1237
|
if (RHASH_SIZE(obj) == 0) {
|
|
1239
1238
|
fbuffer_append(buffer, "{}", 2);
|
|
1240
|
-
--data->
|
|
1239
|
+
--data->depth;
|
|
1241
1240
|
return;
|
|
1242
1241
|
}
|
|
1243
1242
|
|
|
@@ -1250,7 +1249,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1250
1249
|
};
|
|
1251
1250
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
1252
1251
|
|
|
1253
|
-
depth = --data->
|
|
1252
|
+
depth = --data->depth;
|
|
1254
1253
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1255
1254
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1256
1255
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
@@ -1266,7 +1265,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1266
1265
|
|
|
1267
1266
|
if (RARRAY_LEN(obj) == 0) {
|
|
1268
1267
|
fbuffer_append(buffer, "[]", 2);
|
|
1269
|
-
--data->
|
|
1268
|
+
--data->depth;
|
|
1270
1269
|
return;
|
|
1271
1270
|
}
|
|
1272
1271
|
|
|
@@ -1282,7 +1281,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1282
1281
|
}
|
|
1283
1282
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
1284
1283
|
}
|
|
1285
|
-
data->
|
|
1284
|
+
data->depth = --depth;
|
|
1286
1285
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
|
1287
1286
|
fbuffer_append_str(buffer, data->state->array_nl);
|
|
1288
1287
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
@@ -1296,7 +1295,7 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d
|
|
|
1296
1295
|
{
|
|
1297
1296
|
VALUE tmp;
|
|
1298
1297
|
if (rb_respond_to(obj, i_to_json)) {
|
|
1299
|
-
tmp =
|
|
1298
|
+
tmp = json_call_to_json(data, obj);
|
|
1300
1299
|
Check_Type(tmp, T_STRING);
|
|
1301
1300
|
fbuffer_append_str(buffer, tmp);
|
|
1302
1301
|
} else {
|
|
@@ -1363,7 +1362,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1363
1362
|
if (casted_obj != obj) {
|
|
1364
1363
|
increase_depth(data);
|
|
1365
1364
|
generate_json(buffer, data, casted_obj);
|
|
1366
|
-
data->
|
|
1365
|
+
data->depth--;
|
|
1367
1366
|
return;
|
|
1368
1367
|
}
|
|
1369
1368
|
}
|
|
@@ -1498,8 +1497,9 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
|
1498
1497
|
|
|
1499
1498
|
struct generate_json_data data = {
|
|
1500
1499
|
.buffer = &buffer,
|
|
1501
|
-
.vstate = self
|
|
1500
|
+
.vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
|
|
1502
1501
|
.state = state,
|
|
1502
|
+
.depth = state->depth,
|
|
1503
1503
|
.obj = obj,
|
|
1504
1504
|
.func = func
|
|
1505
1505
|
};
|
|
@@ -1522,36 +1522,6 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1522
1522
|
return cState_partial_generate(self, obj, generate_json, io);
|
|
1523
1523
|
}
|
|
1524
1524
|
|
|
1525
|
-
static VALUE cState_generate_new(int argc, VALUE *argv, VALUE self)
|
|
1526
|
-
{
|
|
1527
|
-
rb_check_arity(argc, 1, 2);
|
|
1528
|
-
VALUE obj = argv[0];
|
|
1529
|
-
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1530
|
-
|
|
1531
|
-
GET_STATE(self);
|
|
1532
|
-
|
|
1533
|
-
JSON_Generator_State new_state;
|
|
1534
|
-
MEMCPY(&new_state, state, JSON_Generator_State, 1);
|
|
1535
|
-
|
|
1536
|
-
// FIXME: depth shouldn't be part of JSON_Generator_State, as that prevents it from being used concurrently.
|
|
1537
|
-
new_state.depth = 0;
|
|
1538
|
-
|
|
1539
|
-
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1540
|
-
FBuffer buffer = {
|
|
1541
|
-
.io = RTEST(io) ? io : Qfalse,
|
|
1542
|
-
};
|
|
1543
|
-
fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1544
|
-
|
|
1545
|
-
struct generate_json_data data = {
|
|
1546
|
-
.buffer = &buffer,
|
|
1547
|
-
.vstate = Qfalse,
|
|
1548
|
-
.state = &new_state,
|
|
1549
|
-
.obj = obj,
|
|
1550
|
-
.func = generate_json
|
|
1551
|
-
};
|
|
1552
|
-
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
1525
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
1556
1526
|
{
|
|
1557
1527
|
rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
|
|
@@ -1630,6 +1600,7 @@ static VALUE string_config(VALUE config)
|
|
|
1630
1600
|
*/
|
|
1631
1601
|
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
1632
1602
|
{
|
|
1603
|
+
rb_check_frozen(self);
|
|
1633
1604
|
GET_STATE(self);
|
|
1634
1605
|
RB_OBJ_WRITE(self, &state->indent, string_config(indent));
|
|
1635
1606
|
return Qnil;
|
|
@@ -1655,6 +1626,7 @@ static VALUE cState_space(VALUE self)
|
|
|
1655
1626
|
*/
|
|
1656
1627
|
static VALUE cState_space_set(VALUE self, VALUE space)
|
|
1657
1628
|
{
|
|
1629
|
+
rb_check_frozen(self);
|
|
1658
1630
|
GET_STATE(self);
|
|
1659
1631
|
RB_OBJ_WRITE(self, &state->space, string_config(space));
|
|
1660
1632
|
return Qnil;
|
|
@@ -1678,6 +1650,7 @@ static VALUE cState_space_before(VALUE self)
|
|
|
1678
1650
|
*/
|
|
1679
1651
|
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
1680
1652
|
{
|
|
1653
|
+
rb_check_frozen(self);
|
|
1681
1654
|
GET_STATE(self);
|
|
1682
1655
|
RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
|
|
1683
1656
|
return Qnil;
|
|
@@ -1703,6 +1676,7 @@ static VALUE cState_object_nl(VALUE self)
|
|
|
1703
1676
|
*/
|
|
1704
1677
|
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
1705
1678
|
{
|
|
1679
|
+
rb_check_frozen(self);
|
|
1706
1680
|
GET_STATE(self);
|
|
1707
1681
|
RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
|
|
1708
1682
|
return Qnil;
|
|
@@ -1726,6 +1700,7 @@ static VALUE cState_array_nl(VALUE self)
|
|
|
1726
1700
|
*/
|
|
1727
1701
|
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
1728
1702
|
{
|
|
1703
|
+
rb_check_frozen(self);
|
|
1729
1704
|
GET_STATE(self);
|
|
1730
1705
|
RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
|
|
1731
1706
|
return Qnil;
|
|
@@ -1749,6 +1724,7 @@ static VALUE cState_as_json(VALUE self)
|
|
|
1749
1724
|
*/
|
|
1750
1725
|
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
|
1751
1726
|
{
|
|
1727
|
+
rb_check_frozen(self);
|
|
1752
1728
|
GET_STATE(self);
|
|
1753
1729
|
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
|
1754
1730
|
return Qnil;
|
|
@@ -1791,6 +1767,7 @@ static long long_config(VALUE num)
|
|
|
1791
1767
|
*/
|
|
1792
1768
|
static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
|
1793
1769
|
{
|
|
1770
|
+
rb_check_frozen(self);
|
|
1794
1771
|
GET_STATE(self);
|
|
1795
1772
|
state->max_nesting = long_config(depth);
|
|
1796
1773
|
return Qnil;
|
|
@@ -1816,6 +1793,7 @@ static VALUE cState_script_safe(VALUE self)
|
|
|
1816
1793
|
*/
|
|
1817
1794
|
static VALUE cState_script_safe_set(VALUE self, VALUE enable)
|
|
1818
1795
|
{
|
|
1796
|
+
rb_check_frozen(self);
|
|
1819
1797
|
GET_STATE(self);
|
|
1820
1798
|
state->script_safe = RTEST(enable);
|
|
1821
1799
|
return Qnil;
|
|
@@ -1847,6 +1825,7 @@ static VALUE cState_strict(VALUE self)
|
|
|
1847
1825
|
*/
|
|
1848
1826
|
static VALUE cState_strict_set(VALUE self, VALUE enable)
|
|
1849
1827
|
{
|
|
1828
|
+
rb_check_frozen(self);
|
|
1850
1829
|
GET_STATE(self);
|
|
1851
1830
|
state->strict = RTEST(enable);
|
|
1852
1831
|
return Qnil;
|
|
@@ -1871,6 +1850,7 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
1871
1850
|
*/
|
|
1872
1851
|
static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
|
|
1873
1852
|
{
|
|
1853
|
+
rb_check_frozen(self);
|
|
1874
1854
|
GET_STATE(self);
|
|
1875
1855
|
state->allow_nan = RTEST(enable);
|
|
1876
1856
|
return Qnil;
|
|
@@ -1895,6 +1875,7 @@ static VALUE cState_ascii_only_p(VALUE self)
|
|
|
1895
1875
|
*/
|
|
1896
1876
|
static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
1897
1877
|
{
|
|
1878
|
+
rb_check_frozen(self);
|
|
1898
1879
|
GET_STATE(self);
|
|
1899
1880
|
state->ascii_only = RTEST(enable);
|
|
1900
1881
|
return Qnil;
|
|
@@ -1932,6 +1913,7 @@ static VALUE cState_depth(VALUE self)
|
|
|
1932
1913
|
*/
|
|
1933
1914
|
static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
1934
1915
|
{
|
|
1916
|
+
rb_check_frozen(self);
|
|
1935
1917
|
GET_STATE(self);
|
|
1936
1918
|
state->depth = long_config(depth);
|
|
1937
1919
|
return Qnil;
|
|
@@ -1965,6 +1947,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
|
|
|
1965
1947
|
*/
|
|
1966
1948
|
static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
|
|
1967
1949
|
{
|
|
1950
|
+
rb_check_frozen(self);
|
|
1968
1951
|
GET_STATE(self);
|
|
1969
1952
|
buffer_initial_length_set(state, buffer_initial_length);
|
|
1970
1953
|
return Qnil;
|
|
@@ -2031,6 +2014,7 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
|
|
|
2031
2014
|
|
|
2032
2015
|
static VALUE cState_configure(VALUE self, VALUE opts)
|
|
2033
2016
|
{
|
|
2017
|
+
rb_check_frozen(self);
|
|
2034
2018
|
GET_STATE(self);
|
|
2035
2019
|
configure_state(state, self, opts);
|
|
2036
2020
|
return self;
|
|
@@ -2052,6 +2036,7 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
|
2052
2036
|
.buffer = &buffer,
|
|
2053
2037
|
.vstate = Qfalse,
|
|
2054
2038
|
.state = &state,
|
|
2039
|
+
.depth = state.depth,
|
|
2055
2040
|
.obj = obj,
|
|
2056
2041
|
.func = generate_json,
|
|
2057
2042
|
};
|
|
@@ -2125,7 +2110,6 @@ void Init_generator(void)
|
|
|
2125
2110
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2126
2111
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2127
2112
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
2128
|
-
rb_define_method(cState, "generate_new", cState_generate_new, -1); // :nodoc:
|
|
2129
2113
|
|
|
2130
2114
|
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2131
2115
|
|
|
@@ -2173,10 +2157,6 @@ void Init_generator(void)
|
|
|
2173
2157
|
i_to_s = rb_intern("to_s");
|
|
2174
2158
|
i_to_json = rb_intern("to_json");
|
|
2175
2159
|
i_new = rb_intern("new");
|
|
2176
|
-
i_pack = rb_intern("pack");
|
|
2177
|
-
i_unpack = rb_intern("unpack");
|
|
2178
|
-
i_create_id = rb_intern("create_id");
|
|
2179
|
-
i_extend = rb_intern("extend");
|
|
2180
2160
|
i_encode = rb_intern("encode");
|
|
2181
2161
|
|
|
2182
2162
|
sym_indent = ID2SYM(rb_intern("indent"));
|
data/ext/json/ext/json.h
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'mkmf'
|
|
3
3
|
|
|
4
|
-
$defs << "-DJSON_DEBUG" if ENV
|
|
4
|
+
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
|
|
5
5
|
have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0
|
|
6
6
|
have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
|
|
7
7
|
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
|
|
8
8
|
have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
|
|
9
|
-
have_func("strnlen", "string.h") # Missing on Solaris 10
|
|
10
9
|
|
|
11
10
|
append_cflags("-std=c99")
|
|
12
11
|
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
static VALUE mJSON, eNestingError, Encoding_UTF_8;
|
|
6
6
|
static VALUE CNaN, CInfinity, CMinusInfinity;
|
|
7
7
|
|
|
8
|
-
static ID
|
|
9
|
-
i_leftshift, i_new, i_try_convert, i_uminus, i_encode;
|
|
8
|
+
static ID i_new, i_try_convert, i_uminus, i_encode;
|
|
10
9
|
|
|
11
|
-
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
|
|
10
|
+
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters, sym_symbolize_names, sym_freeze,
|
|
12
11
|
sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
|
|
13
12
|
|
|
14
13
|
static int binary_encindex;
|
|
@@ -89,7 +88,7 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring
|
|
|
89
88
|
#if JSON_CPU_LITTLE_ENDIAN_64BITS
|
|
90
89
|
#if __has_builtin(__builtin_bswap64)
|
|
91
90
|
#undef rstring_cache_memcmp
|
|
92
|
-
|
|
91
|
+
ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr, const long length)
|
|
93
92
|
{
|
|
94
93
|
// The libc memcmp has numerous complex optimizations, but in this particular case,
|
|
95
94
|
// we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to
|
|
@@ -118,7 +117,7 @@ static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rpt
|
|
|
118
117
|
#endif
|
|
119
118
|
#endif
|
|
120
119
|
|
|
121
|
-
|
|
120
|
+
ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
|
|
122
121
|
{
|
|
123
122
|
const char *rstring_ptr;
|
|
124
123
|
long rstring_length;
|
|
@@ -132,7 +131,7 @@ static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length,
|
|
|
132
131
|
}
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
|
|
134
|
+
ALWAYS_INLINE(static) VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length)
|
|
136
135
|
{
|
|
137
136
|
int low = 0;
|
|
138
137
|
int high = cache->length - 1;
|
|
@@ -296,15 +295,6 @@ static void rvalue_stack_eagerly_release(VALUE handle)
|
|
|
296
295
|
}
|
|
297
296
|
}
|
|
298
297
|
|
|
299
|
-
|
|
300
|
-
#ifndef HAVE_STRNLEN
|
|
301
|
-
static size_t strnlen(const char *s, size_t maxlen)
|
|
302
|
-
{
|
|
303
|
-
char *p;
|
|
304
|
-
return ((p = memchr(s, '\0', maxlen)) ? p - s : maxlen);
|
|
305
|
-
}
|
|
306
|
-
#endif
|
|
307
|
-
|
|
308
298
|
static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
|
|
309
299
|
{
|
|
310
300
|
int len = 1;
|
|
@@ -345,7 +335,7 @@ typedef struct JSON_ParserStruct {
|
|
|
345
335
|
int max_nesting;
|
|
346
336
|
bool allow_nan;
|
|
347
337
|
bool allow_trailing_comma;
|
|
348
|
-
bool
|
|
338
|
+
bool allow_control_characters;
|
|
349
339
|
bool symbolize_names;
|
|
350
340
|
bool freeze;
|
|
351
341
|
} JSON_ParserConfig;
|
|
@@ -551,7 +541,7 @@ json_eat_comments(JSON_ParserState *state)
|
|
|
551
541
|
}
|
|
552
542
|
}
|
|
553
543
|
|
|
554
|
-
|
|
544
|
+
ALWAYS_INLINE(static) void
|
|
555
545
|
json_eat_whitespace(JSON_ParserState *state)
|
|
556
546
|
{
|
|
557
547
|
while (true) {
|
|
@@ -627,8 +617,10 @@ static inline bool json_string_cacheable_p(const char *string, size_t length)
|
|
|
627
617
|
return length <= JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH && rb_isalpha(string[0]);
|
|
628
618
|
}
|
|
629
619
|
|
|
630
|
-
static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *string, const char *stringEnd, bool is_name
|
|
620
|
+
static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name)
|
|
631
621
|
{
|
|
622
|
+
bool intern = is_name || config->freeze;
|
|
623
|
+
bool symbolize = is_name && config->symbolize_names;
|
|
632
624
|
size_t bufferSize = stringEnd - string;
|
|
633
625
|
|
|
634
626
|
if (is_name && state->in_array && RB_LIKELY(json_string_cacheable_p(string, bufferSize))) {
|
|
@@ -647,47 +639,73 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *st
|
|
|
647
639
|
return build_string(string, stringEnd, intern, symbolize);
|
|
648
640
|
}
|
|
649
641
|
|
|
650
|
-
|
|
642
|
+
#define JSON_MAX_UNESCAPE_POSITIONS 16
|
|
643
|
+
typedef struct _json_unescape_positions {
|
|
644
|
+
long size;
|
|
645
|
+
const char **positions;
|
|
646
|
+
bool has_more;
|
|
647
|
+
} JSON_UnescapePositions;
|
|
648
|
+
|
|
649
|
+
static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
|
|
650
|
+
{
|
|
651
|
+
while (positions->size) {
|
|
652
|
+
positions->size--;
|
|
653
|
+
const char *next_position = positions->positions[0];
|
|
654
|
+
positions->positions++;
|
|
655
|
+
if (next_position >= pe) {
|
|
656
|
+
return next_position;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (positions->has_more) {
|
|
661
|
+
return memchr(pe, '\\', stringEnd - pe);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return NULL;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions)
|
|
651
668
|
{
|
|
669
|
+
bool intern = is_name || config->freeze;
|
|
670
|
+
bool symbolize = is_name && config->symbolize_names;
|
|
652
671
|
size_t bufferSize = stringEnd - string;
|
|
653
|
-
const char *p = string, *pe = string, *
|
|
672
|
+
const char *p = string, *pe = string, *bufferStart;
|
|
654
673
|
char *buffer;
|
|
655
|
-
int unescape_len;
|
|
656
|
-
char buf[4];
|
|
657
674
|
|
|
658
675
|
VALUE result = rb_str_buf_new(bufferSize);
|
|
659
676
|
rb_enc_associate_index(result, utf8_encindex);
|
|
660
677
|
buffer = RSTRING_PTR(result);
|
|
661
678
|
bufferStart = buffer;
|
|
662
679
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
680
|
+
#define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe;
|
|
681
|
+
|
|
682
|
+
while (pe < stringEnd && (pe = json_next_backslash(pe, stringEnd, positions))) {
|
|
666
683
|
if (pe > p) {
|
|
667
684
|
MEMCPY(buffer, p, char, pe - p);
|
|
668
685
|
buffer += pe - p;
|
|
669
686
|
}
|
|
670
687
|
switch (*++pe) {
|
|
688
|
+
case '"':
|
|
689
|
+
case '/':
|
|
690
|
+
p = pe; // nothing to unescape just need to skip the backslash
|
|
691
|
+
break;
|
|
692
|
+
case '\\':
|
|
693
|
+
APPEND_CHAR('\\');
|
|
694
|
+
break;
|
|
671
695
|
case 'n':
|
|
672
|
-
|
|
696
|
+
APPEND_CHAR('\n');
|
|
673
697
|
break;
|
|
674
698
|
case 'r':
|
|
675
|
-
|
|
699
|
+
APPEND_CHAR('\r');
|
|
676
700
|
break;
|
|
677
701
|
case 't':
|
|
678
|
-
|
|
679
|
-
break;
|
|
680
|
-
case '"':
|
|
681
|
-
unescape = (char *) "\"";
|
|
682
|
-
break;
|
|
683
|
-
case '\\':
|
|
684
|
-
unescape = (char *) "\\";
|
|
702
|
+
APPEND_CHAR('\t');
|
|
685
703
|
break;
|
|
686
704
|
case 'b':
|
|
687
|
-
|
|
705
|
+
APPEND_CHAR('\b');
|
|
688
706
|
break;
|
|
689
707
|
case 'f':
|
|
690
|
-
|
|
708
|
+
APPEND_CHAR('\f');
|
|
691
709
|
break;
|
|
692
710
|
case 'u':
|
|
693
711
|
if (pe > stringEnd - 5) {
|
|
@@ -725,18 +743,29 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
|
|
|
725
743
|
break;
|
|
726
744
|
}
|
|
727
745
|
}
|
|
728
|
-
|
|
729
|
-
|
|
746
|
+
|
|
747
|
+
char buf[4];
|
|
748
|
+
int unescape_len = convert_UTF32_to_UTF8(buf, ch);
|
|
749
|
+
MEMCPY(buffer, buf, char, unescape_len);
|
|
750
|
+
buffer += unescape_len;
|
|
751
|
+
p = ++pe;
|
|
730
752
|
}
|
|
731
753
|
break;
|
|
732
754
|
default:
|
|
733
|
-
|
|
734
|
-
|
|
755
|
+
if ((unsigned char)*pe < 0x20) {
|
|
756
|
+
if (!config->allow_control_characters) {
|
|
757
|
+
if (*pe == '\n') {
|
|
758
|
+
raise_parse_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1);
|
|
759
|
+
}
|
|
760
|
+
raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
|
|
761
|
+
}
|
|
762
|
+
} else {
|
|
763
|
+
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
|
|
764
|
+
}
|
|
765
|
+
break;
|
|
735
766
|
}
|
|
736
|
-
MEMCPY(buffer, unescape, char, unescape_len);
|
|
737
|
-
buffer += unescape_len;
|
|
738
|
-
p = ++pe;
|
|
739
767
|
}
|
|
768
|
+
#undef APPEND_CHAR
|
|
740
769
|
|
|
741
770
|
if (stringEnd > p) {
|
|
742
771
|
MEMCPY(buffer, p, char, stringEnd - p);
|
|
@@ -900,20 +929,6 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
|
|
|
900
929
|
return object;
|
|
901
930
|
}
|
|
902
931
|
|
|
903
|
-
static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfig *config, const char *start, const char *end, bool escaped, bool is_name)
|
|
904
|
-
{
|
|
905
|
-
VALUE string;
|
|
906
|
-
bool intern = is_name || config->freeze;
|
|
907
|
-
bool symbolize = is_name && config->symbolize_names;
|
|
908
|
-
if (escaped) {
|
|
909
|
-
string = json_string_unescape(state, start, end, is_name, intern, symbolize);
|
|
910
|
-
} else {
|
|
911
|
-
string = json_string_fastpath(state, start, end, is_name, intern, symbolize);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
return string;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
932
|
static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value)
|
|
918
933
|
{
|
|
919
934
|
if (RB_UNLIKELY(config->on_load_proc)) {
|
|
@@ -940,7 +955,7 @@ static const bool string_scan_table[256] = {
|
|
|
940
955
|
static SIMD_Implementation simd_impl = SIMD_NONE;
|
|
941
956
|
#endif /* HAVE_SIMD */
|
|
942
957
|
|
|
943
|
-
|
|
958
|
+
ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
|
|
944
959
|
{
|
|
945
960
|
#ifdef HAVE_SIMD
|
|
946
961
|
#if defined(HAVE_SIMD_NEON)
|
|
@@ -948,7 +963,7 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
|
|
|
948
963
|
uint64_t mask = 0;
|
|
949
964
|
if (string_scan_simd_neon(&state->cursor, state->end, &mask)) {
|
|
950
965
|
state->cursor += trailing_zeros64(mask) >> 2;
|
|
951
|
-
return
|
|
966
|
+
return true;
|
|
952
967
|
}
|
|
953
968
|
|
|
954
969
|
#elif defined(HAVE_SIMD_SSE2)
|
|
@@ -956,7 +971,7 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
|
|
|
956
971
|
int mask = 0;
|
|
957
972
|
if (string_scan_simd_sse2(&state->cursor, state->end, &mask)) {
|
|
958
973
|
state->cursor += trailing_zeros(mask);
|
|
959
|
-
return
|
|
974
|
+
return true;
|
|
960
975
|
}
|
|
961
976
|
}
|
|
962
977
|
#endif /* HAVE_SIMD_NEON or HAVE_SIMD_SSE2 */
|
|
@@ -964,46 +979,70 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
|
|
|
964
979
|
|
|
965
980
|
while (!eos(state)) {
|
|
966
981
|
if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) {
|
|
967
|
-
return
|
|
982
|
+
return true;
|
|
968
983
|
}
|
|
969
984
|
state->cursor++;
|
|
970
985
|
}
|
|
971
|
-
return
|
|
986
|
+
return false;
|
|
972
987
|
}
|
|
973
988
|
|
|
974
|
-
static
|
|
989
|
+
static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name, const char *start)
|
|
975
990
|
{
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
991
|
+
const char *backslashes[JSON_MAX_UNESCAPE_POSITIONS];
|
|
992
|
+
JSON_UnescapePositions positions = {
|
|
993
|
+
.size = 0,
|
|
994
|
+
.positions = backslashes,
|
|
995
|
+
.has_more = false,
|
|
996
|
+
};
|
|
979
997
|
|
|
980
|
-
|
|
998
|
+
do {
|
|
981
999
|
switch (*state->cursor) {
|
|
982
1000
|
case '"': {
|
|
983
|
-
VALUE string =
|
|
1001
|
+
VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions);
|
|
984
1002
|
state->cursor++;
|
|
985
1003
|
return json_push_value(state, config, string);
|
|
986
1004
|
}
|
|
987
1005
|
case '\\': {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1006
|
+
if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) {
|
|
1007
|
+
backslashes[positions.size] = state->cursor;
|
|
1008
|
+
positions.size++;
|
|
1009
|
+
} else {
|
|
1010
|
+
positions.has_more = true;
|
|
992
1011
|
}
|
|
1012
|
+
state->cursor++;
|
|
993
1013
|
break;
|
|
994
1014
|
}
|
|
995
1015
|
default:
|
|
996
|
-
|
|
1016
|
+
if (!config->allow_control_characters) {
|
|
1017
|
+
raise_parse_error("invalid ASCII control character in string: %s", state);
|
|
1018
|
+
}
|
|
997
1019
|
break;
|
|
998
1020
|
}
|
|
999
1021
|
|
|
1000
1022
|
state->cursor++;
|
|
1001
|
-
}
|
|
1023
|
+
} while (string_scan(state));
|
|
1002
1024
|
|
|
1003
1025
|
raise_parse_error("unexpected end of input, expected closing \"", state);
|
|
1004
1026
|
return Qfalse;
|
|
1005
1027
|
}
|
|
1006
1028
|
|
|
1029
|
+
ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name)
|
|
1030
|
+
{
|
|
1031
|
+
state->cursor++;
|
|
1032
|
+
const char *start = state->cursor;
|
|
1033
|
+
|
|
1034
|
+
if (RB_UNLIKELY(!string_scan(state))) {
|
|
1035
|
+
raise_parse_error("unexpected end of input, expected closing \"", state);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
if (RB_LIKELY(*state->cursor == '"')) {
|
|
1039
|
+
VALUE string = json_string_fastpath(state, config, start, state->cursor, is_name);
|
|
1040
|
+
state->cursor++;
|
|
1041
|
+
return json_push_value(state, config, string);
|
|
1042
|
+
}
|
|
1043
|
+
return json_parse_escaped_string(state, config, is_name, start);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1007
1046
|
#if JSON_CPU_LITTLE_ENDIAN_64BITS
|
|
1008
1047
|
// From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/
|
|
1009
1048
|
// Additional References:
|
|
@@ -1397,14 +1436,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1397
1436
|
{
|
|
1398
1437
|
JSON_ParserConfig *config = (JSON_ParserConfig *)data;
|
|
1399
1438
|
|
|
1400
|
-
if (key == sym_max_nesting)
|
|
1401
|
-
else if (key == sym_allow_nan)
|
|
1402
|
-
else if (key == sym_allow_trailing_comma)
|
|
1403
|
-
else if (key ==
|
|
1404
|
-
else if (key ==
|
|
1405
|
-
else if (key ==
|
|
1406
|
-
else if (key ==
|
|
1407
|
-
else if (key ==
|
|
1439
|
+
if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
|
|
1440
|
+
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
1441
|
+
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
|
1442
|
+
else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
|
|
1443
|
+
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1444
|
+
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1445
|
+
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
|
1446
|
+
else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1447
|
+
else if (key == sym_decimal_class) {
|
|
1408
1448
|
if (RTEST(val)) {
|
|
1409
1449
|
if (rb_respond_to(val, i_try_convert)) {
|
|
1410
1450
|
config->decimal_class = val;
|
|
@@ -1477,6 +1517,7 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
|
|
|
1477
1517
|
*/
|
|
1478
1518
|
static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
|
1479
1519
|
{
|
|
1520
|
+
rb_check_frozen(self);
|
|
1480
1521
|
GET_PARSER_CONFIG;
|
|
1481
1522
|
|
|
1482
1523
|
parser_config_init(config, opts);
|
|
@@ -1572,7 +1613,7 @@ static const rb_data_type_t JSON_ParserConfig_type = {
|
|
|
1572
1613
|
JSON_ParserConfig_memsize,
|
|
1573
1614
|
},
|
|
1574
1615
|
0, 0,
|
|
1575
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
1616
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
1576
1617
|
};
|
|
1577
1618
|
|
|
1578
1619
|
static VALUE cJSON_parser_s_allocate(VALUE klass)
|
|
@@ -1616,16 +1657,13 @@ void Init_parser(void)
|
|
|
1616
1657
|
sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
|
|
1617
1658
|
sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
|
|
1618
1659
|
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
|
|
1660
|
+
sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
|
|
1619
1661
|
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
|
1620
1662
|
sym_freeze = ID2SYM(rb_intern("freeze"));
|
|
1621
1663
|
sym_on_load = ID2SYM(rb_intern("on_load"));
|
|
1622
1664
|
sym_decimal_class = ID2SYM(rb_intern("decimal_class"));
|
|
1623
1665
|
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
|
1624
1666
|
|
|
1625
|
-
i_chr = rb_intern("chr");
|
|
1626
|
-
i_aset = rb_intern("[]=");
|
|
1627
|
-
i_aref = rb_intern("[]");
|
|
1628
|
-
i_leftshift = rb_intern("<<");
|
|
1629
1667
|
i_new = rb_intern("new");
|
|
1630
1668
|
i_try_convert = rb_intern("try_convert");
|
|
1631
1669
|
i_uminus = rb_intern("-@");
|
data/ext/json/ext/simd/simd.h
CHANGED
|
@@ -73,14 +73,14 @@ static inline SIMD_Implementation find_simd_implementation(void)
|
|
|
73
73
|
#define HAVE_SIMD_NEON 1
|
|
74
74
|
|
|
75
75
|
// See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
|
|
76
|
-
|
|
76
|
+
ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches)
|
|
77
77
|
{
|
|
78
78
|
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4);
|
|
79
79
|
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
|
|
80
80
|
return mask & 0x8888888888888888ull;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr)
|
|
84
84
|
{
|
|
85
85
|
uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
|
|
86
86
|
|
|
@@ -93,7 +93,7 @@ static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr)
|
|
|
93
93
|
return neon_match_mask(needs_escape);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
|
|
97
97
|
{
|
|
98
98
|
while (*ptr + sizeof(uint8x16_t) <= end) {
|
|
99
99
|
uint64_t chunk_mask = compute_chunk_mask_neon(*ptr);
|
|
@@ -140,7 +140,7 @@ static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
|
|
140
140
|
#define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1))
|
|
141
141
|
#define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a)
|
|
142
142
|
|
|
143
|
-
static TARGET_SSE2
|
|
143
|
+
ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr)
|
|
144
144
|
{
|
|
145
145
|
__m128i chunk = _mm_loadu_si128((__m128i const*)ptr);
|
|
146
146
|
// Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
|
|
@@ -151,7 +151,7 @@ static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr)
|
|
|
151
151
|
return _mm_movemask_epi8(needs_escape);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
static TARGET_SSE2
|
|
154
|
+
ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
|
|
155
155
|
{
|
|
156
156
|
while (*ptr + sizeof(__m128i) <= end) {
|
|
157
157
|
int chunk_mask = compute_chunk_mask_sse2(*ptr);
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
#include <string.h>
|
|
30
30
|
#include <stdint.h>
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
#if JSON_DEBUG
|
|
33
33
|
#include <assert.h>
|
|
34
34
|
#endif
|
|
35
35
|
|
|
@@ -472,7 +472,7 @@ static int fpconv_dtoa(double d, char dest[28])
|
|
|
472
472
|
int ndigits = grisu2(d, digits, &K);
|
|
473
473
|
|
|
474
474
|
str_len += emit_digits(digits, ndigits, dest + str_len, K, neg);
|
|
475
|
-
#
|
|
475
|
+
#if JSON_DEBUG
|
|
476
476
|
assert(str_len <= 32);
|
|
477
477
|
#endif
|
|
478
478
|
|
data/lib/json/common.rb
CHANGED
|
@@ -550,6 +550,7 @@ module JSON
|
|
|
550
550
|
:create_additions => nil,
|
|
551
551
|
}
|
|
552
552
|
# :call-seq:
|
|
553
|
+
# JSON.unsafe_load(source, options = {}) -> object
|
|
553
554
|
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
|
554
555
|
#
|
|
555
556
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
@@ -681,7 +682,12 @@ module JSON
|
|
|
681
682
|
#
|
|
682
683
|
def unsafe_load(source, proc = nil, options = nil)
|
|
683
684
|
opts = if options.nil?
|
|
684
|
-
|
|
685
|
+
if proc && proc.is_a?(Hash)
|
|
686
|
+
options, proc = proc, nil
|
|
687
|
+
options
|
|
688
|
+
else
|
|
689
|
+
_unsafe_load_default_options
|
|
690
|
+
end
|
|
685
691
|
else
|
|
686
692
|
_unsafe_load_default_options.merge(options)
|
|
687
693
|
end
|
|
@@ -709,6 +715,7 @@ module JSON
|
|
|
709
715
|
end
|
|
710
716
|
|
|
711
717
|
# :call-seq:
|
|
718
|
+
# JSON.load(source, options = {}) -> object
|
|
712
719
|
# JSON.load(source, proc = nil, options = {}) -> object
|
|
713
720
|
#
|
|
714
721
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
@@ -845,8 +852,18 @@ module JSON
|
|
|
845
852
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
846
853
|
#
|
|
847
854
|
def load(source, proc = nil, options = nil)
|
|
855
|
+
if proc && options.nil? && proc.is_a?(Hash)
|
|
856
|
+
options = proc
|
|
857
|
+
proc = nil
|
|
858
|
+
end
|
|
859
|
+
|
|
848
860
|
opts = if options.nil?
|
|
849
|
-
|
|
861
|
+
if proc && proc.is_a?(Hash)
|
|
862
|
+
options, proc = proc, nil
|
|
863
|
+
options
|
|
864
|
+
else
|
|
865
|
+
_load_default_options
|
|
866
|
+
end
|
|
850
867
|
else
|
|
851
868
|
_load_default_options.merge(options)
|
|
852
869
|
end
|
|
@@ -1048,7 +1065,7 @@ module JSON
|
|
|
1048
1065
|
options[:as_json] = as_json if as_json
|
|
1049
1066
|
|
|
1050
1067
|
@state = State.new(options).freeze
|
|
1051
|
-
@parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options))
|
|
1068
|
+
@parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
|
|
1052
1069
|
end
|
|
1053
1070
|
|
|
1054
1071
|
# call-seq:
|
|
@@ -1057,7 +1074,7 @@ module JSON
|
|
|
1057
1074
|
#
|
|
1058
1075
|
# Serialize the given object into a \JSON document.
|
|
1059
1076
|
def dump(object, io = nil)
|
|
1060
|
-
@state.
|
|
1077
|
+
@state.generate(object, io)
|
|
1061
1078
|
end
|
|
1062
1079
|
alias_method :generate, :dump
|
|
1063
1080
|
|
|
@@ -312,8 +312,8 @@ module JSON
|
|
|
312
312
|
def to_h
|
|
313
313
|
result = {}
|
|
314
314
|
instance_variables.each do |iv|
|
|
315
|
-
|
|
316
|
-
result[
|
|
315
|
+
key = iv.to_s[1..-1]
|
|
316
|
+
result[key.to_sym] = instance_variable_get(iv)
|
|
317
317
|
end
|
|
318
318
|
|
|
319
319
|
if result[:allow_duplicate_key].nil?
|
|
@@ -330,6 +330,9 @@ module JSON
|
|
|
330
330
|
# created this method raises a
|
|
331
331
|
# GeneratorError exception.
|
|
332
332
|
def generate(obj, anIO = nil)
|
|
333
|
+
return dup.generate(obj, anIO) if frozen?
|
|
334
|
+
|
|
335
|
+
depth = @depth
|
|
333
336
|
if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
|
|
334
337
|
!@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
|
|
335
338
|
result = generate_json(obj, ''.dup)
|
|
@@ -346,14 +349,8 @@ module JSON
|
|
|
346
349
|
else
|
|
347
350
|
result
|
|
348
351
|
end
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
def generate_new(obj, anIO = nil) # :nodoc:
|
|
352
|
-
dup.generate(obj, anIO)
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
private def initialize_copy(_orig)
|
|
356
|
-
@depth = 0
|
|
352
|
+
ensure
|
|
353
|
+
@depth = depth unless frozen?
|
|
357
354
|
end
|
|
358
355
|
|
|
359
356
|
# Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
|
|
@@ -494,17 +491,15 @@ module JSON
|
|
|
494
491
|
# _depth_ is used to find out nesting depth, to indent accordingly.
|
|
495
492
|
def to_json(state = nil, *)
|
|
496
493
|
state = State.from_state(state)
|
|
494
|
+
depth = state.depth
|
|
497
495
|
state.check_max_nesting
|
|
498
496
|
json_transform(state)
|
|
497
|
+
ensure
|
|
498
|
+
state.depth = depth
|
|
499
499
|
end
|
|
500
500
|
|
|
501
501
|
private
|
|
502
502
|
|
|
503
|
-
def json_shift(state)
|
|
504
|
-
state.object_nl.empty? or return ''
|
|
505
|
-
state.indent * state.depth
|
|
506
|
-
end
|
|
507
|
-
|
|
508
503
|
def json_transform(state)
|
|
509
504
|
depth = state.depth += 1
|
|
510
505
|
|
|
@@ -559,17 +554,19 @@ module JSON
|
|
|
559
554
|
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
|
|
560
555
|
end
|
|
561
556
|
result << value.to_json(state)
|
|
557
|
+
state.depth = depth
|
|
562
558
|
else
|
|
563
559
|
raise GeneratorError.new("#{value.class} not allowed in JSON", value)
|
|
564
560
|
end
|
|
565
561
|
elsif value.respond_to?(:to_json)
|
|
566
562
|
result << value.to_json(state)
|
|
563
|
+
state.depth = depth
|
|
567
564
|
else
|
|
568
565
|
result << %{"#{String(value)}"}
|
|
569
566
|
end
|
|
570
567
|
first = false
|
|
571
568
|
}
|
|
572
|
-
depth
|
|
569
|
+
depth -= 1
|
|
573
570
|
unless first
|
|
574
571
|
result << state.object_nl
|
|
575
572
|
result << state.indent * depth if indent
|
|
@@ -586,8 +583,11 @@ module JSON
|
|
|
586
583
|
# produced JSON string output further.
|
|
587
584
|
def to_json(state = nil, *)
|
|
588
585
|
state = State.from_state(state)
|
|
586
|
+
depth = state.depth
|
|
589
587
|
state.check_max_nesting
|
|
590
588
|
json_transform(state)
|
|
589
|
+
ensure
|
|
590
|
+
state.depth = depth
|
|
591
591
|
end
|
|
592
592
|
|
|
593
593
|
private
|
|
@@ -625,12 +625,13 @@ module JSON
|
|
|
625
625
|
end
|
|
626
626
|
elsif value.respond_to?(:to_json)
|
|
627
627
|
result << value.to_json(state)
|
|
628
|
+
state.depth = depth
|
|
628
629
|
else
|
|
629
630
|
result << %{"#{String(value)}"}
|
|
630
631
|
end
|
|
631
632
|
first = false
|
|
632
633
|
}
|
|
633
|
-
depth
|
|
634
|
+
depth -= 1
|
|
634
635
|
result << state.array_nl
|
|
635
636
|
result << state.indent * depth if indent
|
|
636
637
|
result << ']'
|
|
@@ -655,6 +656,9 @@ module JSON
|
|
|
655
656
|
if casted_value.equal?(self)
|
|
656
657
|
raise GeneratorError.new("#{self} not allowed in JSON", self)
|
|
657
658
|
end
|
|
659
|
+
unless Generator.native_type?(casted_value)
|
|
660
|
+
raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
|
|
661
|
+
end
|
|
658
662
|
|
|
659
663
|
state.check_max_nesting
|
|
660
664
|
state.depth += 1
|
data/lib/json/version.rb
CHANGED