json 2.13.0 → 2.13.2
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 +9 -1
- data/ext/json/ext/generator/generator.c +34 -11
- data/ext/json/ext/parser/parser.c +49 -4
- data/ext/json/ext/simd/conf.rb +15 -11
- data/ext/json/ext/simd/simd.h +47 -46
- data/lib/json/common.rb +18 -17
- data/lib/json/version.rb +1 -1
- 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: 71499860706a6f27871853ec88fe26d5bd6a53f85fba2e9764b9ef0aadc170d9
|
4
|
+
data.tar.gz: c07cd26190c4f36864490465890c162e1b9ce0ae1f262069e51c94d7cf74b117
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1c626d30c67e99c56d9f411b7944b6263261676567c02f6d57ba7566087743b26835d3c6c4c1636a0dc76cc2b8c4e2739a1130f286cd069406c38c66138df97
|
7
|
+
data.tar.gz: eb4fd62079fc730962359e4128f7abd36ca04c17b580506c3e8e3ebdd0e8e0c54c3cf75d1ae899dc53e13697bfa0c11c6c04277d701cfc83fa9ee611785ac85e
|
data/CHANGES.md
CHANGED
@@ -2,7 +2,15 @@
|
|
2
2
|
|
3
3
|
### Unreleased
|
4
4
|
|
5
|
-
### 2025-
|
5
|
+
### 2025-07-28 (2.13.2)
|
6
|
+
|
7
|
+
* Improve duplicate key warning and errors to include the key name and point to the right caller.
|
8
|
+
|
9
|
+
### 2025-07-24 (2.13.1)
|
10
|
+
|
11
|
+
* Fix support for older compilers without `__builtin_cpu_supports`.
|
12
|
+
|
13
|
+
### 2025-07-17 (2.13.0)
|
6
14
|
|
7
15
|
* Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered.
|
8
16
|
In `json 3.0` an error will be raised.
|
@@ -1907,15 +1907,30 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l
|
|
1907
1907
|
return Qnil;
|
1908
1908
|
}
|
1909
1909
|
|
1910
|
+
struct configure_state_data {
|
1911
|
+
JSON_Generator_State *state;
|
1912
|
+
VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated
|
1913
|
+
};
|
1914
|
+
|
1915
|
+
static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value)
|
1916
|
+
{
|
1917
|
+
if (RTEST(data->vstate)) {
|
1918
|
+
RB_OBJ_WRITE(data->vstate, field, value);
|
1919
|
+
} else {
|
1920
|
+
*field = value;
|
1921
|
+
}
|
1922
|
+
}
|
1923
|
+
|
1910
1924
|
static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
1911
1925
|
{
|
1912
|
-
|
1926
|
+
struct configure_state_data *data = (struct configure_state_data *)_arg;
|
1927
|
+
JSON_Generator_State *state = data->state;
|
1913
1928
|
|
1914
|
-
if (key == sym_indent) { state->indent
|
1915
|
-
else if (key == sym_space) { state->space
|
1916
|
-
else if (key == sym_space_before) { state->space_before
|
1917
|
-
else if (key == sym_object_nl) { state->object_nl
|
1918
|
-
else if (key == sym_array_nl) { state->array_nl
|
1929
|
+
if (key == sym_indent) { state_write_value(data, &state->indent, string_config(val)); }
|
1930
|
+
else if (key == sym_space) { state_write_value(data, &state->space, string_config(val)); }
|
1931
|
+
else if (key == sym_space_before) { state_write_value(data, &state->space_before, string_config(val)); }
|
1932
|
+
else if (key == sym_object_nl) { state_write_value(data, &state->object_nl, string_config(val)); }
|
1933
|
+
else if (key == sym_array_nl) { state_write_value(data, &state->array_nl, string_config(val)); }
|
1919
1934
|
else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
|
1920
1935
|
else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
|
1921
1936
|
else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
|
@@ -1924,11 +1939,14 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
1924
1939
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
1925
1940
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
1926
1941
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
1927
|
-
else if (key == sym_as_json) {
|
1942
|
+
else if (key == sym_as_json) {
|
1943
|
+
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
|
1944
|
+
state_write_value(data, &state->as_json, proc);
|
1945
|
+
}
|
1928
1946
|
return ST_CONTINUE;
|
1929
1947
|
}
|
1930
1948
|
|
1931
|
-
static void configure_state(JSON_Generator_State *state, VALUE config)
|
1949
|
+
static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE config)
|
1932
1950
|
{
|
1933
1951
|
if (!RTEST(config)) return;
|
1934
1952
|
|
@@ -1936,15 +1954,20 @@ static void configure_state(JSON_Generator_State *state, VALUE config)
|
|
1936
1954
|
|
1937
1955
|
if (!RHASH_SIZE(config)) return;
|
1938
1956
|
|
1957
|
+
struct configure_state_data data = {
|
1958
|
+
.state = state,
|
1959
|
+
.vstate = vstate
|
1960
|
+
};
|
1961
|
+
|
1939
1962
|
// We assume in most cases few keys are set so it's faster to go over
|
1940
1963
|
// the provided keys than to check all possible keys.
|
1941
|
-
rb_hash_foreach(config, configure_state_i, (VALUE)
|
1964
|
+
rb_hash_foreach(config, configure_state_i, (VALUE)&data);
|
1942
1965
|
}
|
1943
1966
|
|
1944
1967
|
static VALUE cState_configure(VALUE self, VALUE opts)
|
1945
1968
|
{
|
1946
1969
|
GET_STATE(self);
|
1947
|
-
configure_state(state, opts);
|
1970
|
+
configure_state(state, self, opts);
|
1948
1971
|
return self;
|
1949
1972
|
}
|
1950
1973
|
|
@@ -1952,7 +1975,7 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1952
1975
|
{
|
1953
1976
|
JSON_Generator_State state = {0};
|
1954
1977
|
state_init(&state);
|
1955
|
-
configure_state(&state, opts);
|
1978
|
+
configure_state(&state, Qfalse, opts);
|
1956
1979
|
|
1957
1980
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
1958
1981
|
FBuffer buffer = {
|
@@ -422,10 +422,12 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
|
|
422
422
|
long line, column;
|
423
423
|
cursor_position(state, &line, &column);
|
424
424
|
|
425
|
-
|
425
|
+
VALUE warning = rb_sprintf("%s at line %ld column %ld", message, line, column);
|
426
|
+
rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning);
|
426
427
|
}
|
427
428
|
|
428
429
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
430
|
+
|
429
431
|
#ifdef RBIMPL_ATTR_NORETURN
|
430
432
|
RBIMPL_ATTR_NORETURN()
|
431
433
|
#endif
|
@@ -830,21 +832,64 @@ static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig
|
|
830
832
|
return array;
|
831
833
|
}
|
832
834
|
|
835
|
+
static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
|
836
|
+
{
|
837
|
+
VALUE set = rb_hash_new_capa(count / 2);
|
838
|
+
for (size_t index = 0; index < count; index += 2) {
|
839
|
+
size_t before = RHASH_SIZE(set);
|
840
|
+
VALUE key = pairs[index];
|
841
|
+
rb_hash_aset(set, key, Qtrue);
|
842
|
+
if (RHASH_SIZE(set) == before) {
|
843
|
+
if (RB_SYMBOL_P(key)) {
|
844
|
+
return rb_sym2str(key);
|
845
|
+
}
|
846
|
+
return key;
|
847
|
+
}
|
848
|
+
}
|
849
|
+
return Qfalse;
|
850
|
+
}
|
851
|
+
|
852
|
+
static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
853
|
+
{
|
854
|
+
VALUE message = rb_sprintf(
|
855
|
+
"detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
|
856
|
+
rb_inspect(duplicate_key)
|
857
|
+
);
|
858
|
+
|
859
|
+
emit_parse_warning(RSTRING_PTR(message), state);
|
860
|
+
RB_GC_GUARD(message);
|
861
|
+
}
|
862
|
+
|
863
|
+
#ifdef RBIMPL_ATTR_NORETURN
|
864
|
+
RBIMPL_ATTR_NORETURN()
|
865
|
+
#endif
|
866
|
+
static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
867
|
+
{
|
868
|
+
VALUE message = rb_sprintf(
|
869
|
+
"duplicate key %"PRIsVALUE,
|
870
|
+
rb_inspect(duplicate_key)
|
871
|
+
);
|
872
|
+
|
873
|
+
raise_parse_error(RSTRING_PTR(message), state);
|
874
|
+
RB_GC_GUARD(message);
|
875
|
+
}
|
876
|
+
|
833
877
|
static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
|
834
878
|
{
|
835
879
|
size_t entries_count = count / 2;
|
836
880
|
VALUE object = rb_hash_new_capa(entries_count);
|
837
|
-
|
881
|
+
const VALUE *pairs = rvalue_stack_peek(state->stack, count);
|
882
|
+
rb_hash_bulk_insert(count, pairs, object);
|
838
883
|
|
839
884
|
if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) {
|
840
885
|
switch (config->on_duplicate_key) {
|
841
886
|
case JSON_IGNORE:
|
842
887
|
break;
|
843
888
|
case JSON_DEPRECATED:
|
844
|
-
|
889
|
+
emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
|
845
890
|
break;
|
846
891
|
case JSON_RAISE:
|
847
|
-
|
892
|
+
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
|
848
893
|
break;
|
849
894
|
}
|
850
895
|
}
|
data/ext/json/ext/simd/conf.rb
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
case RbConfig::CONFIG['host_cpu']
|
2
2
|
when /^(arm|aarch64)/
|
3
3
|
# Try to compile a small program using NEON instructions
|
4
|
-
header, type, init = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)'
|
4
|
+
header, type, init, extra = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)', nil
|
5
5
|
when /^(x86_64|x64)/
|
6
|
-
header, type, init = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)'
|
6
|
+
header, type, init, extra = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)', 'if (__builtin_cpu_supports("sse2")) { printf("OK"); }'
|
7
7
|
end
|
8
8
|
if header
|
9
|
-
have_header(header) && try_compile(<<~SRC)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
if have_header(header) && try_compile(<<~SRC, '-Werror=implicit-function-declaration')
|
10
|
+
#{cpp_include(header)}
|
11
|
+
int main(int argc, char **argv) {
|
12
|
+
#{type} test = #{init};
|
13
|
+
#{extra}
|
14
|
+
if (argc > 100000) printf("%p", &test);
|
15
|
+
return 0;
|
16
|
+
}
|
17
|
+
SRC
|
18
|
+
$defs.push("-DJSON_ENABLE_SIMD")
|
19
|
+
else
|
20
|
+
puts "Disable SIMD"
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
have_header('cpuid.h')
|
data/ext/json/ext/simd/simd.h
CHANGED
@@ -7,45 +7,45 @@ typedef enum {
|
|
7
7
|
#ifdef JSON_ENABLE_SIMD
|
8
8
|
|
9
9
|
#ifdef __clang__
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
# if __has_builtin(__builtin_ctzll)
|
11
|
+
# define HAVE_BUILTIN_CTZLL 1
|
12
|
+
# else
|
13
|
+
# define HAVE_BUILTIN_CTZLL 0
|
14
|
+
# endif
|
15
15
|
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
|
16
|
-
|
16
|
+
# define HAVE_BUILTIN_CTZLL 1
|
17
17
|
#else
|
18
|
-
|
18
|
+
# define HAVE_BUILTIN_CTZLL 0
|
19
19
|
#endif
|
20
20
|
|
21
21
|
static inline uint32_t trailing_zeros64(uint64_t input)
|
22
22
|
{
|
23
23
|
#if HAVE_BUILTIN_CTZLL
|
24
|
-
|
24
|
+
return __builtin_ctzll(input);
|
25
25
|
#else
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
uint32_t trailing_zeros = 0;
|
27
|
+
uint64_t temp = input;
|
28
|
+
while ((temp & 1) == 0 && temp > 0) {
|
29
|
+
trailing_zeros++;
|
30
|
+
temp >>= 1;
|
31
|
+
}
|
32
|
+
return trailing_zeros;
|
33
33
|
#endif
|
34
34
|
}
|
35
35
|
|
36
36
|
static inline int trailing_zeros(int input)
|
37
37
|
{
|
38
|
-
|
38
|
+
#if HAVE_BUILTIN_CTZLL
|
39
39
|
return __builtin_ctz(input);
|
40
|
-
|
40
|
+
#else
|
41
41
|
int trailing_zeros = 0;
|
42
42
|
int temp = input;
|
43
43
|
while ((temp & 1) == 0 && temp > 0) {
|
44
|
-
|
45
|
-
|
44
|
+
trailing_zeros++;
|
45
|
+
temp >>= 1;
|
46
46
|
}
|
47
47
|
return trailing_zeros;
|
48
|
-
|
48
|
+
#endif
|
49
49
|
}
|
50
50
|
|
51
51
|
#if (defined(__GNUC__ ) || defined(__clang__))
|
@@ -79,37 +79,38 @@ static inline FORCE_INLINE uint64_t neon_match_mask(uint8x16_t matches)
|
|
79
79
|
|
80
80
|
static inline FORCE_INLINE uint64_t compute_chunk_mask_neon(const char *ptr)
|
81
81
|
{
|
82
|
-
|
82
|
+
uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
// Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
|
85
|
+
// https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/
|
86
|
+
const uint8x16_t too_low_or_dbl_quote = vcltq_u8(veorq_u8(chunk, vdupq_n_u8(2)), vdupq_n_u8(33));
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
uint8x16_t has_backslash = vceqq_u8(chunk, vdupq_n_u8('\\'));
|
89
|
+
uint8x16_t needs_escape = vorrq_u8(too_low_or_dbl_quote, has_backslash);
|
90
|
+
return neon_match_mask(needs_escape);
|
91
91
|
}
|
92
92
|
|
93
93
|
static inline FORCE_INLINE int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
|
94
94
|
{
|
95
95
|
while (*ptr + sizeof(uint8x16_t) <= end) {
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
uint64_t chunk_mask = compute_chunk_mask_neon(*ptr);
|
97
|
+
if (chunk_mask) {
|
98
|
+
*mask = chunk_mask;
|
99
|
+
return 1;
|
100
|
+
}
|
101
|
+
*ptr += sizeof(uint8x16_t);
|
102
102
|
}
|
103
103
|
return 0;
|
104
104
|
}
|
105
105
|
|
106
|
-
uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
106
|
+
static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
107
|
+
{
|
108
|
+
uint8x16x4_t tab;
|
109
|
+
tab.val[0] = vld1q_u8(table);
|
110
|
+
tab.val[1] = vld1q_u8(table+16);
|
111
|
+
tab.val[2] = vld1q_u8(table+32);
|
112
|
+
tab.val[3] = vld1q_u8(table+48);
|
113
|
+
return tab;
|
113
114
|
}
|
114
115
|
|
115
116
|
#endif /* ARM Neon Support.*/
|
@@ -150,12 +151,12 @@ static inline TARGET_SSE2 FORCE_INLINE int compute_chunk_mask_sse2(const char *p
|
|
150
151
|
static inline TARGET_SSE2 FORCE_INLINE int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
|
151
152
|
{
|
152
153
|
while (*ptr + sizeof(__m128i) <= end) {
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
154
|
+
int chunk_mask = compute_chunk_mask_sse2(*ptr);
|
155
|
+
if (chunk_mask) {
|
156
|
+
*mask = chunk_mask;
|
157
|
+
return 1;
|
158
|
+
}
|
159
|
+
*ptr += sizeof(__m128i);
|
159
160
|
}
|
160
161
|
|
161
162
|
return 0;
|
data/lib/json/common.rb
CHANGED
@@ -48,7 +48,7 @@ module JSON
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
# TODO:
|
51
|
+
# TODO: extract :create_additions support to another gem for version 3.0
|
52
52
|
def create_additions_proc(opts)
|
53
53
|
if opts[:symbolize_names]
|
54
54
|
raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction"
|
@@ -87,31 +87,32 @@ module JSON
|
|
87
87
|
opts
|
88
88
|
end
|
89
89
|
|
90
|
-
GEM_ROOT = File.expand_path("../../../", __FILE__) + "/"
|
91
90
|
def create_additions_warning
|
92
|
-
|
91
|
+
JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \
|
93
92
|
"and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
|
94
93
|
"pass `create_additions: true`"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
if RUBY_VERSION >= "3.0"
|
106
|
-
warn(message, uplevel: uplevel - 1, category: :deprecated)
|
98
|
+
class << self
|
99
|
+
def deprecation_warning(message, uplevel = 3) # :nodoc:
|
100
|
+
gem_root = File.expand_path("../../../", __FILE__) + "/"
|
101
|
+
caller_locations(uplevel, 10).each do |frame|
|
102
|
+
if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
|
103
|
+
uplevel += 1
|
107
104
|
else
|
108
|
-
|
105
|
+
break
|
109
106
|
end
|
110
107
|
end
|
108
|
+
|
109
|
+
if RUBY_VERSION >= "3.0"
|
110
|
+
warn(message, uplevel: uplevel, category: :deprecated)
|
111
|
+
else
|
112
|
+
warn(message, uplevel: uplevel)
|
113
|
+
end
|
111
114
|
end
|
112
|
-
end
|
113
115
|
|
114
|
-
class << self
|
115
116
|
# :call-seq:
|
116
117
|
# JSON[object] -> new_array or new_string
|
117
118
|
#
|
data/lib/json/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.13.
|
4
|
+
version: 2.13.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Florian Frank
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-07-
|
10
|
+
date: 2025-07-28 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: This is a JSON implementation as a Ruby extension in C.
|
13
13
|
email: flori@ping.de
|