json 2.13.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51746c89475207b2f61cd248e90092dfbc5ede1c57226e3809f972ba83155b03
4
- data.tar.gz: df39f6262a9eacd358a263b5ab00e0c30474115c9ef367132498f385eeb8fc03
3
+ metadata.gz: 71499860706a6f27871853ec88fe26d5bd6a53f85fba2e9764b9ef0aadc170d9
4
+ data.tar.gz: c07cd26190c4f36864490465890c162e1b9ce0ae1f262069e51c94d7cf74b117
5
5
  SHA512:
6
- metadata.gz: f192e8bd1e2008570805de002e2ee1ff75f5f31929f5f3c719e7dc11a2cb754f97d70655308372275e4eb9c39d5ee16cb85f2fe7823d5a94f71879d87b11d705
7
- data.tar.gz: 69846485c78975cd38c91e5068f365bc8ee9b41ec761d964a8c2166e3e6e86e72d34634f26764a6532ec1e8a3537d669069774440742c2dec7003de09090041c
6
+ metadata.gz: f1c626d30c67e99c56d9f411b7944b6263261676567c02f6d57ba7566087743b26835d3c6c4c1636a0dc76cc2b8c4e2739a1130f286cd069406c38c66138df97
7
+ data.tar.gz: eb4fd62079fc730962359e4128f7abd36ca04c17b580506c3e8e3ebdd0e8e0c54c3cf75d1ae899dc53e13697bfa0c11c6c04277d701cfc83fa9ee611785ac85e
data/CHANGES.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ### Unreleased
4
4
 
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
+
5
9
  ### 2025-07-24 (2.13.1)
6
10
 
7
11
  * Fix support for older compilers without `__builtin_cpu_supports`.
@@ -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
- rb_warn("%s at line %ld column %ld", message, line, column);
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
- rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), object);
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
- emit_parse_warning("detected duplicate keys in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`", state);
889
+ emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
845
890
  break;
846
891
  case JSON_RAISE:
847
- raise_parse_error("duplicate key", state);
892
+ raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
848
893
  break;
849
894
  }
850
895
  }
@@ -7,45 +7,45 @@ typedef enum {
7
7
  #ifdef JSON_ENABLE_SIMD
8
8
 
9
9
  #ifdef __clang__
10
- #if __has_builtin(__builtin_ctzll)
11
- #define HAVE_BUILTIN_CTZLL 1
12
- #else
13
- #define HAVE_BUILTIN_CTZLL 0
14
- #endif
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
- #define HAVE_BUILTIN_CTZLL 1
16
+ # define HAVE_BUILTIN_CTZLL 1
17
17
  #else
18
- #define HAVE_BUILTIN_CTZLL 0
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
- return __builtin_ctzll(input);
24
+ return __builtin_ctzll(input);
25
25
  #else
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;
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
- #if HAVE_BUILTIN_CTZLL
38
+ #if HAVE_BUILTIN_CTZLL
39
39
  return __builtin_ctz(input);
40
- #else
40
+ #else
41
41
  int trailing_zeros = 0;
42
42
  int temp = input;
43
43
  while ((temp & 1) == 0 && temp > 0) {
44
- trailing_zeros++;
45
- temp >>= 1;
44
+ trailing_zeros++;
45
+ temp >>= 1;
46
46
  }
47
47
  return trailing_zeros;
48
- #endif
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
- uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
82
+ uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
83
83
 
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));
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
- 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);
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
- 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);
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
- uint8x16x4_t tab;
108
- tab.val[0] = vld1q_u8(table);
109
- tab.val[1] = vld1q_u8(table+16);
110
- tab.val[2] = vld1q_u8(table+32);
111
- tab.val[3] = vld1q_u8(table+48);
112
- return tab;
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
- int chunk_mask = compute_chunk_mask_sse2(*ptr);
154
- if (chunk_mask) {
155
- *mask = chunk_mask;
156
- return 1;
157
- }
158
- *ptr += sizeof(__m128i);
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: exctract :create_additions support to another gem for version 3.0
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
- message = "JSON.load implicit support for `create_additions: true` is deprecated " \
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
- uplevel = 4
97
- caller_locations(uplevel, 10).each do |frame|
98
- if frame.path.nil? || frame.path.start_with?(GEM_ROOT) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
99
- uplevel += 1
100
- else
101
- break
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
- warn(message, uplevel: uplevel - 1)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSON
4
- VERSION = '2.13.1'
4
+ VERSION = '2.13.2'
5
5
  end
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.1
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-24 00:00:00.000000000 Z
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