json 2.16.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.
@@ -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 6
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>
@@ -73,14 +100,14 @@ static inline SIMD_Implementation find_simd_implementation(void)
73
100
  #define HAVE_SIMD_NEON 1
74
101
 
75
102
  // See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
76
- static ALWAYS_INLINE() uint64_t neon_match_mask(uint8x16_t matches)
103
+ ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches)
77
104
  {
78
105
  const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4);
79
106
  const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
80
107
  return mask & 0x8888888888888888ull;
81
108
  }
82
109
 
83
- static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr)
110
+ ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr)
84
111
  {
85
112
  uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
86
113
 
@@ -93,7 +120,7 @@ static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr)
93
120
  return neon_match_mask(needs_escape);
94
121
  }
95
122
 
96
- static ALWAYS_INLINE() int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
123
+ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
97
124
  {
98
125
  while (*ptr + sizeof(uint8x16_t) <= end) {
99
126
  uint64_t chunk_mask = compute_chunk_mask_neon(*ptr);
@@ -106,16 +133,6 @@ static ALWAYS_INLINE() int string_scan_simd_neon(const char **ptr, const char *e
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)
@@ -140,7 +157,7 @@ static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
140
157
  #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1))
141
158
  #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a)
142
159
 
143
- static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr)
160
+ ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr)
144
161
  {
145
162
  __m128i chunk = _mm_loadu_si128((__m128i const*)ptr);
146
163
  // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
@@ -151,7 +168,7 @@ static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr)
151
168
  return _mm_movemask_epi8(needs_escape);
152
169
  }
153
170
 
154
- static TARGET_SSE2 ALWAYS_INLINE() int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
171
+ ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
155
172
  {
156
173
  while (*ptr + sizeof(__m128i) <= end) {
157
174
  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
- #ifdef JSON_DEBUG
32
+ #if JSON_DEBUG
33
33
  #include <assert.h>
34
34
  #endif
35
35
 
@@ -449,7 +449,7 @@ static int filter_special(double fp, char* dest)
449
449
  * }
450
450
  *
451
451
  */
452
- static int fpconv_dtoa(double d, char dest[28])
452
+ static int fpconv_dtoa(double d, char dest[32])
453
453
  {
454
454
  char digits[18];
455
455
 
@@ -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
- #ifdef JSON_DEBUG
475
+ #if JSON_DEBUG
476
476
  assert(str_len <= 32);
477
477
  #endif
478
478
 
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
- generator_methods = generator::GeneratorMethods
160
- for const in generator_methods.constants
161
- klass = const_get(const)
162
- modul = generator_methods.const_get(const)
163
- klass.class_eval do
164
- instance_methods(false).each do |m|
165
- m.to_s == 'to_json' and remove_method m
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
@@ -550,6 +552,7 @@ module JSON
550
552
  :create_additions => nil,
551
553
  }
552
554
  # :call-seq:
555
+ # JSON.unsafe_load(source, options = {}) -> object
553
556
  # JSON.unsafe_load(source, proc = nil, options = {}) -> object
554
557
  #
555
558
  # Returns the Ruby objects created by parsing the given +source+.
@@ -681,7 +684,12 @@ module JSON
681
684
  #
682
685
  def unsafe_load(source, proc = nil, options = nil)
683
686
  opts = if options.nil?
684
- _unsafe_load_default_options
687
+ if proc && proc.is_a?(Hash)
688
+ options, proc = proc, nil
689
+ options
690
+ else
691
+ _unsafe_load_default_options
692
+ end
685
693
  else
686
694
  _unsafe_load_default_options.merge(options)
687
695
  end
@@ -709,6 +717,7 @@ module JSON
709
717
  end
710
718
 
711
719
  # :call-seq:
720
+ # JSON.load(source, options = {}) -> object
712
721
  # JSON.load(source, proc = nil, options = {}) -> object
713
722
  #
714
723
  # Returns the Ruby objects created by parsing the given +source+.
@@ -845,8 +854,18 @@ module JSON
845
854
  # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
846
855
  #
847
856
  def load(source, proc = nil, options = nil)
857
+ if proc && options.nil? && proc.is_a?(Hash)
858
+ options = proc
859
+ proc = nil
860
+ end
861
+
848
862
  opts = if options.nil?
849
- _load_default_options
863
+ if proc && proc.is_a?(Hash)
864
+ options, proc = proc, nil
865
+ options
866
+ else
867
+ _load_default_options
868
+ end
850
869
  else
851
870
  _load_default_options.merge(options)
852
871
  end
@@ -861,7 +880,7 @@ module JSON
861
880
  end
862
881
  end
863
882
 
864
- if opts[:allow_blank] && (source.nil? || source.empty?)
883
+ if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
865
884
  source = 'null'
866
885
  end
867
886
 
@@ -1019,7 +1038,8 @@ module JSON
1019
1038
  # JSON.new(options = nil, &block)
1020
1039
  #
1021
1040
  # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
1022
- # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
1041
+ # See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
1042
+ # and {Generating Options}[rdoc-ref:JSON@Generating+Options].
1023
1043
  #
1024
1044
  # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
1025
1045
  # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
@@ -1048,7 +1068,7 @@ module JSON
1048
1068
  options[:as_json] = as_json if as_json
1049
1069
 
1050
1070
  @state = State.new(options).freeze
1051
- @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options))
1071
+ @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
1052
1072
  end
1053
1073
 
1054
1074
  # call-seq:
@@ -1057,7 +1077,7 @@ module JSON
1057
1077
  #
1058
1078
  # Serialize the given object into a \JSON document.
1059
1079
  def dump(object, io = nil)
1060
- @state.generate_new(object, io)
1080
+ @state.generate(object, io)
1061
1081
  end
1062
1082
  alias_method :generate, :dump
1063
1083
 
@@ -1078,6 +1098,30 @@ module JSON
1078
1098
  load(File.read(path, encoding: Encoding::UTF_8))
1079
1099
  end
1080
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
1081
1125
  end
1082
1126
 
1083
1127
  module ::Kernel
@@ -1123,3 +1167,7 @@ module ::Kernel
1123
1167
  JSON[object, opts]
1124
1168
  end
1125
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}[#module-JSON-label-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
- attr_accessor :depth
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
@@ -312,8 +324,8 @@ module JSON
312
324
  def to_h
313
325
  result = {}
314
326
  instance_variables.each do |iv|
315
- iv = iv.to_s[1..-1]
316
- result[iv.to_sym] = self[iv]
327
+ key = iv.to_s[1..-1]
328
+ result[key.to_sym] = instance_variable_get(iv)
317
329
  end
318
330
 
319
331
  if result[:allow_duplicate_key].nil?
@@ -330,6 +342,9 @@ module JSON
330
342
  # created this method raises a
331
343
  # GeneratorError exception.
332
344
  def generate(obj, anIO = nil)
345
+ return dup.generate(obj, anIO) if frozen?
346
+
347
+ depth = @depth
333
348
  if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
334
349
  !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
335
350
  result = generate_json(obj, ''.dup)
@@ -346,14 +361,8 @@ module JSON
346
361
  else
347
362
  result
348
363
  end
349
- end
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
364
+ ensure
365
+ @depth = depth unless frozen?
357
366
  end
358
367
 
359
368
  # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
@@ -494,17 +503,15 @@ module JSON
494
503
  # _depth_ is used to find out nesting depth, to indent accordingly.
495
504
  def to_json(state = nil, *)
496
505
  state = State.from_state(state)
506
+ depth = state.depth
497
507
  state.check_max_nesting
498
508
  json_transform(state)
509
+ ensure
510
+ state.depth = depth
499
511
  end
500
512
 
501
513
  private
502
514
 
503
- def json_shift(state)
504
- state.object_nl.empty? or return ''
505
- state.indent * state.depth
506
- end
507
-
508
515
  def json_transform(state)
509
516
  depth = state.depth += 1
510
517
 
@@ -559,17 +566,19 @@ module JSON
559
566
  raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
560
567
  end
561
568
  result << value.to_json(state)
569
+ state.depth = depth
562
570
  else
563
571
  raise GeneratorError.new("#{value.class} not allowed in JSON", value)
564
572
  end
565
573
  elsif value.respond_to?(:to_json)
566
574
  result << value.to_json(state)
575
+ state.depth = depth
567
576
  else
568
577
  result << %{"#{String(value)}"}
569
578
  end
570
579
  first = false
571
580
  }
572
- depth = state.depth -= 1
581
+ depth -= 1
573
582
  unless first
574
583
  result << state.object_nl
575
584
  result << state.indent * depth if indent
@@ -586,8 +595,11 @@ module JSON
586
595
  # produced JSON string output further.
587
596
  def to_json(state = nil, *)
588
597
  state = State.from_state(state)
598
+ depth = state.depth
589
599
  state.check_max_nesting
590
600
  json_transform(state)
601
+ ensure
602
+ state.depth = depth
591
603
  end
592
604
 
593
605
  private
@@ -625,12 +637,13 @@ module JSON
625
637
  end
626
638
  elsif value.respond_to?(:to_json)
627
639
  result << value.to_json(state)
640
+ state.depth = depth
628
641
  else
629
642
  result << %{"#{String(value)}"}
630
643
  end
631
644
  first = false
632
645
  }
633
- depth = state.depth -= 1
646
+ depth -= 1
634
647
  result << state.array_nl
635
648
  result << state.indent * depth if indent
636
649
  result << ']'
@@ -655,6 +668,9 @@ module JSON
655
668
  if casted_value.equal?(self)
656
669
  raise GeneratorError.new("#{self} not allowed in JSON", self)
657
670
  end
671
+ unless Generator.native_type?(casted_value)
672
+ raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
673
+ end
658
674
 
659
675
  state.check_max_nesting
660
676
  state.depth += 1
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.16.0'
4
+ VERSION = '2.19.3'
5
5
  end
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.16.0
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: 3.6.9
87
+ rubygems_version: 4.0.3
88
88
  specification_version: 4
89
89
  summary: JSON Implementation for Ruby
90
90
  test_files: []