json 2.18.0 → 2.19.3
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 +23 -1
- data/ext/json/ext/fbuffer/fbuffer.h +23 -19
- data/ext/json/ext/generator/generator.c +97 -314
- data/ext/json/ext/json.h +8 -0
- data/ext/json/ext/parser/parser.c +121 -103
- data/ext/json/ext/simd/simd.h +28 -11
- data/ext/json/ext/vendor/fpconv.c +1 -1
- data/lib/json/common.rb +41 -10
- data/lib/json/ext/generator/state.rb +1 -1
- data/lib/json/truffle_ruby/generator.rb +13 -1
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +33 -0
- metadata +2 -2
|
@@ -7,8 +7,9 @@ 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_allow_control_characters,
|
|
11
|
-
sym_decimal_class, sym_on_load,
|
|
10
|
+
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters,
|
|
11
|
+
sym_allow_invalid_escape, sym_symbolize_names, sym_freeze, sym_decimal_class, sym_on_load,
|
|
12
|
+
sym_allow_duplicate_key;
|
|
12
13
|
|
|
13
14
|
static int binary_encindex;
|
|
14
15
|
static int utf8_encindex;
|
|
@@ -336,6 +337,7 @@ typedef struct JSON_ParserStruct {
|
|
|
336
337
|
bool allow_nan;
|
|
337
338
|
bool allow_trailing_comma;
|
|
338
339
|
bool allow_control_characters;
|
|
340
|
+
bool allow_invalid_escape;
|
|
339
341
|
bool symbolize_names;
|
|
340
342
|
bool freeze;
|
|
341
343
|
} JSON_ParserConfig;
|
|
@@ -400,14 +402,9 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
|
|
|
400
402
|
|
|
401
403
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
|
402
404
|
|
|
403
|
-
|
|
404
|
-
RBIMPL_ATTR_NORETURN()
|
|
405
|
-
#endif
|
|
406
|
-
static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
405
|
+
static VALUE build_parse_error_message(const char *format, JSON_ParserState *state, long line, long column)
|
|
407
406
|
{
|
|
408
407
|
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
|
|
409
|
-
long line, column;
|
|
410
|
-
cursor_position(state, &line, &column);
|
|
411
408
|
|
|
412
409
|
const char *ptr = "EOF";
|
|
413
410
|
if (state->cursor && state->cursor < state->end) {
|
|
@@ -442,17 +439,26 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
|
442
439
|
VALUE msg = rb_sprintf(format, ptr);
|
|
443
440
|
VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
|
|
444
441
|
RB_GC_GUARD(msg);
|
|
442
|
+
return message;
|
|
443
|
+
}
|
|
445
444
|
|
|
445
|
+
static VALUE parse_error_new(VALUE message, long line, long column)
|
|
446
|
+
{
|
|
446
447
|
VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
|
|
447
448
|
rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
|
|
448
449
|
rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
|
|
449
|
-
|
|
450
|
+
return exc;
|
|
450
451
|
}
|
|
451
452
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
453
|
+
NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
454
|
+
{
|
|
455
|
+
long line, column;
|
|
456
|
+
cursor_position(state, &line, &column);
|
|
457
|
+
VALUE message = build_parse_error_message(format, state, line, column);
|
|
458
|
+
rb_exc_raise(parse_error_new(message, line, column));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
NORETURN(static) void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at)
|
|
456
462
|
{
|
|
457
463
|
state->cursor = at;
|
|
458
464
|
raise_parse_error(format, state);
|
|
@@ -477,23 +483,24 @@ static const signed char digit_values[256] = {
|
|
|
477
483
|
-1, -1, -1, -1, -1, -1, -1
|
|
478
484
|
};
|
|
479
485
|
|
|
480
|
-
static uint32_t unescape_unicode(JSON_ParserState *state, const
|
|
481
|
-
{
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
486
|
+
static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe)
|
|
487
|
+
{
|
|
488
|
+
if (RB_UNLIKELY(sp > spe - 4)) {
|
|
489
|
+
raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const unsigned char *p = (const unsigned char *)sp;
|
|
493
|
+
|
|
494
|
+
const signed char b0 = digit_values[p[0]];
|
|
495
|
+
const signed char b1 = digit_values[p[1]];
|
|
496
|
+
const signed char b2 = digit_values[p[2]];
|
|
497
|
+
const signed char b3 = digit_values[p[3]];
|
|
498
|
+
|
|
499
|
+
if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) {
|
|
500
|
+
raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3;
|
|
497
504
|
}
|
|
498
505
|
|
|
499
506
|
#define GET_PARSER_CONFIG \
|
|
@@ -643,7 +650,7 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserCon
|
|
|
643
650
|
typedef struct _json_unescape_positions {
|
|
644
651
|
long size;
|
|
645
652
|
const char **positions;
|
|
646
|
-
|
|
653
|
+
unsigned long additional_backslashes;
|
|
647
654
|
} JSON_UnescapePositions;
|
|
648
655
|
|
|
649
656
|
static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
|
|
@@ -657,7 +664,8 @@ static inline const char *json_next_backslash(const char *pe, const char *string
|
|
|
657
664
|
}
|
|
658
665
|
}
|
|
659
666
|
|
|
660
|
-
if (positions->
|
|
667
|
+
if (positions->additional_backslashes) {
|
|
668
|
+
positions->additional_backslashes--;
|
|
661
669
|
return memchr(pe, '\\', stringEnd - pe);
|
|
662
670
|
}
|
|
663
671
|
|
|
@@ -707,50 +715,41 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
707
715
|
case 'f':
|
|
708
716
|
APPEND_CHAR('\f');
|
|
709
717
|
break;
|
|
710
|
-
case 'u':
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (
|
|
729
|
-
raise_parse_error_at("
|
|
730
|
-
}
|
|
731
|
-
if (pe[0] == '\\' && pe[1] == 'u') {
|
|
732
|
-
uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2);
|
|
733
|
-
|
|
734
|
-
if ((sur & 0xFC00) != 0xDC00) {
|
|
735
|
-
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
|
739
|
-
| (sur & 0x3FF));
|
|
740
|
-
pe += 5;
|
|
741
|
-
} else {
|
|
742
|
-
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
743
|
-
break;
|
|
718
|
+
case 'u': {
|
|
719
|
+
uint32_t ch = unescape_unicode(state, ++pe, stringEnd);
|
|
720
|
+
pe += 3;
|
|
721
|
+
/* To handle values above U+FFFF, we take a sequence of
|
|
722
|
+
* \uXXXX escapes in the U+D800..U+DBFF then
|
|
723
|
+
* U+DC00..U+DFFF ranges, take the low 10 bits from each
|
|
724
|
+
* to make a 20-bit number, then add 0x10000 to get the
|
|
725
|
+
* final codepoint.
|
|
726
|
+
*
|
|
727
|
+
* See Unicode 15: 3.8 "Surrogates", 5.3 "Handling
|
|
728
|
+
* Surrogate Pairs in UTF-16", and 23.6 "Surrogates
|
|
729
|
+
* Area".
|
|
730
|
+
*/
|
|
731
|
+
if ((ch & 0xFC00) == 0xD800) {
|
|
732
|
+
pe++;
|
|
733
|
+
if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) {
|
|
734
|
+
uint32_t sur = unescape_unicode(state, pe + 2, stringEnd);
|
|
735
|
+
|
|
736
|
+
if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) {
|
|
737
|
+
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
|
744
738
|
}
|
|
745
|
-
}
|
|
746
739
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
740
|
+
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF));
|
|
741
|
+
pe += 5;
|
|
742
|
+
} else {
|
|
743
|
+
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
752
746
|
}
|
|
747
|
+
|
|
748
|
+
int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
|
|
749
|
+
buffer += unescape_len;
|
|
750
|
+
p = ++pe;
|
|
753
751
|
break;
|
|
752
|
+
}
|
|
754
753
|
default:
|
|
755
754
|
if ((unsigned char)*pe < 0x20) {
|
|
756
755
|
if (!config->allow_control_characters) {
|
|
@@ -759,6 +758,10 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
759
758
|
}
|
|
760
759
|
raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
|
|
761
760
|
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (config->allow_invalid_escape) {
|
|
764
|
+
APPEND_CHAR(*pe);
|
|
762
765
|
} else {
|
|
763
766
|
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
|
|
764
767
|
}
|
|
@@ -783,20 +786,39 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
783
786
|
}
|
|
784
787
|
|
|
785
788
|
#define MAX_FAST_INTEGER_SIZE 18
|
|
789
|
+
#define MAX_NUMBER_STACK_BUFFER 128
|
|
790
|
+
|
|
791
|
+
typedef VALUE (*json_number_decode_func_t)(const char *ptr);
|
|
792
|
+
|
|
793
|
+
static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func)
|
|
794
|
+
{
|
|
795
|
+
if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) {
|
|
796
|
+
char buffer[MAX_NUMBER_STACK_BUFFER];
|
|
797
|
+
MEMCPY(buffer, start, char, len);
|
|
798
|
+
buffer[len] = '\0';
|
|
799
|
+
return func(buffer);
|
|
800
|
+
} else {
|
|
801
|
+
VALUE buffer_v = rb_str_tmp_new(len);
|
|
802
|
+
char *buffer = RSTRING_PTR(buffer_v);
|
|
803
|
+
MEMCPY(buffer, start, char, len);
|
|
804
|
+
buffer[len] = '\0';
|
|
805
|
+
VALUE number = func(buffer);
|
|
806
|
+
RB_GC_GUARD(buffer_v);
|
|
807
|
+
return number;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
786
810
|
|
|
787
|
-
static VALUE
|
|
811
|
+
static VALUE json_decode_inum(const char *buffer)
|
|
788
812
|
{
|
|
789
|
-
|
|
790
|
-
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
|
|
791
|
-
MEMCPY(buffer, start, char, len);
|
|
792
|
-
buffer[len] = '\0';
|
|
793
|
-
VALUE number = rb_cstr2inum(buffer, 10);
|
|
794
|
-
RB_ALLOCV_END(buffer_v);
|
|
795
|
-
return number;
|
|
813
|
+
return rb_cstr2inum(buffer, 10);
|
|
796
814
|
}
|
|
797
815
|
|
|
798
|
-
static
|
|
799
|
-
|
|
816
|
+
NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
|
|
817
|
+
{
|
|
818
|
+
return json_decode_large_number(start, len, json_decode_inum);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
|
|
800
822
|
{
|
|
801
823
|
if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) {
|
|
802
824
|
if (negative) {
|
|
@@ -808,22 +830,14 @@ json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const
|
|
|
808
830
|
return json_decode_large_integer(start, end - start);
|
|
809
831
|
}
|
|
810
832
|
|
|
811
|
-
static VALUE
|
|
833
|
+
static VALUE json_decode_dnum(const char *buffer)
|
|
812
834
|
{
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
MEMCPY(buffer, start, char, len);
|
|
816
|
-
buffer[len] = '\0';
|
|
817
|
-
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
818
|
-
}
|
|
835
|
+
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
836
|
+
}
|
|
819
837
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
buffer[len] = '\0';
|
|
824
|
-
VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
825
|
-
RB_ALLOCV_END(buffer_v);
|
|
826
|
-
return number;
|
|
838
|
+
NOINLINE(static) VALUE json_decode_large_float(const char *start, long len)
|
|
839
|
+
{
|
|
840
|
+
return json_decode_large_number(start, len, json_decode_dnum);
|
|
827
841
|
}
|
|
828
842
|
|
|
829
843
|
/* Ruby JSON optimized float decoder using vendored Ryu algorithm
|
|
@@ -875,7 +889,7 @@ static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
|
|
|
875
889
|
return Qfalse;
|
|
876
890
|
}
|
|
877
891
|
|
|
878
|
-
static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
|
892
|
+
NOINLINE(static) void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
|
879
893
|
{
|
|
880
894
|
VALUE message = rb_sprintf(
|
|
881
895
|
"detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
|
|
@@ -886,16 +900,18 @@ static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_
|
|
|
886
900
|
RB_GC_GUARD(message);
|
|
887
901
|
}
|
|
888
902
|
|
|
889
|
-
|
|
890
|
-
RBIMPL_ATTR_NORETURN()
|
|
891
|
-
#endif
|
|
892
|
-
static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
|
903
|
+
NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
|
893
904
|
{
|
|
894
905
|
VALUE message = rb_sprintf(
|
|
895
906
|
"duplicate key %"PRIsVALUE,
|
|
896
907
|
rb_inspect(duplicate_key)
|
|
897
908
|
);
|
|
898
909
|
|
|
910
|
+
long line, column;
|
|
911
|
+
cursor_position(state, &line, &column);
|
|
912
|
+
rb_str_concat(message, build_parse_error_message("", state, line, column)) ;
|
|
913
|
+
rb_exc_raise(parse_error_new(message, line, column));
|
|
914
|
+
|
|
899
915
|
raise_parse_error(RSTRING_PTR(message), state);
|
|
900
916
|
RB_GC_GUARD(message);
|
|
901
917
|
}
|
|
@@ -992,7 +1008,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
|
|
|
992
1008
|
JSON_UnescapePositions positions = {
|
|
993
1009
|
.size = 0,
|
|
994
1010
|
.positions = backslashes,
|
|
995
|
-
.
|
|
1011
|
+
.additional_backslashes = 0,
|
|
996
1012
|
};
|
|
997
1013
|
|
|
998
1014
|
do {
|
|
@@ -1007,7 +1023,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
|
|
|
1007
1023
|
backslashes[positions.size] = state->cursor;
|
|
1008
1024
|
positions.size++;
|
|
1009
1025
|
} else {
|
|
1010
|
-
positions.
|
|
1026
|
+
positions.additional_backslashes++;
|
|
1011
1027
|
}
|
|
1012
1028
|
state->cursor++;
|
|
1013
1029
|
break;
|
|
@@ -1440,6 +1456,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1440
1456
|
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
1441
1457
|
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
|
1442
1458
|
else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
|
|
1459
|
+
else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
|
|
1443
1460
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1444
1461
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1445
1462
|
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
|
@@ -1658,6 +1675,7 @@ void Init_parser(void)
|
|
|
1658
1675
|
sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
|
|
1659
1676
|
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
|
|
1660
1677
|
sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
|
|
1678
|
+
sym_allow_invalid_escape = ID2SYM(rb_intern("allow_invalid_escape"));
|
|
1661
1679
|
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
|
1662
1680
|
sym_freeze = ID2SYM(rb_intern("freeze"));
|
|
1663
1681
|
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>
|
|
@@ -106,16 +133,6 @@ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *en
|
|
|
106
133
|
return 0;
|
|
107
134
|
}
|
|
108
135
|
|
|
109
|
-
static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
|
110
|
-
{
|
|
111
|
-
uint8x16x4_t tab;
|
|
112
|
-
tab.val[0] = vld1q_u8(table);
|
|
113
|
-
tab.val[1] = vld1q_u8(table+16);
|
|
114
|
-
tab.val[2] = vld1q_u8(table+32);
|
|
115
|
-
tab.val[3] = vld1q_u8(table+48);
|
|
116
|
-
return tab;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
136
|
#endif /* ARM Neon Support.*/
|
|
120
137
|
|
|
121
138
|
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
data/lib/json/common.rb
CHANGED
|
@@ -156,15 +156,17 @@ module JSON
|
|
|
156
156
|
def generator=(generator) # :nodoc:
|
|
157
157
|
old, $VERBOSE = $VERBOSE, nil
|
|
158
158
|
@generator = generator
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
if generator.const_defined?(:GeneratorMethods)
|
|
160
|
+
generator_methods = generator::GeneratorMethods
|
|
161
|
+
for const in generator_methods.constants
|
|
162
|
+
klass = const_get(const)
|
|
163
|
+
modul = generator_methods.const_get(const)
|
|
164
|
+
klass.class_eval do
|
|
165
|
+
instance_methods(false).each do |m|
|
|
166
|
+
m.to_s == 'to_json' and remove_method m
|
|
167
|
+
end
|
|
168
|
+
include modul
|
|
166
169
|
end
|
|
167
|
-
include modul
|
|
168
170
|
end
|
|
169
171
|
end
|
|
170
172
|
self.state = generator::State
|
|
@@ -878,7 +880,7 @@ module JSON
|
|
|
878
880
|
end
|
|
879
881
|
end
|
|
880
882
|
|
|
881
|
-
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
883
|
+
if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
|
|
882
884
|
source = 'null'
|
|
883
885
|
end
|
|
884
886
|
|
|
@@ -1036,7 +1038,8 @@ module JSON
|
|
|
1036
1038
|
# JSON.new(options = nil, &block)
|
|
1037
1039
|
#
|
|
1038
1040
|
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
|
|
1039
|
-
# See {Parsing Options}[
|
|
1041
|
+
# See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
|
|
1042
|
+
# and {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
1040
1043
|
#
|
|
1041
1044
|
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
|
1042
1045
|
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
|
@@ -1095,6 +1098,30 @@ module JSON
|
|
|
1095
1098
|
load(File.read(path, encoding: Encoding::UTF_8))
|
|
1096
1099
|
end
|
|
1097
1100
|
end
|
|
1101
|
+
|
|
1102
|
+
module GeneratorMethods
|
|
1103
|
+
# call-seq: to_json(*)
|
|
1104
|
+
#
|
|
1105
|
+
# Converts this object into a JSON string.
|
|
1106
|
+
# If this object doesn't directly maps to a JSON native type,
|
|
1107
|
+
# first convert it to a string (calling #to_s), then converts
|
|
1108
|
+
# it to a JSON string, and returns the result.
|
|
1109
|
+
# This is a fallback, if no special method #to_json was defined for some object.
|
|
1110
|
+
def to_json(state = nil, *)
|
|
1111
|
+
obj = case self
|
|
1112
|
+
when nil, false, true, Integer, Float, Array, Hash
|
|
1113
|
+
self
|
|
1114
|
+
else
|
|
1115
|
+
"#{self}"
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
if state.nil?
|
|
1119
|
+
JSON::State._generate_no_fallback(obj, nil, nil)
|
|
1120
|
+
else
|
|
1121
|
+
JSON::State.from_state(state)._generate_no_fallback(obj)
|
|
1122
|
+
end
|
|
1123
|
+
end
|
|
1124
|
+
end
|
|
1098
1125
|
end
|
|
1099
1126
|
|
|
1100
1127
|
module ::Kernel
|
|
@@ -1140,3 +1167,7 @@ module ::Kernel
|
|
|
1140
1167
|
JSON[object, opts]
|
|
1141
1168
|
end
|
|
1142
1169
|
end
|
|
1170
|
+
|
|
1171
|
+
class Object
|
|
1172
|
+
include JSON::GeneratorMethods
|
|
1173
|
+
end
|
|
@@ -9,7 +9,7 @@ module JSON
|
|
|
9
9
|
# Instantiates a new State object, configured by _opts_.
|
|
10
10
|
#
|
|
11
11
|
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
|
12
|
-
# See {Generating Options}[
|
|
12
|
+
# See {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
13
13
|
def initialize(opts = nil)
|
|
14
14
|
if opts && !opts.empty?
|
|
15
15
|
configure(opts)
|
|
@@ -211,7 +211,14 @@ module JSON
|
|
|
211
211
|
|
|
212
212
|
# This integer returns the current depth data structure nesting in the
|
|
213
213
|
# generated JSON.
|
|
214
|
-
|
|
214
|
+
attr_reader :depth
|
|
215
|
+
|
|
216
|
+
def depth=(depth)
|
|
217
|
+
if depth.negative?
|
|
218
|
+
raise ArgumentError, "depth must be >= 0 (got #{depth})"
|
|
219
|
+
end
|
|
220
|
+
@depth = depth
|
|
221
|
+
end
|
|
215
222
|
|
|
216
223
|
def check_max_nesting # :nodoc:
|
|
217
224
|
return if @max_nesting.zero?
|
|
@@ -260,6 +267,11 @@ module JSON
|
|
|
260
267
|
else
|
|
261
268
|
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
262
269
|
end
|
|
270
|
+
|
|
271
|
+
if opts[:depth]&.negative?
|
|
272
|
+
raise ArgumentError, "depth must be >= 0 (got #{opts[:depth]})"
|
|
273
|
+
end
|
|
274
|
+
|
|
263
275
|
opts.each do |key, value|
|
|
264
276
|
instance_variable_set "@#{key}", value
|
|
265
277
|
end
|
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,30 @@ 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
|
+
#
|
|
197
|
+
# ---
|
|
198
|
+
#
|
|
199
|
+
# Option +allow_invalid_escape+ (boolean) specifies whether to ignore backslahes that are followed
|
|
200
|
+
# by an invalid escape character in strings;
|
|
201
|
+
# defaults to +false+.
|
|
202
|
+
#
|
|
203
|
+
# With the default, +false+:
|
|
204
|
+
# JSON.parse('"Hell\o"') # invalid escape character in string (JSON::ParserError)
|
|
205
|
+
#
|
|
206
|
+
# When enabled:
|
|
207
|
+
# JSON.parse('"Hell\o"', allow_invalid_escape: true) # => "Hello"
|
|
208
|
+
#
|
|
176
209
|
# ====== Output Options
|
|
177
210
|
#
|
|
178
211
|
# 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.19.3
|
|
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.0.3
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: JSON Implementation for Ruby
|
|
90
90
|
test_files: []
|