json 2.17.1 → 2.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +10 -1
- data/ext/json/ext/fbuffer/fbuffer.h +8 -6
- data/ext/json/ext/generator/generator.c +48 -26
- data/ext/json/ext/json.h +4 -0
- data/ext/json/ext/parser/parser.c +76 -72
- data/ext/json/ext/simd/simd.h +28 -1
- data/ext/json/ext/vendor/fpconv.c +1 -1
- data/lib/json/truffle_ruby/generator.rb +0 -5
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +21 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb2890db4c527125d27bc7c21fc64d3ac532ffbec8080f89a678daf48c36e09e
|
|
4
|
+
data.tar.gz: c4b37d085d05d3c43df97b3c24898dc6be61c76ba64c749b5a8a86bf4fc1198d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb55ef5a0aa6961ef0fe3bb30f398834820357045ad27a8fdb7e53eaba3af7c4d356ef26c0e73b7a87d2d9d51e500eae7193d7d1ae3aa1058c7973bcc462674b
|
|
7
|
+
data.tar.gz: bfb499789bbcee7f5f8d67e32ded664dc62c632ae39fe80bf4bff3d6aec16eee3730a7c4883216a393326f2ab33e94e5f9c58da4c5b31627347108c36c2b211c
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-02-03 (2.18.1)
|
|
6
|
+
|
|
7
|
+
* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
|
|
8
|
+
without first invoking a user defined `#to_json` method.
|
|
9
|
+
|
|
10
|
+
### 2025-12-11 (2.18.0)
|
|
11
|
+
|
|
12
|
+
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
13
|
+
|
|
5
14
|
### 2025-12-04 (2.17.1)
|
|
6
15
|
|
|
7
16
|
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
@@ -62,7 +71,7 @@
|
|
|
62
71
|
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
|
63
72
|
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
|
64
73
|
* Fix `JSON.unsafe_load` usage with proc
|
|
65
|
-
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
74
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
66
75
|
* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
|
|
67
76
|
|
|
68
77
|
### 2025-07-28 (2.13.2)
|
|
@@ -161,23 +161,25 @@ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
|
161
161
|
|
|
162
162
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
163
163
|
{
|
|
164
|
-
const char *
|
|
165
|
-
unsigned long len
|
|
164
|
+
const char *ptr;
|
|
165
|
+
unsigned long len;
|
|
166
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
166
167
|
|
|
167
|
-
fbuffer_append(fb,
|
|
168
|
+
fbuffer_append(fb, ptr, len);
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
171
172
|
{
|
|
172
|
-
const char *
|
|
173
|
-
unsigned long len
|
|
173
|
+
const char *ptr;
|
|
174
|
+
unsigned long len;
|
|
175
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
174
176
|
|
|
175
177
|
fbuffer_inc_capa(fb, repeat * len);
|
|
176
178
|
while (repeat) {
|
|
177
179
|
#if JSON_DEBUG
|
|
178
180
|
fb->requested = len;
|
|
179
181
|
#endif
|
|
180
|
-
fbuffer_append_reserved(fb,
|
|
182
|
+
fbuffer_append_reserved(fb, ptr, len);
|
|
181
183
|
repeat--;
|
|
182
184
|
}
|
|
183
185
|
}
|
|
@@ -63,6 +63,8 @@ struct generate_json_data {
|
|
|
63
63
|
long depth;
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
static SIMD_Implementation simd_impl;
|
|
67
|
+
|
|
66
68
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
67
69
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
|
68
70
|
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -155,8 +157,6 @@ static const unsigned char escape_table_basic[256] = {
|
|
|
155
157
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
156
158
|
};
|
|
157
159
|
|
|
158
|
-
static unsigned char (*search_escape_basic_impl)(search_state *);
|
|
159
|
-
|
|
160
160
|
static inline unsigned char search_escape_basic(search_state *search)
|
|
161
161
|
{
|
|
162
162
|
while (search->ptr < search->end) {
|
|
@@ -212,11 +212,39 @@ ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
|
212
212
|
* Everything else (should be UTF-8) is just passed through and
|
|
213
213
|
* appended to the result.
|
|
214
214
|
*/
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
#if defined(HAVE_SIMD_NEON)
|
|
218
|
+
static inline unsigned char search_escape_basic_neon(search_state *search);
|
|
219
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
220
|
+
static inline unsigned char search_escape_basic_sse2(search_state *search);
|
|
221
|
+
#endif
|
|
222
|
+
|
|
223
|
+
static inline unsigned char search_escape_basic(search_state *search);
|
|
224
|
+
|
|
215
225
|
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
216
226
|
{
|
|
217
|
-
|
|
227
|
+
#ifdef HAVE_SIMD
|
|
228
|
+
#if defined(HAVE_SIMD_NEON)
|
|
229
|
+
while (search_escape_basic_neon(search)) {
|
|
230
|
+
escape_UTF8_char_basic(search);
|
|
231
|
+
}
|
|
232
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
233
|
+
if (simd_impl == SIMD_SSE2) {
|
|
234
|
+
while (search_escape_basic_sse2(search)) {
|
|
235
|
+
escape_UTF8_char_basic(search);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
while (search_escape_basic(search)) {
|
|
240
|
+
escape_UTF8_char_basic(search);
|
|
241
|
+
}
|
|
242
|
+
#endif
|
|
243
|
+
#else
|
|
244
|
+
while (search_escape_basic(search)) {
|
|
218
245
|
escape_UTF8_char_basic(search);
|
|
219
246
|
}
|
|
247
|
+
#endif /* HAVE_SIMD */
|
|
220
248
|
}
|
|
221
249
|
|
|
222
250
|
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
@@ -260,6 +288,8 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
260
288
|
|
|
261
289
|
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
262
290
|
{
|
|
291
|
+
RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
|
|
292
|
+
|
|
263
293
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
264
294
|
search_flush(search);
|
|
265
295
|
|
|
@@ -269,12 +299,18 @@ ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned
|
|
|
269
299
|
char *s = (buf->ptr + buf->len);
|
|
270
300
|
|
|
271
301
|
// Pad the buffer with dummy characters that won't need escaping.
|
|
272
|
-
// This seem
|
|
273
|
-
|
|
302
|
+
// This seem wasteful at first sight, but memset of vector length is very fast.
|
|
303
|
+
// This is a space as it can be directly represented as an immediate on AArch64.
|
|
304
|
+
memset(s, ' ', vec_len);
|
|
274
305
|
|
|
275
306
|
// Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
|
|
276
307
|
// to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
|
|
277
|
-
|
|
308
|
+
if (vec_len == 16) {
|
|
309
|
+
RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
|
|
310
|
+
json_fast_memcpy16(s, search->ptr, len);
|
|
311
|
+
} else {
|
|
312
|
+
MEMCPY(s, search->ptr, char, len);
|
|
313
|
+
}
|
|
278
314
|
|
|
279
315
|
return s;
|
|
280
316
|
}
|
|
@@ -1091,6 +1127,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data
|
|
|
1091
1127
|
search.matches_mask = 0;
|
|
1092
1128
|
search.has_matches = false;
|
|
1093
1129
|
search.chunk_base = NULL;
|
|
1130
|
+
search.chunk_end = NULL;
|
|
1094
1131
|
#endif /* HAVE_SIMD */
|
|
1095
1132
|
|
|
1096
1133
|
switch (rb_enc_str_coderange(obj)) {
|
|
@@ -1337,7 +1374,7 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1337
1374
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1338
1375
|
{
|
|
1339
1376
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1340
|
-
fbuffer_append_str(buffer, tmp);
|
|
1377
|
+
fbuffer_append_str(buffer, StringValue(tmp));
|
|
1341
1378
|
}
|
|
1342
1379
|
|
|
1343
1380
|
#ifdef RUBY_INTEGER_UNIFICATION
|
|
@@ -1503,7 +1540,9 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
|
1503
1540
|
.obj = obj,
|
|
1504
1541
|
.func = func
|
|
1505
1542
|
};
|
|
1506
|
-
|
|
1543
|
+
VALUE result = rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1544
|
+
RB_GC_GUARD(self);
|
|
1545
|
+
return result;
|
|
1507
1546
|
}
|
|
1508
1547
|
|
|
1509
1548
|
/* call-seq:
|
|
@@ -2181,22 +2220,5 @@ void Init_generator(void)
|
|
|
2181
2220
|
|
|
2182
2221
|
rb_require("json/ext/generator/state");
|
|
2183
2222
|
|
|
2184
|
-
|
|
2185
|
-
switch (find_simd_implementation()) {
|
|
2186
|
-
#ifdef HAVE_SIMD
|
|
2187
|
-
#ifdef HAVE_SIMD_NEON
|
|
2188
|
-
case SIMD_NEON:
|
|
2189
|
-
search_escape_basic_impl = search_escape_basic_neon;
|
|
2190
|
-
break;
|
|
2191
|
-
#endif /* HAVE_SIMD_NEON */
|
|
2192
|
-
#ifdef HAVE_SIMD_SSE2
|
|
2193
|
-
case SIMD_SSE2:
|
|
2194
|
-
search_escape_basic_impl = search_escape_basic_sse2;
|
|
2195
|
-
break;
|
|
2196
|
-
#endif /* HAVE_SIMD_SSE2 */
|
|
2197
|
-
#endif /* HAVE_SIMD */
|
|
2198
|
-
default:
|
|
2199
|
-
search_escape_basic_impl = search_escape_basic;
|
|
2200
|
-
break;
|
|
2201
|
-
}
|
|
2223
|
+
simd_impl = find_simd_implementation();
|
|
2202
2224
|
}
|
data/ext/json/ext/json.h
CHANGED
|
@@ -7,7 +7,7 @@ static VALUE CNaN, CInfinity, CMinusInfinity;
|
|
|
7
7
|
|
|
8
8
|
static ID i_new, i_try_convert, i_uminus, i_encode;
|
|
9
9
|
|
|
10
|
-
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,
|
|
11
11
|
sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
|
|
12
12
|
|
|
13
13
|
static int binary_encindex;
|
|
@@ -335,6 +335,7 @@ typedef struct JSON_ParserStruct {
|
|
|
335
335
|
int max_nesting;
|
|
336
336
|
bool allow_nan;
|
|
337
337
|
bool allow_trailing_comma;
|
|
338
|
+
bool allow_control_characters;
|
|
338
339
|
bool symbolize_names;
|
|
339
340
|
bool freeze;
|
|
340
341
|
} JSON_ParserConfig;
|
|
@@ -476,23 +477,24 @@ static const signed char digit_values[256] = {
|
|
|
476
477
|
-1, -1, -1, -1, -1, -1, -1
|
|
477
478
|
};
|
|
478
479
|
|
|
479
|
-
static uint32_t unescape_unicode(JSON_ParserState *state, const
|
|
480
|
+
static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe)
|
|
480
481
|
{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
482
|
+
if (RB_UNLIKELY(sp > spe - 4)) {
|
|
483
|
+
raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const unsigned char *p = (const unsigned char *)sp;
|
|
487
|
+
|
|
488
|
+
const signed char b0 = digit_values[p[0]];
|
|
489
|
+
const signed char b1 = digit_values[p[1]];
|
|
490
|
+
const signed char b2 = digit_values[p[2]];
|
|
491
|
+
const signed char b3 = digit_values[p[3]];
|
|
492
|
+
|
|
493
|
+
if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) {
|
|
494
|
+
raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3;
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
#define GET_PARSER_CONFIG \
|
|
@@ -642,7 +644,7 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserCon
|
|
|
642
644
|
typedef struct _json_unescape_positions {
|
|
643
645
|
long size;
|
|
644
646
|
const char **positions;
|
|
645
|
-
|
|
647
|
+
unsigned long additional_backslashes;
|
|
646
648
|
} JSON_UnescapePositions;
|
|
647
649
|
|
|
648
650
|
static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
|
|
@@ -656,7 +658,8 @@ static inline const char *json_next_backslash(const char *pe, const char *string
|
|
|
656
658
|
}
|
|
657
659
|
}
|
|
658
660
|
|
|
659
|
-
if (positions->
|
|
661
|
+
if (positions->additional_backslashes) {
|
|
662
|
+
positions->additional_backslashes--;
|
|
660
663
|
return memchr(pe, '\\', stringEnd - pe);
|
|
661
664
|
}
|
|
662
665
|
|
|
@@ -706,55 +709,52 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
706
709
|
case 'f':
|
|
707
710
|
APPEND_CHAR('\f');
|
|
708
711
|
break;
|
|
709
|
-
case 'u':
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
if (
|
|
728
|
-
raise_parse_error_at("
|
|
729
|
-
}
|
|
730
|
-
if (pe[0] == '\\' && pe[1] == 'u') {
|
|
731
|
-
uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2);
|
|
732
|
-
|
|
733
|
-
if ((sur & 0xFC00) != 0xDC00) {
|
|
734
|
-
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
|
738
|
-
| (sur & 0x3FF));
|
|
739
|
-
pe += 5;
|
|
740
|
-
} else {
|
|
741
|
-
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
742
|
-
break;
|
|
712
|
+
case 'u': {
|
|
713
|
+
uint32_t ch = unescape_unicode(state, ++pe, stringEnd);
|
|
714
|
+
pe += 3;
|
|
715
|
+
/* To handle values above U+FFFF, we take a sequence of
|
|
716
|
+
* \uXXXX escapes in the U+D800..U+DBFF then
|
|
717
|
+
* U+DC00..U+DFFF ranges, take the low 10 bits from each
|
|
718
|
+
* to make a 20-bit number, then add 0x10000 to get the
|
|
719
|
+
* final codepoint.
|
|
720
|
+
*
|
|
721
|
+
* See Unicode 15: 3.8 "Surrogates", 5.3 "Handling
|
|
722
|
+
* Surrogate Pairs in UTF-16", and 23.6 "Surrogates
|
|
723
|
+
* Area".
|
|
724
|
+
*/
|
|
725
|
+
if ((ch & 0xFC00) == 0xD800) {
|
|
726
|
+
pe++;
|
|
727
|
+
if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) {
|
|
728
|
+
uint32_t sur = unescape_unicode(state, pe + 2, stringEnd);
|
|
729
|
+
|
|
730
|
+
if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) {
|
|
731
|
+
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
|
743
732
|
}
|
|
744
|
-
}
|
|
745
733
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
734
|
+
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF));
|
|
735
|
+
pe += 5;
|
|
736
|
+
} else {
|
|
737
|
+
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
751
740
|
}
|
|
741
|
+
|
|
742
|
+
int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
|
|
743
|
+
buffer += unescape_len;
|
|
744
|
+
p = ++pe;
|
|
752
745
|
break;
|
|
746
|
+
}
|
|
753
747
|
default:
|
|
754
748
|
if ((unsigned char)*pe < 0x20) {
|
|
755
|
-
|
|
749
|
+
if (!config->allow_control_characters) {
|
|
750
|
+
if (*pe == '\n') {
|
|
751
|
+
raise_parse_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1);
|
|
752
|
+
}
|
|
753
|
+
raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
|
|
754
|
+
}
|
|
755
|
+
} else {
|
|
756
|
+
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
|
|
756
757
|
}
|
|
757
|
-
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
|
|
758
758
|
break;
|
|
759
759
|
}
|
|
760
760
|
}
|
|
@@ -985,7 +985,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
|
|
|
985
985
|
JSON_UnescapePositions positions = {
|
|
986
986
|
.size = 0,
|
|
987
987
|
.positions = backslashes,
|
|
988
|
-
.
|
|
988
|
+
.additional_backslashes = 0,
|
|
989
989
|
};
|
|
990
990
|
|
|
991
991
|
do {
|
|
@@ -1000,13 +1000,15 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
|
|
|
1000
1000
|
backslashes[positions.size] = state->cursor;
|
|
1001
1001
|
positions.size++;
|
|
1002
1002
|
} else {
|
|
1003
|
-
positions.
|
|
1003
|
+
positions.additional_backslashes++;
|
|
1004
1004
|
}
|
|
1005
1005
|
state->cursor++;
|
|
1006
1006
|
break;
|
|
1007
1007
|
}
|
|
1008
1008
|
default:
|
|
1009
|
-
|
|
1009
|
+
if (!config->allow_control_characters) {
|
|
1010
|
+
raise_parse_error("invalid ASCII control character in string: %s", state);
|
|
1011
|
+
}
|
|
1010
1012
|
break;
|
|
1011
1013
|
}
|
|
1012
1014
|
|
|
@@ -1427,14 +1429,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1427
1429
|
{
|
|
1428
1430
|
JSON_ParserConfig *config = (JSON_ParserConfig *)data;
|
|
1429
1431
|
|
|
1430
|
-
if (key == sym_max_nesting)
|
|
1431
|
-
else if (key == sym_allow_nan)
|
|
1432
|
-
else if (key == sym_allow_trailing_comma)
|
|
1433
|
-
else if (key ==
|
|
1434
|
-
else if (key ==
|
|
1435
|
-
else if (key ==
|
|
1436
|
-
else if (key ==
|
|
1437
|
-
else if (key ==
|
|
1432
|
+
if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
|
|
1433
|
+
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
1434
|
+
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
|
1435
|
+
else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
|
|
1436
|
+
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1437
|
+
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1438
|
+
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
|
1439
|
+
else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1440
|
+
else if (key == sym_decimal_class) {
|
|
1438
1441
|
if (RTEST(val)) {
|
|
1439
1442
|
if (rb_respond_to(val, i_try_convert)) {
|
|
1440
1443
|
config->decimal_class = val;
|
|
@@ -1647,6 +1650,7 @@ void Init_parser(void)
|
|
|
1647
1650
|
sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
|
|
1648
1651
|
sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
|
|
1649
1652
|
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
|
|
1653
|
+
sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
|
|
1650
1654
|
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
|
1651
1655
|
sym_freeze = ID2SYM(rb_intern("freeze"));
|
|
1652
1656
|
sym_on_load = ID2SYM(rb_intern("on_load"));
|
data/ext/json/ext/simd/simd.h
CHANGED
|
@@ -58,7 +58,34 @@ static inline int trailing_zeros(int input)
|
|
|
58
58
|
|
|
59
59
|
#ifdef JSON_ENABLE_SIMD
|
|
60
60
|
|
|
61
|
-
#define SIMD_MINIMUM_THRESHOLD
|
|
61
|
+
#define SIMD_MINIMUM_THRESHOLD 4
|
|
62
|
+
|
|
63
|
+
ALWAYS_INLINE(static) void json_fast_memcpy16(char *dst, const char *src, size_t len)
|
|
64
|
+
{
|
|
65
|
+
RBIMPL_ASSERT_OR_ASSUME(len < 16);
|
|
66
|
+
RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD); // 4
|
|
67
|
+
#if defined(__has_builtin) && __has_builtin(__builtin_memcpy)
|
|
68
|
+
// If __builtin_memcpy is available, use it to copy between SIMD_MINIMUM_THRESHOLD (4) and vec_len-1 (15) bytes.
|
|
69
|
+
// These copies overlap. The first copy will copy the first 8 (or 4) bytes. The second copy will copy
|
|
70
|
+
// the last 8 (or 4) bytes but overlap with the first copy. The overlapping bytes will be in the correct
|
|
71
|
+
// position in both copies.
|
|
72
|
+
|
|
73
|
+
// Please do not attempt to replace __builtin_memcpy with memcpy without profiling and/or looking at the
|
|
74
|
+
// generated assembly. On clang-specifically (tested on Apple clang version 17.0.0 (clang-1700.0.13.3)),
|
|
75
|
+
// when using memcpy, the compiler will notice the only difference is a 4 or 8 and generate a conditional
|
|
76
|
+
// select instruction instead of direct loads and stores with a branch. This ends up slower than the branch
|
|
77
|
+
// plus two loads and stores generated when using __builtin_memcpy.
|
|
78
|
+
if (len >= 8) {
|
|
79
|
+
__builtin_memcpy(dst, src, 8);
|
|
80
|
+
__builtin_memcpy(dst + len - 8, src + len - 8, 8);
|
|
81
|
+
} else {
|
|
82
|
+
__builtin_memcpy(dst, src, 4);
|
|
83
|
+
__builtin_memcpy(dst + len - 4, src + len - 4, 4);
|
|
84
|
+
}
|
|
85
|
+
#else
|
|
86
|
+
MEMCPY(dst, src, char, len);
|
|
87
|
+
#endif
|
|
88
|
+
}
|
|
62
89
|
|
|
63
90
|
#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64)
|
|
64
91
|
#include <arm_neon.h>
|
data/lib/json/version.rb
CHANGED
data/lib/json.rb
CHANGED
|
@@ -6,6 +6,15 @@ require 'json/common'
|
|
|
6
6
|
#
|
|
7
7
|
# \JSON is a lightweight data-interchange format.
|
|
8
8
|
#
|
|
9
|
+
# \JSON is easy for us humans to read and write,
|
|
10
|
+
# and equally simple for machines to read (parse) and write (generate).
|
|
11
|
+
#
|
|
12
|
+
# \JSON is language-independent, making it an ideal interchange format
|
|
13
|
+
# for applications in differing programming languages
|
|
14
|
+
# and on differing operating systems.
|
|
15
|
+
#
|
|
16
|
+
# == \JSON Values
|
|
17
|
+
#
|
|
9
18
|
# A \JSON value is one of the following:
|
|
10
19
|
# - Double-quoted text: <tt>"foo"</tt>.
|
|
11
20
|
# - Number: +1+, +1.0+, +2.0e2+.
|
|
@@ -173,6 +182,18 @@ require 'json/common'
|
|
|
173
182
|
# When enabled:
|
|
174
183
|
# JSON.parse('[1,]', allow_trailing_comma: true) # => [1]
|
|
175
184
|
#
|
|
185
|
+
# ---
|
|
186
|
+
#
|
|
187
|
+
# Option +allow_control_characters+ (boolean) specifies whether to allow
|
|
188
|
+
# unescaped ASCII control characters, such as newlines, in strings;
|
|
189
|
+
# defaults to +false+.
|
|
190
|
+
#
|
|
191
|
+
# With the default, +false+:
|
|
192
|
+
# JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError)
|
|
193
|
+
#
|
|
194
|
+
# When enabled:
|
|
195
|
+
# JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld"
|
|
196
|
+
#
|
|
176
197
|
# ====== Output Options
|
|
177
198
|
#
|
|
178
199
|
# Option +freeze+ (boolean) specifies whether the returned objects will be frozen;
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.18.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Frank
|
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: '0'
|
|
86
86
|
requirements: []
|
|
87
|
-
rubygems_version:
|
|
87
|
+
rubygems_version: 4.1.0.dev
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: JSON Implementation for Ruby
|
|
90
90
|
test_files: []
|