json 1.0.0 → 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +503 -0
  3. data/LICENSE +56 -0
  4. data/README.md +416 -0
  5. data/ext/json/ext/fbuffer/fbuffer.h +187 -0
  6. data/ext/json/ext/generator/depend +1 -0
  7. data/ext/json/ext/generator/extconf.rb +2 -7
  8. data/ext/json/ext/generator/generator.c +1312 -338
  9. data/ext/json/ext/generator/generator.h +177 -0
  10. data/ext/json/ext/parser/depend +1 -0
  11. data/ext/json/ext/parser/extconf.rb +28 -5
  12. data/ext/json/ext/parser/parser.c +1349 -689
  13. data/ext/json/ext/parser/parser.h +96 -0
  14. data/ext/json/ext/parser/parser.rl +644 -188
  15. data/ext/json/extconf.rb +3 -0
  16. data/json.gemspec +68 -0
  17. data/lib/json/add/bigdecimal.rb +58 -0
  18. data/lib/json/add/complex.rb +51 -0
  19. data/lib/json/add/core.rb +12 -0
  20. data/lib/json/add/date.rb +54 -0
  21. data/lib/json/add/date_time.rb +67 -0
  22. data/lib/json/add/exception.rb +49 -0
  23. data/lib/json/add/ostruct.rb +54 -0
  24. data/lib/json/add/range.rb +54 -0
  25. data/lib/json/add/rational.rb +49 -0
  26. data/lib/json/add/regexp.rb +48 -0
  27. data/lib/json/add/set.rb +48 -0
  28. data/lib/json/add/struct.rb +52 -0
  29. data/lib/json/add/symbol.rb +48 -0
  30. data/lib/json/add/time.rb +59 -0
  31. data/lib/json/common.rb +588 -74
  32. data/lib/json/ext.rb +3 -1
  33. data/lib/json/generic_object.rb +75 -0
  34. data/lib/json/pure/generator.rb +311 -119
  35. data/lib/json/pure/parser.rb +182 -55
  36. data/lib/json/pure.rb +5 -65
  37. data/lib/json/version.rb +2 -1
  38. data/lib/json.rb +583 -196
  39. metadata +78 -137
  40. data/CHANGES +0 -25
  41. data/GPL +0 -340
  42. data/README +0 -77
  43. data/Rakefile +0 -250
  44. data/TODO +0 -1
  45. data/VERSION +0 -1
  46. data/benchmarks/benchmark.txt +0 -133
  47. data/benchmarks/benchmark_generator.rb +0 -44
  48. data/benchmarks/benchmark_parser.rb +0 -22
  49. data/benchmarks/benchmark_rails.rb +0 -26
  50. data/bin/edit_json.rb +0 -11
  51. data/data/example.json +0 -1
  52. data/data/index.html +0 -37
  53. data/data/prototype.js +0 -2515
  54. data/ext/json/ext/generator/Makefile +0 -149
  55. data/ext/json/ext/generator/unicode.c +0 -184
  56. data/ext/json/ext/generator/unicode.h +0 -40
  57. data/ext/json/ext/parser/Makefile +0 -149
  58. data/ext/json/ext/parser/unicode.c +0 -156
  59. data/ext/json/ext/parser/unicode.h +0 -44
  60. data/install.rb +0 -26
  61. data/lib/json/Array.xpm +0 -21
  62. data/lib/json/FalseClass.xpm +0 -21
  63. data/lib/json/Hash.xpm +0 -21
  64. data/lib/json/Key.xpm +0 -73
  65. data/lib/json/NilClass.xpm +0 -21
  66. data/lib/json/Numeric.xpm +0 -28
  67. data/lib/json/String.xpm +0 -96
  68. data/lib/json/TrueClass.xpm +0 -21
  69. data/lib/json/editor.rb +0 -1207
  70. data/lib/json/json.xpm +0 -1499
  71. data/tests/fixtures/fail1.json +0 -1
  72. data/tests/fixtures/fail10.json +0 -1
  73. data/tests/fixtures/fail11.json +0 -1
  74. data/tests/fixtures/fail12.json +0 -1
  75. data/tests/fixtures/fail13.json +0 -1
  76. data/tests/fixtures/fail14.json +0 -1
  77. data/tests/fixtures/fail15.json +0 -1
  78. data/tests/fixtures/fail16.json +0 -1
  79. data/tests/fixtures/fail17.json +0 -1
  80. data/tests/fixtures/fail19.json +0 -1
  81. data/tests/fixtures/fail2.json +0 -1
  82. data/tests/fixtures/fail20.json +0 -1
  83. data/tests/fixtures/fail21.json +0 -1
  84. data/tests/fixtures/fail22.json +0 -1
  85. data/tests/fixtures/fail23.json +0 -1
  86. data/tests/fixtures/fail24.json +0 -1
  87. data/tests/fixtures/fail25.json +0 -1
  88. data/tests/fixtures/fail26.json +0 -1
  89. data/tests/fixtures/fail27.json +0 -2
  90. data/tests/fixtures/fail28.json +0 -2
  91. data/tests/fixtures/fail3.json +0 -1
  92. data/tests/fixtures/fail4.json +0 -1
  93. data/tests/fixtures/fail5.json +0 -1
  94. data/tests/fixtures/fail6.json +0 -1
  95. data/tests/fixtures/fail7.json +0 -1
  96. data/tests/fixtures/fail8.json +0 -1
  97. data/tests/fixtures/fail9.json +0 -1
  98. data/tests/fixtures/pass1.json +0 -56
  99. data/tests/fixtures/pass18.json +0 -1
  100. data/tests/fixtures/pass2.json +0 -1
  101. data/tests/fixtures/pass3.json +0 -6
  102. data/tests/runner.rb +0 -24
  103. data/tests/test_json.rb +0 -235
  104. data/tests/test_json_addition.rb +0 -94
  105. data/tests/test_json_fixtures.rb +0 -30
  106. data/tests/test_json_generate.rb +0 -81
  107. data/tests/test_json_unicode.rb +0 -55
  108. data/tools/fuzz.rb +0 -133
  109. data/tools/server.rb +0 -62
@@ -1,3 +1,4 @@
1
+ #frozen_string_literal: false
1
2
  require 'strscan'
2
3
 
3
4
  module JSON
@@ -6,9 +7,12 @@ module JSON
6
7
  # into a Ruby data structure.
7
8
  class Parser < StringScanner
8
9
  STRING = /" ((?:[^\x0-\x1f"\\] |
10
+ # escaped special characters:
9
11
  \\["\\\/bfnrt] |
10
- \\u[0-9a-fA-F]{4})*)
11
- "/x
12
+ \\u[0-9a-fA-F]{4} |
13
+ # match all but escaped special characters:
14
+ \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
15
+ "/nx
12
16
  INTEGER = /(-?0|-?[1-9]\d*)/
13
17
  FLOAT = /(-?
14
18
  (?:0|[1-9]\d*)
@@ -18,6 +22,9 @@ module JSON
18
22
  (?i:e[+-]?\d+)
19
23
  )
20
24
  )/x
25
+ NAN = /NaN/
26
+ INFINITY = /Infinity/
27
+ MINUS_INFINITY = /-Infinity/
21
28
  OBJECT_OPEN = /\{/
22
29
  OBJECT_CLOSE = /\}/
23
30
  ARRAY_OPEN = /\[/
@@ -35,83 +42,184 @@ module JSON
35
42
  [^*/]| # normal chars
36
43
  /[^*]| # slashes that do not start a nested comment
37
44
  \*[^/]| # asterisks that do not end this comment
38
- /(?=\*/) # single slash before this comment's end
45
+ /(?=\*/) # single slash before this comment's end
39
46
  )*
40
47
  \*/ # the End of this comment
41
- |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
48
+ |[ \t\r\n]+ # whitespaces: space, horizontal tab, lf, cr
42
49
  )+
43
50
  )mx
44
51
 
45
- UNPARSED = Object.new
52
+ UNPARSED = Object.new.freeze
46
53
 
47
54
  # Creates a new JSON::Pure::Parser instance for the string _source_.
48
- def initialize(source)
49
- super
50
- @create_id = JSON.create_id
55
+ #
56
+ # It will be configured by the _opts_ hash. _opts_ can have the following
57
+ # keys:
58
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
59
+ # structures. Disable depth checking with :max_nesting => false|nil|0,
60
+ # it defaults to 100.
61
+ # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
62
+ # defiance of RFC 7159 to be parsed by the Parser. This option defaults
63
+ # to false.
64
+ # * *freeze*: If set to true, all parsed objects will be frozen. Parsed
65
+ # string will be deduplicated if possible.
66
+ # * *symbolize_names*: If set to true, returns symbols for the names
67
+ # (keys) in a JSON object. Otherwise strings are returned, which is
68
+ # also the default. It's not possible to use this option in
69
+ # conjunction with the *create_additions* option.
70
+ # * *create_additions*: If set to true, the Parser creates
71
+ # additions when a matching class and create_id are found. This
72
+ # option defaults to false.
73
+ # * *object_class*: Defaults to Hash
74
+ # * *array_class*: Defaults to Array
75
+ # * *decimal_class*: Specifies which class to use instead of the default
76
+ # (Float) when parsing decimal numbers. This class must accept a single
77
+ # string argument in its constructor.
78
+ def initialize(source, opts = {})
79
+ opts ||= {}
80
+ source = convert_encoding source
81
+ super source
82
+ if !opts.key?(:max_nesting) # defaults to 100
83
+ @max_nesting = 100
84
+ elsif opts[:max_nesting]
85
+ @max_nesting = opts[:max_nesting]
86
+ else
87
+ @max_nesting = 0
88
+ end
89
+ @allow_nan = !!opts[:allow_nan]
90
+ @symbolize_names = !!opts[:symbolize_names]
91
+ @freeze = !!opts[:freeze]
92
+ if opts.key?(:create_additions)
93
+ @create_additions = !!opts[:create_additions]
94
+ else
95
+ @create_additions = false
96
+ end
97
+ @symbolize_names && @create_additions and raise ArgumentError,
98
+ 'options :symbolize_names and :create_additions cannot be used '\
99
+ 'in conjunction'
100
+ @create_id = @create_additions ? JSON.create_id : nil
101
+ @object_class = opts[:object_class] || Hash
102
+ @array_class = opts[:array_class] || Array
103
+ @decimal_class = opts[:decimal_class]
104
+ @match_string = opts[:match_string]
51
105
  end
52
106
 
53
107
  alias source string
54
108
 
55
- # Parses the current JSON string _source_ and returns the complete data
56
- # structure as a result.
109
+ def reset
110
+ super
111
+ @current_nesting = 0
112
+ end
113
+
114
+ # Parses the current JSON string _source_ and returns the
115
+ # complete data structure as a result.
57
116
  def parse
58
117
  reset
59
118
  obj = nil
60
- until eos?
61
- case
62
- when scan(OBJECT_OPEN)
63
- obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
64
- obj = parse_object
65
- when scan(ARRAY_OPEN)
66
- obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
67
- obj = parse_array
68
- when skip(IGNORE)
69
- ;
70
- else
71
- raise ParserError, "source '#{peek(20)}' not in JSON!"
72
- end
119
+ while !eos? && skip(IGNORE) do end
120
+ if eos?
121
+ raise ParserError, "source is not valid JSON!"
122
+ else
123
+ obj = parse_value
124
+ UNPARSED.equal?(obj) and raise ParserError,
125
+ "source is not valid JSON!"
126
+ obj.freeze if @freeze
73
127
  end
74
- obj or raise ParserError, "source did not contain any JSON!"
128
+ while !eos? && skip(IGNORE) do end
129
+ eos? or raise ParserError, "source is not valid JSON!"
75
130
  obj
76
131
  end
77
132
 
78
133
  private
79
134
 
135
+ def convert_encoding(source)
136
+ if source.respond_to?(:to_str)
137
+ source = source.to_str
138
+ else
139
+ raise TypeError,
140
+ "#{source.inspect} is not like a string"
141
+ end
142
+ if source.encoding != ::Encoding::ASCII_8BIT
143
+ source = source.encode(::Encoding::UTF_8)
144
+ source.force_encoding(::Encoding::ASCII_8BIT)
145
+ end
146
+ source
147
+ end
148
+
149
+ # Unescape characters in strings.
150
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
151
+ UNESCAPE_MAP.update({
152
+ ?" => '"',
153
+ ?\\ => '\\',
154
+ ?/ => '/',
155
+ ?b => "\b",
156
+ ?f => "\f",
157
+ ?n => "\n",
158
+ ?r => "\r",
159
+ ?t => "\t",
160
+ ?u => nil,
161
+ })
162
+
163
+ EMPTY_8BIT_STRING = ''
164
+ if ::String.method_defined?(:encode)
165
+ EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
166
+ end
167
+
168
+ STR_UMINUS = ''.respond_to?(:-@)
80
169
  def parse_string
81
170
  if scan(STRING)
82
171
  return '' if self[1].empty?
83
- self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+))) do
84
- case $~[0]
85
- when '\\"' then '"'
86
- when '\\\\' then '\\'
87
- when '\\/' then '/'
88
- when '\\b' then "\b"
89
- when '\\f' then "\f"
90
- when '\\n' then "\n"
91
- when '\\r' then "\r"
92
- when '\\t' then "\t"
93
- else
94
- bytes = ''
95
- c = $~[0]
172
+ string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
173
+ if u = UNESCAPE_MAP[$&[1]]
174
+ u
175
+ else # \uXXXX
176
+ bytes = EMPTY_8BIT_STRING.dup
96
177
  i = 0
97
- while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
178
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
98
179
  bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
99
180
  i += 1
100
181
  end
101
- JSON::UTF16toUTF8.iconv(bytes)
182
+ JSON.iconv('utf-8', 'utf-16be', bytes).force_encoding(::Encoding::ASCII_8BIT)
183
+ end
184
+ end
185
+ if string.respond_to?(:force_encoding)
186
+ string.force_encoding(::Encoding::UTF_8)
187
+ end
188
+
189
+ if @freeze
190
+ if STR_UMINUS
191
+ string = -string
192
+ else
193
+ string.freeze
102
194
  end
103
195
  end
196
+
197
+ if @create_additions and @match_string
198
+ for (regexp, klass) in @match_string
199
+ klass.json_creatable? or next
200
+ string =~ regexp and return klass.json_create(string)
201
+ end
202
+ end
203
+ string
104
204
  else
105
205
  UNPARSED
106
206
  end
107
- rescue Iconv::Failure => e
108
- raise GeneratorError, "Caught #{e.class}: #{e}"
207
+ rescue => e
208
+ raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}"
109
209
  end
110
210
 
111
211
  def parse_value
112
212
  case
113
213
  when scan(FLOAT)
114
- Float(self[1])
214
+ if @decimal_class then
215
+ if @decimal_class == BigDecimal then
216
+ BigDecimal(self[1])
217
+ else
218
+ @decimal_class.new(self[1]) || Float(self[1])
219
+ end
220
+ else
221
+ Float(self[1])
222
+ end
115
223
  when scan(INTEGER)
116
224
  Integer(self[1])
117
225
  when scan(TRUE)
@@ -120,23 +228,39 @@ module JSON
120
228
  false
121
229
  when scan(NULL)
122
230
  nil
123
- when (string = parse_string) != UNPARSED
231
+ when !UNPARSED.equal?(string = parse_string)
124
232
  string
125
233
  when scan(ARRAY_OPEN)
126
- parse_array
234
+ @current_nesting += 1
235
+ ary = parse_array
236
+ @current_nesting -= 1
237
+ ary
127
238
  when scan(OBJECT_OPEN)
128
- parse_object
239
+ @current_nesting += 1
240
+ obj = parse_object
241
+ @current_nesting -= 1
242
+ obj
243
+ when @allow_nan && scan(NAN)
244
+ NaN
245
+ when @allow_nan && scan(INFINITY)
246
+ Infinity
247
+ when @allow_nan && scan(MINUS_INFINITY)
248
+ MinusInfinity
129
249
  else
130
250
  UNPARSED
131
251
  end
132
252
  end
133
253
 
134
254
  def parse_array
135
- result = []
255
+ raise NestingError, "nesting of #@current_nesting is too deep" if
256
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
257
+ result = @array_class.new
136
258
  delim = false
137
- until eos?
259
+ loop do
138
260
  case
139
- when (value = parse_value) != UNPARSED
261
+ when eos?
262
+ raise ParserError, "unexpected end of string while parsing array"
263
+ when !UNPARSED.equal?(value = parse_value)
140
264
  delim = false
141
265
  result << value
142
266
  skip(IGNORE)
@@ -162,18 +286,22 @@ module JSON
162
286
  end
163
287
 
164
288
  def parse_object
165
- result = {}
289
+ raise NestingError, "nesting of #@current_nesting is too deep" if
290
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
291
+ result = @object_class.new
166
292
  delim = false
167
- until eos?
293
+ loop do
168
294
  case
169
- when (string = parse_string) != UNPARSED
295
+ when eos?
296
+ raise ParserError, "unexpected end of string while parsing object"
297
+ when !UNPARSED.equal?(string = parse_string)
170
298
  skip(IGNORE)
171
299
  unless scan(PAIR_DELIMITER)
172
300
  raise ParserError, "expected ':' in object at '#{peek(20)}'!"
173
301
  end
174
302
  skip(IGNORE)
175
- unless (value = parse_value).equal? UNPARSED
176
- result[string] = value
303
+ unless UNPARSED.equal?(value = parse_value)
304
+ result[@symbolize_names ? string.to_sym : string] = value
177
305
  delim = false
178
306
  skip(IGNORE)
179
307
  if scan(COLLECTION_DELIMITER)
@@ -190,11 +318,10 @@ module JSON
190
318
  if delim
191
319
  raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
192
320
  end
193
- if klassname = result[@create_id]
321
+ if @create_additions and klassname = result[@create_id]
194
322
  klass = JSON.deep_const_get klassname
195
323
  break unless klass and klass.json_creatable?
196
324
  result = klass.json_create(result)
197
- result
198
325
  end
199
326
  break
200
327
  when skip(IGNORE)
data/lib/json/pure.rb CHANGED
@@ -1,75 +1,15 @@
1
1
  require 'json/common'
2
- require 'json/pure/parser'
3
- require 'json/pure/generator'
4
2
 
5
3
  module JSON
6
- begin
7
- require 'iconv'
8
- # An iconv instance to convert from UTF8 to UTF16 Big Endian.
9
- UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be') # :nodoc:
10
- # An iconv instance to convert from UTF16 Big Endian to UTF8.
11
- UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8') # :nodoc:
12
- UTF8toUTF16.iconv('no bom')
13
- rescue Errno::EINVAL, Iconv::InvalidEncoding
14
- # Iconv doesn't support big endian utf-16. Let's try to hack this manually
15
- # into the converters.
16
- begin
17
- old_verbose, $VERBSOSE = $VERBOSE, nil
18
- # An iconv instance to convert from UTF8 to UTF16 Big Endian.
19
- UTF16toUTF8 = Iconv.new('utf-8', 'utf-16') # :nodoc:
20
- # An iconv instance to convert from UTF16 Big Endian to UTF8.
21
- UTF8toUTF16 = Iconv.new('utf-16', 'utf-8') # :nodoc:
22
- UTF8toUTF16.iconv('no bom')
23
- if UTF8toUTF16.iconv("\xe2\x82\xac") == "\xac\x20"
24
- swapper = Class.new do
25
- def initialize(iconv) # :nodoc:
26
- @iconv = iconv
27
- end
28
-
29
- def iconv(string) # :nodoc:
30
- result = @iconv.iconv(string)
31
- JSON.swap!(result)
32
- end
33
- end
34
- UTF8toUTF16 = swapper.new(UTF8toUTF16) # :nodoc:
35
- end
36
- if UTF16toUTF8.iconv("\xac\x20") == "\xe2\x82\xac"
37
- swapper = Class.new do
38
- def initialize(iconv) # :nodoc:
39
- @iconv = iconv
40
- end
41
-
42
- def iconv(string) # :nodoc:
43
- string = JSON.swap!(string.dup)
44
- @iconv.iconv(string)
45
- end
46
- end
47
- UTF16toUTF8 = swapper.new(UTF16toUTF8) # :nodoc:
48
- end
49
- rescue Errno::EINVAL, Iconv::InvalidEncoding
50
- raise MissingUnicodeSupport, "iconv doesn't seem to support UTF-8/UTF-16 conversions"
51
- ensure
52
- $VERBOSE = old_verbose
53
- end
54
- rescue LoadError
55
- raise MissingUnicodeSupport,
56
- "iconv couldn't be loaded, which is required for UTF-8/UTF-16 conversions"
57
- end
58
-
59
- # Swap consecutive bytes of _string_ in place.
60
- def self.swap!(string) # :nodoc:
61
- 0.upto(string.size / 2) do |i|
62
- break unless string[2 * i + 1]
63
- string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
64
- end
65
- string
66
- end
67
-
68
4
  # This module holds all the modules/classes that implement JSON's
69
5
  # functionality in pure ruby.
70
6
  module Pure
71
- $DEBUG and warn "Using pure library for JSON."
7
+ require 'json/pure/parser'
8
+ require 'json/pure/generator'
9
+ $DEBUG and warn "Using Pure library for JSON."
72
10
  JSON.parser = Parser
73
11
  JSON.generator = Generator
74
12
  end
13
+
14
+ JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
75
15
  end
data/lib/json/version.rb CHANGED
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: false
1
2
  module JSON
2
3
  # JSON version
3
- VERSION = '1.0.0'
4
+ VERSION = '2.7.2'
4
5
  VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
6
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
7
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: