json 2.16.0 → 2.17.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 +12 -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 +102 -77
- 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 -12
- 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: a35454680e80a622fb539155325b2f693e90b352aad3f24b7f9eafc5ca797963
|
|
4
|
+
data.tar.gz: 9eee4f7ce5348a1eb44100766d5a7b326a4e76691fbfd3db8a2410020fb75ec1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0771fad39331fe9d47dbd855a02f662053a79ff08679c8bc76855dcfd3edbcdb32746cc0db435e6ea14815eacdbe97bf1152b704eacb7ddf1423917eb2545923
|
|
7
|
+
data.tar.gz: 742dc87f4cc0306e613cfb65824aaee600aa53f0715135190ffaa4ca1f25aa6820a10b1860befcf90f3dcf6676bd5bb304f0e2858ec1e5933f66b089fca82e78
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2025-12-03 (2.17.0)
|
|
6
|
+
|
|
7
|
+
* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
|
|
8
|
+
* Fix the parser to no longer ignore invalid escapes in strings.
|
|
9
|
+
Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
|
|
10
|
+
* Fixed `JSON::Coder` to use the depth it was initialized with.
|
|
11
|
+
* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
|
|
12
|
+
* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
|
|
13
|
+
automatically to its initial value.
|
|
14
|
+
In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
|
|
15
|
+
generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
|
|
16
|
+
|
|
5
17
|
### 2025-11-07 (2.16.0)
|
|
6
18
|
|
|
7
19
|
* 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,8 +5,7 @@
|
|
|
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
10
|
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
|
|
12
11
|
sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
|
|
@@ -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,6 @@ typedef struct JSON_ParserStruct {
|
|
|
345
335
|
int max_nesting;
|
|
346
336
|
bool allow_nan;
|
|
347
337
|
bool allow_trailing_comma;
|
|
348
|
-
bool parsing_name;
|
|
349
338
|
bool symbolize_names;
|
|
350
339
|
bool freeze;
|
|
351
340
|
} JSON_ParserConfig;
|
|
@@ -551,7 +540,7 @@ json_eat_comments(JSON_ParserState *state)
|
|
|
551
540
|
}
|
|
552
541
|
}
|
|
553
542
|
|
|
554
|
-
|
|
543
|
+
ALWAYS_INLINE(static) void
|
|
555
544
|
json_eat_whitespace(JSON_ParserState *state)
|
|
556
545
|
{
|
|
557
546
|
while (true) {
|
|
@@ -627,8 +616,10 @@ static inline bool json_string_cacheable_p(const char *string, size_t length)
|
|
|
627
616
|
return length <= JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH && rb_isalpha(string[0]);
|
|
628
617
|
}
|
|
629
618
|
|
|
630
|
-
static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *string, const char *stringEnd, bool is_name
|
|
619
|
+
static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name)
|
|
631
620
|
{
|
|
621
|
+
bool intern = is_name || config->freeze;
|
|
622
|
+
bool symbolize = is_name && config->symbolize_names;
|
|
632
623
|
size_t bufferSize = stringEnd - string;
|
|
633
624
|
|
|
634
625
|
if (is_name && state->in_array && RB_LIKELY(json_string_cacheable_p(string, bufferSize))) {
|
|
@@ -647,47 +638,71 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *st
|
|
|
647
638
|
return build_string(string, stringEnd, intern, symbolize);
|
|
648
639
|
}
|
|
649
640
|
|
|
650
|
-
|
|
641
|
+
#define JSON_MAX_UNESCAPE_POSITIONS 16
|
|
642
|
+
typedef struct _json_unescape_positions {
|
|
643
|
+
long size;
|
|
644
|
+
const char **positions;
|
|
645
|
+
bool has_more;
|
|
646
|
+
} JSON_UnescapePositions;
|
|
647
|
+
|
|
648
|
+
static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
|
|
649
|
+
{
|
|
650
|
+
while (positions->size) {
|
|
651
|
+
positions->size--;
|
|
652
|
+
const char *next_position = positions->positions[0];
|
|
653
|
+
positions->positions++;
|
|
654
|
+
return next_position;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (positions->has_more) {
|
|
658
|
+
return memchr(pe, '\\', stringEnd - pe);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return NULL;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
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
665
|
{
|
|
666
|
+
bool intern = is_name || config->freeze;
|
|
667
|
+
bool symbolize = is_name && config->symbolize_names;
|
|
652
668
|
size_t bufferSize = stringEnd - string;
|
|
653
|
-
const char *p = string, *pe = string, *
|
|
669
|
+
const char *p = string, *pe = string, *bufferStart;
|
|
654
670
|
char *buffer;
|
|
655
|
-
int unescape_len;
|
|
656
|
-
char buf[4];
|
|
657
671
|
|
|
658
672
|
VALUE result = rb_str_buf_new(bufferSize);
|
|
659
673
|
rb_enc_associate_index(result, utf8_encindex);
|
|
660
674
|
buffer = RSTRING_PTR(result);
|
|
661
675
|
bufferStart = buffer;
|
|
662
676
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
677
|
+
#define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe;
|
|
678
|
+
|
|
679
|
+
while (pe < stringEnd && (pe = json_next_backslash(pe, stringEnd, positions))) {
|
|
666
680
|
if (pe > p) {
|
|
667
681
|
MEMCPY(buffer, p, char, pe - p);
|
|
668
682
|
buffer += pe - p;
|
|
669
683
|
}
|
|
670
684
|
switch (*++pe) {
|
|
685
|
+
case '"':
|
|
686
|
+
case '/':
|
|
687
|
+
p = pe; // nothing to unescape just need to skip the backslash
|
|
688
|
+
break;
|
|
689
|
+
case '\\':
|
|
690
|
+
APPEND_CHAR('\\');
|
|
691
|
+
break;
|
|
671
692
|
case 'n':
|
|
672
|
-
|
|
693
|
+
APPEND_CHAR('\n');
|
|
673
694
|
break;
|
|
674
695
|
case 'r':
|
|
675
|
-
|
|
696
|
+
APPEND_CHAR('\r');
|
|
676
697
|
break;
|
|
677
698
|
case 't':
|
|
678
|
-
|
|
679
|
-
break;
|
|
680
|
-
case '"':
|
|
681
|
-
unescape = (char *) "\"";
|
|
682
|
-
break;
|
|
683
|
-
case '\\':
|
|
684
|
-
unescape = (char *) "\\";
|
|
699
|
+
APPEND_CHAR('\t');
|
|
685
700
|
break;
|
|
686
701
|
case 'b':
|
|
687
|
-
|
|
702
|
+
APPEND_CHAR('\b');
|
|
688
703
|
break;
|
|
689
704
|
case 'f':
|
|
690
|
-
|
|
705
|
+
APPEND_CHAR('\f');
|
|
691
706
|
break;
|
|
692
707
|
case 'u':
|
|
693
708
|
if (pe > stringEnd - 5) {
|
|
@@ -725,18 +740,23 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
|
|
|
725
740
|
break;
|
|
726
741
|
}
|
|
727
742
|
}
|
|
728
|
-
|
|
729
|
-
|
|
743
|
+
|
|
744
|
+
char buf[4];
|
|
745
|
+
int unescape_len = convert_UTF32_to_UTF8(buf, ch);
|
|
746
|
+
MEMCPY(buffer, buf, char, unescape_len);
|
|
747
|
+
buffer += unescape_len;
|
|
748
|
+
p = ++pe;
|
|
730
749
|
}
|
|
731
750
|
break;
|
|
732
751
|
default:
|
|
733
|
-
|
|
734
|
-
|
|
752
|
+
if ((unsigned char)*pe < 0x20) {
|
|
753
|
+
raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
|
|
754
|
+
}
|
|
755
|
+
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
|
|
756
|
+
break;
|
|
735
757
|
}
|
|
736
|
-
MEMCPY(buffer, unescape, char, unescape_len);
|
|
737
|
-
buffer += unescape_len;
|
|
738
|
-
p = ++pe;
|
|
739
758
|
}
|
|
759
|
+
#undef APPEND_CHAR
|
|
740
760
|
|
|
741
761
|
if (stringEnd > p) {
|
|
742
762
|
MEMCPY(buffer, p, char, stringEnd - p);
|
|
@@ -900,20 +920,6 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
|
|
|
900
920
|
return object;
|
|
901
921
|
}
|
|
902
922
|
|
|
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
923
|
static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value)
|
|
918
924
|
{
|
|
919
925
|
if (RB_UNLIKELY(config->on_load_proc)) {
|
|
@@ -940,7 +946,7 @@ static const bool string_scan_table[256] = {
|
|
|
940
946
|
static SIMD_Implementation simd_impl = SIMD_NONE;
|
|
941
947
|
#endif /* HAVE_SIMD */
|
|
942
948
|
|
|
943
|
-
|
|
949
|
+
ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
|
|
944
950
|
{
|
|
945
951
|
#ifdef HAVE_SIMD
|
|
946
952
|
#if defined(HAVE_SIMD_NEON)
|
|
@@ -948,7 +954,7 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
|
|
|
948
954
|
uint64_t mask = 0;
|
|
949
955
|
if (string_scan_simd_neon(&state->cursor, state->end, &mask)) {
|
|
950
956
|
state->cursor += trailing_zeros64(mask) >> 2;
|
|
951
|
-
return
|
|
957
|
+
return true;
|
|
952
958
|
}
|
|
953
959
|
|
|
954
960
|
#elif defined(HAVE_SIMD_SSE2)
|
|
@@ -956,7 +962,7 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
|
|
|
956
962
|
int mask = 0;
|
|
957
963
|
if (string_scan_simd_sse2(&state->cursor, state->end, &mask)) {
|
|
958
964
|
state->cursor += trailing_zeros(mask);
|
|
959
|
-
return
|
|
965
|
+
return true;
|
|
960
966
|
}
|
|
961
967
|
}
|
|
962
968
|
#endif /* HAVE_SIMD_NEON or HAVE_SIMD_SSE2 */
|
|
@@ -964,32 +970,37 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
|
|
|
964
970
|
|
|
965
971
|
while (!eos(state)) {
|
|
966
972
|
if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) {
|
|
967
|
-
return
|
|
973
|
+
return true;
|
|
968
974
|
}
|
|
969
975
|
state->cursor++;
|
|
970
976
|
}
|
|
971
|
-
return
|
|
977
|
+
return false;
|
|
972
978
|
}
|
|
973
979
|
|
|
974
|
-
static
|
|
980
|
+
static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name, const char *start)
|
|
975
981
|
{
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
982
|
+
const char *backslashes[JSON_MAX_UNESCAPE_POSITIONS];
|
|
983
|
+
JSON_UnescapePositions positions = {
|
|
984
|
+
.size = 0,
|
|
985
|
+
.positions = backslashes,
|
|
986
|
+
.has_more = false,
|
|
987
|
+
};
|
|
979
988
|
|
|
980
|
-
|
|
989
|
+
do {
|
|
981
990
|
switch (*state->cursor) {
|
|
982
991
|
case '"': {
|
|
983
|
-
VALUE string =
|
|
992
|
+
VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions);
|
|
984
993
|
state->cursor++;
|
|
985
994
|
return json_push_value(state, config, string);
|
|
986
995
|
}
|
|
987
996
|
case '\\': {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
997
|
+
if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) {
|
|
998
|
+
backslashes[positions.size] = state->cursor;
|
|
999
|
+
positions.size++;
|
|
1000
|
+
} else {
|
|
1001
|
+
positions.has_more = true;
|
|
992
1002
|
}
|
|
1003
|
+
state->cursor++;
|
|
993
1004
|
break;
|
|
994
1005
|
}
|
|
995
1006
|
default:
|
|
@@ -998,12 +1009,29 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig
|
|
|
998
1009
|
}
|
|
999
1010
|
|
|
1000
1011
|
state->cursor++;
|
|
1001
|
-
}
|
|
1012
|
+
} while (string_scan(state));
|
|
1002
1013
|
|
|
1003
1014
|
raise_parse_error("unexpected end of input, expected closing \"", state);
|
|
1004
1015
|
return Qfalse;
|
|
1005
1016
|
}
|
|
1006
1017
|
|
|
1018
|
+
ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name)
|
|
1019
|
+
{
|
|
1020
|
+
state->cursor++;
|
|
1021
|
+
const char *start = state->cursor;
|
|
1022
|
+
|
|
1023
|
+
if (RB_UNLIKELY(!string_scan(state))) {
|
|
1024
|
+
raise_parse_error("unexpected end of input, expected closing \"", state);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (RB_LIKELY(*state->cursor == '"')) {
|
|
1028
|
+
VALUE string = json_string_fastpath(state, config, start, state->cursor, is_name);
|
|
1029
|
+
state->cursor++;
|
|
1030
|
+
return json_push_value(state, config, string);
|
|
1031
|
+
}
|
|
1032
|
+
return json_parse_escaped_string(state, config, is_name, start);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1007
1035
|
#if JSON_CPU_LITTLE_ENDIAN_64BITS
|
|
1008
1036
|
// From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/
|
|
1009
1037
|
// Additional References:
|
|
@@ -1477,6 +1505,7 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
|
|
|
1477
1505
|
*/
|
|
1478
1506
|
static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
|
1479
1507
|
{
|
|
1508
|
+
rb_check_frozen(self);
|
|
1480
1509
|
GET_PARSER_CONFIG;
|
|
1481
1510
|
|
|
1482
1511
|
parser_config_init(config, opts);
|
|
@@ -1572,7 +1601,7 @@ static const rb_data_type_t JSON_ParserConfig_type = {
|
|
|
1572
1601
|
JSON_ParserConfig_memsize,
|
|
1573
1602
|
},
|
|
1574
1603
|
0, 0,
|
|
1575
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
1604
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
1576
1605
|
};
|
|
1577
1606
|
|
|
1578
1607
|
static VALUE cJSON_parser_s_allocate(VALUE klass)
|
|
@@ -1622,10 +1651,6 @@ void Init_parser(void)
|
|
|
1622
1651
|
sym_decimal_class = ID2SYM(rb_intern("decimal_class"));
|
|
1623
1652
|
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
|
1624
1653
|
|
|
1625
|
-
i_chr = rb_intern("chr");
|
|
1626
|
-
i_aset = rb_intern("[]=");
|
|
1627
|
-
i_aref = rb_intern("[]");
|
|
1628
|
-
i_leftshift = rb_intern("<<");
|
|
1629
1654
|
i_new = rb_intern("new");
|
|
1630
1655
|
i_try_convert = rb_intern("try_convert");
|
|
1631
1656
|
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,8 +491,11 @@ 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
|
|
@@ -559,17 +559,19 @@ module JSON
|
|
|
559
559
|
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
|
|
560
560
|
end
|
|
561
561
|
result << value.to_json(state)
|
|
562
|
+
state.depth = depth
|
|
562
563
|
else
|
|
563
564
|
raise GeneratorError.new("#{value.class} not allowed in JSON", value)
|
|
564
565
|
end
|
|
565
566
|
elsif value.respond_to?(:to_json)
|
|
566
567
|
result << value.to_json(state)
|
|
568
|
+
state.depth = depth
|
|
567
569
|
else
|
|
568
570
|
result << %{"#{String(value)}"}
|
|
569
571
|
end
|
|
570
572
|
first = false
|
|
571
573
|
}
|
|
572
|
-
depth
|
|
574
|
+
depth -= 1
|
|
573
575
|
unless first
|
|
574
576
|
result << state.object_nl
|
|
575
577
|
result << state.indent * depth if indent
|
|
@@ -586,8 +588,11 @@ module JSON
|
|
|
586
588
|
# produced JSON string output further.
|
|
587
589
|
def to_json(state = nil, *)
|
|
588
590
|
state = State.from_state(state)
|
|
591
|
+
depth = state.depth
|
|
589
592
|
state.check_max_nesting
|
|
590
593
|
json_transform(state)
|
|
594
|
+
ensure
|
|
595
|
+
state.depth = depth
|
|
591
596
|
end
|
|
592
597
|
|
|
593
598
|
private
|
|
@@ -625,12 +630,13 @@ module JSON
|
|
|
625
630
|
end
|
|
626
631
|
elsif value.respond_to?(:to_json)
|
|
627
632
|
result << value.to_json(state)
|
|
633
|
+
state.depth = depth
|
|
628
634
|
else
|
|
629
635
|
result << %{"#{String(value)}"}
|
|
630
636
|
end
|
|
631
637
|
first = false
|
|
632
638
|
}
|
|
633
|
-
depth
|
|
639
|
+
depth -= 1
|
|
634
640
|
result << state.array_nl
|
|
635
641
|
result << state.indent * depth if indent
|
|
636
642
|
result << ']'
|
|
@@ -655,6 +661,9 @@ module JSON
|
|
|
655
661
|
if casted_value.equal?(self)
|
|
656
662
|
raise GeneratorError.new("#{self} not allowed in JSON", self)
|
|
657
663
|
end
|
|
664
|
+
unless Generator.native_type?(casted_value)
|
|
665
|
+
raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
|
|
666
|
+
end
|
|
658
667
|
|
|
659
668
|
state.check_max_nesting
|
|
660
669
|
state.depth += 1
|
data/lib/json/version.rb
CHANGED