json_pure 1.8.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +5 -3
  4. data/CHANGES +5 -5
  5. data/Gemfile +3 -1
  6. data/README.md +131 -84
  7. data/Rakefile +17 -10
  8. data/VERSION +1 -1
  9. data/ext/json/ext/generator/generator.c +1 -52
  10. data/ext/json/ext/generator/generator.h +0 -5
  11. data/ext/json/ext/parser/extconf.rb +3 -0
  12. data/ext/json/ext/parser/parser.c +304 -458
  13. data/ext/json/ext/parser/parser.h +0 -1
  14. data/ext/json/ext/parser/parser.rl +35 -152
  15. data/ext/json/extconf.rb +0 -1
  16. data/java/src/json/ext/Generator.java +2 -5
  17. data/java/src/json/ext/GeneratorState.java +2 -54
  18. data/java/src/json/ext/OptionsReader.java +1 -1
  19. data/java/src/json/ext/Parser.java +109 -409
  20. data/java/src/json/ext/Parser.rl +24 -117
  21. data/java/src/json/ext/RuntimeInfo.java +0 -4
  22. data/json.gemspec +0 -0
  23. data/json_pure.gemspec +7 -7
  24. data/lib/json.rb +1 -0
  25. data/lib/json/add/bigdecimal.rb +1 -0
  26. data/lib/json/add/complex.rb +2 -1
  27. data/lib/json/add/core.rb +1 -0
  28. data/lib/json/add/date.rb +1 -1
  29. data/lib/json/add/date_time.rb +1 -1
  30. data/lib/json/add/exception.rb +1 -1
  31. data/lib/json/add/ostruct.rb +1 -1
  32. data/lib/json/add/range.rb +1 -1
  33. data/lib/json/add/rational.rb +1 -0
  34. data/lib/json/add/regexp.rb +1 -1
  35. data/lib/json/add/struct.rb +1 -1
  36. data/lib/json/add/symbol.rb +1 -1
  37. data/lib/json/add/time.rb +1 -1
  38. data/lib/json/common.rb +24 -52
  39. data/lib/json/ext.rb +0 -6
  40. data/lib/json/generic_object.rb +5 -4
  41. data/lib/json/pure.rb +2 -8
  42. data/lib/json/pure/generator.rb +51 -123
  43. data/lib/json/pure/parser.rb +28 -80
  44. data/lib/json/version.rb +2 -1
  45. data/references/rfc7159.txt +899 -0
  46. data/tests/fixtures/obsolete_fail1.json +1 -0
  47. data/tests/{test_json_addition.rb → json_addition_test.rb} +22 -25
  48. data/tests/json_common_interface_test.rb +126 -0
  49. data/tests/json_encoding_test.rb +105 -0
  50. data/tests/json_ext_parser_test.rb +15 -0
  51. data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +5 -8
  52. data/tests/{test_json_generate.rb → json_generator_test.rb} +65 -37
  53. data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
  54. data/tests/json_parser_test.rb +448 -0
  55. data/tests/json_string_matching_test.rb +38 -0
  56. data/tests/test_helper.rb +23 -0
  57. data/tools/fuzz.rb +1 -9
  58. metadata +18 -31
  59. data/TODO +0 -1
  60. data/tests/fixtures/fail1.json +0 -1
  61. data/tests/setup_variant.rb +0 -11
  62. data/tests/test_json.rb +0 -519
  63. data/tests/test_json_encoding.rb +0 -65
  64. data/tests/test_json_string_matching.rb +0 -39
  65. data/tests/test_json_unicode.rb +0 -72
@@ -1,9 +1,3 @@
1
- if ENV['SIMPLECOV_COVERAGE'].to_i == 1
2
- require 'simplecov'
3
- SimpleCov.start do
4
- add_filter "/tests/"
5
- end
6
- end
7
1
  require 'json/common'
8
2
 
9
3
  module JSON
@@ -1,3 +1,4 @@
1
+ #frozen_string_literal: false
1
2
  require 'ostruct'
2
3
 
3
4
  module JSON
@@ -48,12 +49,12 @@ module JSON
48
49
  end
49
50
 
50
51
  def [](name)
51
- table[name.to_sym]
52
- end
52
+ __send__(name)
53
+ end unless method_defined?(:[])
53
54
 
54
55
  def []=(name, value)
55
- __send__ "#{name}=", value
56
- end
56
+ __send__("#{name}=", value)
57
+ end unless method_defined?(:[]=)
57
58
 
58
59
  def |(other)
59
60
  self.class[other.to_hash.merge(to_hash)]
@@ -1,17 +1,11 @@
1
- if ENV['SIMPLECOV_COVERAGE'].to_i == 1
2
- require 'simplecov'
3
- SimpleCov.start do
4
- add_filter "/tests/"
5
- end
6
- end
7
1
  require 'json/common'
8
- require 'json/pure/parser'
9
- require 'json/pure/generator'
10
2
 
11
3
  module JSON
12
4
  # This module holds all the modules/classes that implement JSON's
13
5
  # functionality in pure ruby.
14
6
  module Pure
7
+ require 'json/pure/parser'
8
+ require 'json/pure/generator'
15
9
  $DEBUG and warn "Using Pure library for JSON."
16
10
  JSON.parser = Parser
17
11
  JSON.generator = Generator
@@ -1,3 +1,4 @@
1
+ #frozen_string_literal: false
1
2
  module JSON
2
3
  MAP = {
3
4
  "\x0" => '\u0000',
@@ -38,85 +39,45 @@ module JSON
38
39
 
39
40
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
40
41
  # UTF16 big endian characters as \u????, and return it.
41
- if defined?(::Encoding)
42
- def utf8_to_json(string) # :nodoc:
43
- string = string.dup
44
- string.force_encoding(::Encoding::ASCII_8BIT)
45
- string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
46
- string.force_encoding(::Encoding::UTF_8)
47
- string
48
- end
49
-
50
- def utf8_to_json_ascii(string) # :nodoc:
51
- string = string.dup
52
- string.force_encoding(::Encoding::ASCII_8BIT)
53
- string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
54
- string.gsub!(/(
55
- (?:
56
- [\xc2-\xdf][\x80-\xbf] |
57
- [\xe0-\xef][\x80-\xbf]{2} |
58
- [\xf0-\xf4][\x80-\xbf]{3}
59
- )+ |
60
- [\x80-\xc1\xf5-\xff] # invalid
61
- )/nx) { |c|
62
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
63
- s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
64
- s.force_encoding(::Encoding::ASCII_8BIT)
65
- s.gsub!(/.{4}/n, '\\\\u\&')
66
- s.force_encoding(::Encoding::UTF_8)
67
- }
68
- string.force_encoding(::Encoding::UTF_8)
69
- string
70
- rescue => e
71
- raise GeneratorError.wrap(e)
72
- end
73
-
74
- def valid_utf8?(string)
75
- encoding = string.encoding
76
- (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
77
- string.valid_encoding?
78
- end
79
- module_function :valid_utf8?
80
- else
81
- def utf8_to_json(string) # :nodoc:
82
- string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] }
83
- end
42
+ def utf8_to_json(string) # :nodoc:
43
+ string = string.dup
44
+ string.force_encoding(::Encoding::ASCII_8BIT)
45
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
46
+ string.force_encoding(::Encoding::UTF_8)
47
+ string
48
+ end
84
49
 
85
- def utf8_to_json_ascii(string) # :nodoc:
86
- string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
87
- string.gsub!(/(
88
- (?:
89
- [\xc2-\xdf][\x80-\xbf] |
90
- [\xe0-\xef][\x80-\xbf]{2} |
91
- [\xf0-\xf4][\x80-\xbf]{3}
92
- )+ |
93
- [\x80-\xc1\xf5-\xff] # invalid
94
- )/nx) { |c|
95
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
96
- s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
97
- s.gsub!(/.{4}/n, '\\\\u\&')
98
- }
99
- string
100
- rescue => e
101
- raise GeneratorError.wrap(e)
102
- end
50
+ def utf8_to_json_ascii(string) # :nodoc:
51
+ string = string.dup
52
+ string.force_encoding(::Encoding::ASCII_8BIT)
53
+ string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
54
+ string.gsub!(/(
55
+ (?:
56
+ [\xc2-\xdf][\x80-\xbf] |
57
+ [\xe0-\xef][\x80-\xbf]{2} |
58
+ [\xf0-\xf4][\x80-\xbf]{3}
59
+ )+ |
60
+ [\x80-\xc1\xf5-\xff] # invalid
61
+ )/nx) { |c|
62
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
63
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
64
+ s.force_encoding(::Encoding::ASCII_8BIT)
65
+ s.gsub!(/.{4}/n, '\\\\u\&')
66
+ s.force_encoding(::Encoding::UTF_8)
67
+ }
68
+ string.force_encoding(::Encoding::UTF_8)
69
+ string
70
+ rescue => e
71
+ raise GeneratorError.wrap(e)
72
+ end
103
73
 
104
- def valid_utf8?(string)
105
- string =~
106
- /\A( [\x09\x0a\x0d\x20-\x7e] # ASCII
107
- | [\xc2-\xdf][\x80-\xbf] # non-overlong 2-byte
108
- | \xe0[\xa0-\xbf][\x80-\xbf] # excluding overlongs
109
- | [\xe1-\xec\xee\xef][\x80-\xbf]{2} # straight 3-byte
110
- | \xed[\x80-\x9f][\x80-\xbf] # excluding surrogates
111
- | \xf0[\x90-\xbf][\x80-\xbf]{2} # planes 1-3
112
- | [\xf1-\xf3][\x80-\xbf]{3} # planes 4-15
113
- | \xf4[\x80-\x8f][\x80-\xbf]{2} # plane 16
114
- )*\z/nx
115
- end
74
+ def valid_utf8?(string)
75
+ encoding = string.encoding
76
+ (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
77
+ string.valid_encoding?
116
78
  end
117
79
  module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
118
80
 
119
-
120
81
  module Pure
121
82
  module Generator
122
83
  # This class is used to create State instances, that are use to hold data
@@ -154,8 +115,6 @@ module JSON
154
115
  # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
155
116
  # generated, otherwise an exception is thrown, if these values are
156
117
  # encountered. This options defaults to false.
157
- # * *quirks_mode*: Enables quirks_mode for parser, that is for example
158
- # generating single JSON values instead of documents is possible.
159
118
  def initialize(opts = {})
160
119
  @indent = ''
161
120
  @space = ''
@@ -164,7 +123,6 @@ module JSON
164
123
  @array_nl = ''
165
124
  @allow_nan = false
166
125
  @ascii_only = false
167
- @quirks_mode = false
168
126
  @buffer_initial_length = 1024
169
127
  configure opts
170
128
  end
@@ -190,10 +148,6 @@ module JSON
190
148
  # the generated JSON, max_nesting = 0 if no maximum is checked.
191
149
  attr_accessor :max_nesting
192
150
 
193
- # If this attribute is set to true, quirks mode is enabled, otherwise
194
- # it's disabled.
195
- attr_accessor :quirks_mode
196
-
197
151
  # :stopdoc:
198
152
  attr_reader :buffer_initial_length
199
153
 
@@ -233,11 +187,6 @@ module JSON
233
187
  @ascii_only
234
188
  end
235
189
 
236
- # Returns true, if quirks mode is enabled. Otherwise returns false.
237
- def quirks_mode?
238
- @quirks_mode
239
- end
240
-
241
190
  # Configure this State instance with the Hash _opts_, and return
242
191
  # itself.
243
192
  def configure(opts)
@@ -259,7 +208,6 @@ module JSON
259
208
  @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
260
209
  @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
261
210
  @depth = opts[:depth] || 0
262
- @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
263
211
  @buffer_initial_length ||= opts[:buffer_initial_length]
264
212
 
265
213
  if !opts.key?(:max_nesting) # defaults to 100
@@ -286,20 +234,14 @@ module JSON
286
234
 
287
235
  alias to_hash to_h
288
236
 
289
- # Generates a valid JSON document from object +obj+ and returns the
290
- # result. If no valid JSON document can be created this method raises a
237
+ # Generates a valid JSON document from object +obj+ and
238
+ # returns the result. If no valid JSON document can be
239
+ # created this method raises a
291
240
  # GeneratorError exception.
292
241
  def generate(obj)
293
242
  result = obj.to_json(self)
294
243
  JSON.valid_utf8?(result) or raise GeneratorError,
295
244
  "source sequence #{result.inspect} is illegal/malformed utf-8"
296
- unless @quirks_mode
297
- unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
298
- result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
299
- then
300
- raise GeneratorError, "only generation of JSON objects or arrays allowed"
301
- end
302
- end
303
245
  result
304
246
  end
305
247
 
@@ -445,34 +387,20 @@ module JSON
445
387
  end
446
388
 
447
389
  module String
448
- if defined?(::Encoding)
449
- # This string should be encoded with UTF-8 A call to this method
450
- # returns a JSON string encoded with UTF16 big endian characters as
451
- # \u????.
452
- def to_json(state = nil, *args)
453
- state = State.from_state(state)
454
- if encoding == ::Encoding::UTF_8
455
- string = self
456
- else
457
- string = encode(::Encoding::UTF_8)
458
- end
459
- if state.ascii_only?
460
- '"' << JSON.utf8_to_json_ascii(string) << '"'
461
- else
462
- '"' << JSON.utf8_to_json(string) << '"'
463
- end
390
+ # This string should be encoded with UTF-8 A call to this method
391
+ # returns a JSON string encoded with UTF16 big endian characters as
392
+ # \u????.
393
+ def to_json(state = nil, *args)
394
+ state = State.from_state(state)
395
+ if encoding == ::Encoding::UTF_8
396
+ string = self
397
+ else
398
+ string = encode(::Encoding::UTF_8)
464
399
  end
465
- else
466
- # This string should be encoded with UTF-8 A call to this method
467
- # returns a JSON string encoded with UTF16 big endian characters as
468
- # \u????.
469
- def to_json(state = nil, *args)
470
- state = State.from_state(state)
471
- if state.ascii_only?
472
- '"' << JSON.utf8_to_json_ascii(self) << '"'
473
- else
474
- '"' << JSON.utf8_to_json(self) << '"'
475
- end
400
+ if state.ascii_only?
401
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
402
+ else
403
+ '"' << JSON.utf8_to_json(string) << '"'
476
404
  end
477
405
  end
478
406
 
@@ -1,3 +1,4 @@
1
+ #frozen_string_literal: false
1
2
  require 'strscan'
2
3
 
3
4
  module JSON
@@ -48,7 +49,7 @@ module JSON
48
49
  )+
49
50
  )mx
50
51
 
51
- UNPARSED = Object.new
52
+ UNPARSED = Object.new.freeze
52
53
 
53
54
  # Creates a new JSON::Pure::Parser instance for the string _source_.
54
55
  #
@@ -58,23 +59,20 @@ module JSON
58
59
  # structures. Disable depth checking with :max_nesting => false|nil|0,
59
60
  # it defaults to 100.
60
61
  # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
61
- # defiance of RFC 4627 to be parsed by the Parser. This option defaults
62
+ # defiance of RFC 7159 to be parsed by the Parser. This option defaults
62
63
  # to false.
63
64
  # * *symbolize_names*: If set to true, returns symbols for the names
64
- # (keys) in a JSON object. Otherwise strings are returned, which is also
65
- # the default.
65
+ # (keys) in a JSON object. Otherwise strings are returned, which is
66
+ # also the default. It's not possible to use this option in
67
+ # conjunction with the *create_additions* option.
66
68
  # * *create_additions*: If set to true, the Parser creates
67
69
  # additions when if a matching class and create_id was found. This
68
70
  # option defaults to false.
69
71
  # * *object_class*: Defaults to Hash
70
72
  # * *array_class*: Defaults to Array
71
- # * *quirks_mode*: Enables quirks_mode for parser, that is for example
72
- # parsing single JSON values instead of documents is possible.
73
73
  def initialize(source, opts = {})
74
74
  opts ||= {}
75
- unless @quirks_mode = opts[:quirks_mode]
76
- source = convert_encoding source
77
- end
75
+ source = convert_encoding source
78
76
  super source
79
77
  if !opts.key?(:max_nesting) # defaults to 100
80
78
  @max_nesting = 100
@@ -90,6 +88,9 @@ module JSON
90
88
  else
91
89
  @create_additions = false
92
90
  end
91
+ @symbolize_names && @create_additions and raise ArgumentError,
92
+ 'options :symbolize_names and :create_additions cannot be used '\
93
+ 'in conjunction'
93
94
  @create_id = @create_additions ? JSON.create_id : nil
94
95
  @object_class = opts[:object_class] || Hash
95
96
  @array_class = opts[:array_class] || Array
@@ -98,48 +99,26 @@ module JSON
98
99
 
99
100
  alias source string
100
101
 
101
- def quirks_mode?
102
- !!@quirks_mode
103
- end
104
-
105
102
  def reset
106
103
  super
107
104
  @current_nesting = 0
108
105
  end
109
106
 
110
- # Parses the current JSON string _source_ and returns the complete data
111
- # structure as a result.
107
+ # Parses the current JSON string _source_ and returns the
108
+ # complete data structure as a result.
112
109
  def parse
113
110
  reset
114
111
  obj = nil
115
- if @quirks_mode
116
- while !eos? && skip(IGNORE)
117
- end
118
- if eos?
119
- raise ParserError, "source did not contain any JSON!"
120
- else
121
- obj = parse_value
122
- obj == UNPARSED and raise ParserError, "source did not contain any JSON!"
123
- end
112
+ while !eos? && skip(IGNORE) do end
113
+ if eos?
114
+ raise ParserError, "source is not valid JSON!"
124
115
  else
125
- until eos?
126
- case
127
- when scan(OBJECT_OPEN)
128
- obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
129
- @current_nesting = 1
130
- obj = parse_object
131
- when scan(ARRAY_OPEN)
132
- obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
133
- @current_nesting = 1
134
- obj = parse_array
135
- when skip(IGNORE)
136
- ;
137
- else
138
- raise ParserError, "source '#{peek(20)}' not in JSON!"
139
- end
140
- end
141
- obj or raise ParserError, "source did not contain any JSON!"
116
+ obj = parse_value
117
+ UNPARSED.equal?(obj) and raise ParserError,
118
+ "source is not valid JSON!"
142
119
  end
120
+ while !eos? && skip(IGNORE) do end
121
+ eos? or raise ParserError, "source is not valid JSON!"
143
122
  obj
144
123
  end
145
124
 
@@ -149,43 +128,12 @@ module JSON
149
128
  if source.respond_to?(:to_str)
150
129
  source = source.to_str
151
130
  else
152
- raise TypeError, "#{source.inspect} is not like a string"
131
+ raise TypeError,
132
+ "#{source.inspect} is not like a string"
153
133
  end
154
- if defined?(::Encoding)
155
- if source.encoding == ::Encoding::ASCII_8BIT
156
- b = source[0, 4].bytes.to_a
157
- source =
158
- case
159
- when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
160
- source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
161
- when b.size >= 4 && b[0] == 0 && b[2] == 0
162
- source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
163
- when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
164
- source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
165
- when b.size >= 4 && b[1] == 0 && b[3] == 0
166
- source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
167
- else
168
- source.dup
169
- end
170
- else
171
- source = source.encode(::Encoding::UTF_8)
172
- end
134
+ if source.encoding != ::Encoding::ASCII_8BIT
135
+ source = source.encode(::Encoding::UTF_8)
173
136
  source.force_encoding(::Encoding::ASCII_8BIT)
174
- else
175
- b = source
176
- source =
177
- case
178
- when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
179
- JSON.iconv('utf-8', 'utf-32be', b)
180
- when b.size >= 4 && b[0] == 0 && b[2] == 0
181
- JSON.iconv('utf-8', 'utf-16be', b)
182
- when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
183
- JSON.iconv('utf-8', 'utf-32le', b)
184
- when b.size >= 4 && b[1] == 0 && b[3] == 0
185
- JSON.iconv('utf-8', 'utf-16le', b)
186
- else
187
- b
188
- end
189
137
  end
190
138
  source
191
139
  end
@@ -254,7 +202,7 @@ module JSON
254
202
  false
255
203
  when scan(NULL)
256
204
  nil
257
- when (string = parse_string) != UNPARSED
205
+ when !UNPARSED.equal?(string = parse_string)
258
206
  string
259
207
  when scan(ARRAY_OPEN)
260
208
  @current_nesting += 1
@@ -284,7 +232,7 @@ module JSON
284
232
  delim = false
285
233
  until eos?
286
234
  case
287
- when (value = parse_value) != UNPARSED
235
+ when !UNPARSED.equal?(value = parse_value)
288
236
  delim = false
289
237
  result << value
290
238
  skip(IGNORE)
@@ -316,13 +264,13 @@ module JSON
316
264
  delim = false
317
265
  until eos?
318
266
  case
319
- when (string = parse_string) != UNPARSED
267
+ when !UNPARSED.equal?(string = parse_string)
320
268
  skip(IGNORE)
321
269
  unless scan(PAIR_DELIMITER)
322
270
  raise ParserError, "expected ':' in object at '#{peek(20)}'!"
323
271
  end
324
272
  skip(IGNORE)
325
- unless (value = parse_value).equal? UNPARSED
273
+ unless UNPARSED.equal?(value = parse_value)
326
274
  result[@symbolize_names ? string.to_sym : string] = value
327
275
  delim = false
328
276
  skip(IGNORE)