json_pure 1.0.0 → 1.4.6

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.
Files changed (93) hide show
  1. data/CHANGES +155 -1
  2. data/COPYING +58 -0
  3. data/GPL +7 -7
  4. data/README +324 -45
  5. data/Rakefile +166 -124
  6. data/TODO +1 -1
  7. data/VERSION +1 -1
  8. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
  9. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
  10. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
  11. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
  12. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
  13. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
  14. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
  15. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
  16. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
  17. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
  18. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
  19. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
  20. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
  21. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
  22. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
  23. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
  24. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
  25. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
  26. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
  27. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
  28. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
  29. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
  30. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
  31. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
  32. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
  33. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
  34. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
  35. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
  36. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
  37. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
  38. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
  39. data/benchmarks/generator2_benchmark.rb +222 -0
  40. data/benchmarks/generator_benchmark.rb +224 -0
  41. data/benchmarks/ohai.json +1216 -0
  42. data/benchmarks/ohai.ruby +1 -0
  43. data/benchmarks/parser2_benchmark.rb +251 -0
  44. data/benchmarks/parser_benchmark.rb +259 -0
  45. data/bin/edit_json.rb +1 -3
  46. data/bin/prettify_json.rb +75 -0
  47. data/data/index.html +5 -4
  48. data/data/prototype.js +2764 -1095
  49. data/ext/json/ext/generator/extconf.rb +14 -3
  50. data/ext/json/ext/generator/generator.c +1022 -334
  51. data/ext/json/ext/generator/generator.h +197 -0
  52. data/ext/json/ext/parser/extconf.rb +9 -3
  53. data/ext/json/ext/parser/parser.c +961 -577
  54. data/ext/json/ext/parser/parser.h +71 -0
  55. data/ext/json/ext/parser/parser.rl +400 -123
  56. data/install.rb +0 -0
  57. data/lib/json/add/core.rb +148 -0
  58. data/lib/json/add/rails.rb +58 -0
  59. data/lib/json/common.rb +254 -47
  60. data/lib/json/editor.rb +236 -72
  61. data/lib/json/ext.rb +2 -0
  62. data/lib/json/pure/generator.rb +235 -117
  63. data/lib/json/pure/parser.rb +124 -25
  64. data/lib/json/pure.rb +5 -3
  65. data/lib/json/version.rb +1 -1
  66. data/lib/json.rb +2 -197
  67. data/tests/fixtures/fail18.json +1 -0
  68. data/tests/test_json.rb +181 -22
  69. data/tests/test_json_addition.rb +84 -16
  70. data/tests/test_json_encoding.rb +68 -0
  71. data/tests/test_json_fixtures.rb +9 -5
  72. data/tests/test_json_generate.rb +114 -14
  73. data/tests/test_json_rails.rb +144 -0
  74. data/tests/test_json_unicode.rb +35 -14
  75. data/tools/fuzz.rb +13 -7
  76. data/tools/server.rb +0 -1
  77. metadata +156 -122
  78. data/benchmarks/benchmark.txt +0 -133
  79. data/benchmarks/benchmark_generator.rb +0 -44
  80. data/benchmarks/benchmark_parser.rb +0 -22
  81. data/benchmarks/benchmark_rails.rb +0 -26
  82. data/ext/json/ext/generator/Makefile +0 -149
  83. data/ext/json/ext/generator/unicode.c +0 -184
  84. data/ext/json/ext/generator/unicode.h +0 -40
  85. data/ext/json/ext/parser/Makefile +0 -149
  86. data/ext/json/ext/parser/unicode.c +0 -156
  87. data/ext/json/ext/parser/unicode.h +0 -44
  88. data/tests/fixtures/pass18.json +0 -1
  89. data/tests/runner.rb +0 -24
  90. /data/tests/fixtures/{fail15.json → pass15.json} +0 -0
  91. /data/tests/fixtures/{fail16.json → pass16.json} +0 -0
  92. /data/tests/fixtures/{fail17.json → pass17.json} +0 -0
  93. /data/tests/fixtures/{fail26.json → pass26.json} +0 -0
@@ -34,30 +34,67 @@ module JSON
34
34
  "\x1f" => '\u001f',
35
35
  '"' => '\"',
36
36
  '\\' => '\\\\',
37
- '/' => '\/',
38
37
  } # :nodoc:
39
38
 
40
39
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
41
40
  # UTF16 big endian characters as \u????, and return it.
42
- def utf8_to_json(string) # :nodoc:
43
- string = string.gsub(/["\\\/\x0-\x1f]/) { |c| MAP[c] }
44
- string.gsub!(/(
45
- (?:
46
- [\xc2-\xdf][\x80-\xbf] |
47
- [\xe0-\xef][\x80-\xbf]{2} |
48
- [\xf0-\xf4][\x80-\xbf]{3}
49
- )+ |
50
- [\x80-\xc1\xf5-\xff] # invalid
51
- )/nx) { |c|
52
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
53
- s = JSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
54
- s.gsub!(/.{4}/n, '\\\\u\&')
55
- }
56
- string
57
- rescue Iconv::Failure => e
58
- raise GeneratorError, "Caught #{e.class}: #{e}"
41
+ if defined?(::Encoding)
42
+ def utf8_to_json(string) # :nodoc:
43
+ string = string.dup
44
+ string << '' # XXX workaround: avoid buffer sharing
45
+ string.force_encoding(::Encoding::ASCII_8BIT)
46
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
47
+ string.force_encoding(::Encoding::UTF_8)
48
+ string
49
+ end
50
+
51
+ def utf8_to_json_ascii(string) # :nodoc:
52
+ string = string.dup
53
+ string << '' # XXX workaround: avoid buffer sharing
54
+ string.force_encoding(::Encoding::ASCII_8BIT)
55
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
56
+ string.gsub!(/(
57
+ (?:
58
+ [\xc2-\xdf][\x80-\xbf] |
59
+ [\xe0-\xef][\x80-\xbf]{2} |
60
+ [\xf0-\xf4][\x80-\xbf]{3}
61
+ )+ |
62
+ [\x80-\xc1\xf5-\xff] # invalid
63
+ )/nx) { |c|
64
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
65
+ s = JSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
66
+ s.gsub!(/.{4}/n, '\\\\u\&')
67
+ }
68
+ string.force_encoding(::Encoding::UTF_8)
69
+ string
70
+ rescue Iconv::Failure => e
71
+ raise GeneratorError, "Caught #{e.class}: #{e}"
72
+ end
73
+ else
74
+ def utf8_to_json(string) # :nodoc:
75
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
76
+ end
77
+
78
+ def utf8_to_json_ascii(string) # :nodoc:
79
+ string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
80
+ string.gsub!(/(
81
+ (?:
82
+ [\xc2-\xdf][\x80-\xbf] |
83
+ [\xe0-\xef][\x80-\xbf]{2} |
84
+ [\xf0-\xf4][\x80-\xbf]{3}
85
+ )+ |
86
+ [\x80-\xc1\xf5-\xff] # invalid
87
+ )/nx) { |c|
88
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
89
+ s = JSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
90
+ s.gsub!(/.{4}/n, '\\\\u\&')
91
+ }
92
+ string
93
+ rescue Iconv::Failure => e
94
+ raise GeneratorError, "Caught #{e.class}: #{e}"
95
+ end
59
96
  end
60
- module_function :utf8_to_json
97
+ module_function :utf8_to_json, :utf8_to_json_ascii
61
98
 
62
99
  module Pure
63
100
  module Generator
@@ -75,7 +112,7 @@ module JSON
75
112
  when Hash
76
113
  new(opts)
77
114
  else
78
- new
115
+ SAFE_STATE_PROTOTYPE.dup
79
116
  end
80
117
  end
81
118
 
@@ -88,16 +125,21 @@ module JSON
88
125
  # * *space_before*: a string that is put before a : pair delimiter (default: ''),
89
126
  # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
90
127
  # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
91
- # * *check_circular*: true if checking for circular data structures
92
- # should be done, false (the default) otherwise.
128
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
129
+ # * *max_nesting*: sets the maximum level of data structure nesting in
130
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
131
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
132
+ # generated, otherwise an exception is thrown, if these values are
133
+ # encountered. This options defaults to false.
93
134
  def initialize(opts = {})
94
- @indent = opts[:indent] || ''
95
- @space = opts[:space] || ''
96
- @space_before = opts[:space_before] || ''
97
- @object_nl = opts[:object_nl] || ''
98
- @array_nl = opts[:array_nl] || ''
99
- @check_circular = !!(opts[:check_circular] || false)
100
- @seen = {}
135
+ @indent = ''
136
+ @space = ''
137
+ @space_before = ''
138
+ @object_nl = ''
139
+ @array_nl = ''
140
+ @allow_nan = false
141
+ @ascii_only = false
142
+ configure opts
101
143
  end
102
144
 
103
145
  # This string is used to indent levels in the JSON text.
@@ -117,27 +159,82 @@ module JSON
117
159
  # This string is put at the end of a line that holds a JSON array.
118
160
  attr_accessor :array_nl
119
161
 
120
- # Returns true, if circular data structures should be checked,
162
+ # This integer returns the maximum level of data structure nesting in
163
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
164
+ attr_accessor :max_nesting
165
+
166
+ # This integer returns the current depth data structure nesting in the
167
+ # generated JSON.
168
+ attr_accessor :depth
169
+
170
+ def check_max_nesting # :nodoc:
171
+ return if @max_nesting.zero?
172
+ current_nesting = depth + 1
173
+ current_nesting > @max_nesting and
174
+ raise NestingError, "nesting of #{current_nesting} is too deep"
175
+ end
176
+
177
+ # Returns true, if circular data structures are checked,
121
178
  # otherwise returns false.
122
179
  def check_circular?
123
- @check_circular
180
+ !@max_nesting.zero?
181
+ end
182
+
183
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
184
+ # valid JSON and output.
185
+ def allow_nan?
186
+ @allow_nan
124
187
  end
125
188
 
126
- # Returns _true_, if _object_ was already seen during this generating
127
- # run.
128
- def seen?(object)
129
- @seen.key?(object.__id__)
189
+ def ascii_only?
190
+ @ascii_only
130
191
  end
131
192
 
132
- # Remember _object_, to find out if it was already encountered (if a
133
- # cyclic data structure is if a cyclic data structure is rendered).
134
- def remember(object)
135
- @seen[object.__id__] = true
193
+ # Configure this State instance with the Hash _opts_, and return
194
+ # itself.
195
+ def configure(opts)
196
+ @indent = opts[:indent] if opts.key?(:indent)
197
+ @space = opts[:space] if opts.key?(:space)
198
+ @space_before = opts[:space_before] if opts.key?(:space_before)
199
+ @object_nl = opts[:object_nl] if opts.key?(:object_nl)
200
+ @array_nl = opts[:array_nl] if opts.key?(:array_nl)
201
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
202
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
203
+ @depth = opts[:depth] || 0
204
+ if !opts.key?(:max_nesting) # defaults to 19
205
+ @max_nesting = 19
206
+ elsif opts[:max_nesting]
207
+ @max_nesting = opts[:max_nesting]
208
+ else
209
+ @max_nesting = 0
210
+ end
211
+ self
136
212
  end
137
213
 
138
- # Forget _object_ for this generating run.
139
- def forget(object)
140
- @seen.delete object.__id__
214
+ # Returns the configuration instance variables as a hash, that can be
215
+ # passed to the configure method.
216
+ def to_h
217
+ result = {}
218
+ for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth]
219
+ result[iv.intern] = instance_variable_get("@#{iv}")
220
+ end
221
+ result
222
+ end
223
+
224
+ # Generates a valid JSON document from object +obj+ and returns the
225
+ # result. If no valid JSON document can be created this method raises a
226
+ # GeneratorError exception.
227
+ def generate(obj)
228
+ result = obj.to_json(self)
229
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
230
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
231
+ end
232
+ result
233
+ end
234
+
235
+ # Return the value returned by method +name+.
236
+ def [](name)
237
+ __send__ name
141
238
  end
142
239
  end
143
240
 
@@ -155,48 +252,40 @@ module JSON
155
252
  # _state_ is a JSON::State object, that can also be used to configure the
156
253
  # produced JSON string output further.
157
254
  # _depth_ is used to find out nesting depth, to indent accordingly.
158
- def to_json(state = nil, depth = 0, *)
159
- if state
160
- state = JSON.state.from_state(state)
161
- json_check_circular(state) { json_transform(state, depth) }
162
- else
163
- json_transform(state, depth)
164
- end
255
+ def to_json(state = nil, *)
256
+ state = State.from_state(state)
257
+ state.check_max_nesting
258
+ json_transform(state)
165
259
  end
166
260
 
167
261
  private
168
262
 
169
- def json_check_circular(state)
170
- if state
171
- state.seen?(self) and raise JSON::CircularDatastructure,
172
- "circular data structures not supported!"
173
- state.remember self
174
- end
175
- yield
176
- ensure
177
- state and state.forget self
178
- end
179
-
180
- def json_shift(state, depth)
181
- state and not state.object_nl.empty? or return ''
182
- state.indent * depth
263
+ def json_shift(state)
264
+ state.object_nl.empty? or return ''
265
+ state.indent * state.depth
183
266
  end
184
267
 
185
- def json_transform(state, depth)
268
+ def json_transform(state)
186
269
  delim = ','
187
- delim << state.object_nl if state
270
+ delim << state.object_nl
188
271
  result = '{'
189
- result << state.object_nl if state
190
- result << map { |key,value|
191
- s = json_shift(state, depth + 1)
192
- s << key.to_s.to_json(state, depth + 1)
193
- s << state.space_before if state
194
- s << ':'
195
- s << state.space if state
196
- s << value.to_json(state, depth + 1)
197
- }.join(delim)
198
- result << state.object_nl if state
199
- result << json_shift(state, depth)
272
+ result << state.object_nl
273
+ depth = state.depth += 1
274
+ first = true
275
+ indent = !state.object_nl.empty?
276
+ each { |key,value|
277
+ result << delim unless first
278
+ result << state.indent * depth if indent
279
+ result << key.to_s.to_json(state)
280
+ result << state.space_before
281
+ result << ':'
282
+ result << state.space
283
+ result << value.to_json(state)
284
+ first = false
285
+ }
286
+ depth = state.depth -= 1
287
+ result << state.object_nl
288
+ result << state.indent * depth if indent if indent
200
289
  result << '}'
201
290
  result
202
291
  end
@@ -207,46 +296,32 @@ module JSON
207
296
  # this Array instance.
208
297
  # _state_ is a JSON::State object, that can also be used to configure the
209
298
  # produced JSON string output further.
210
- # _depth_ is used to find out nesting depth, to indent accordingly.
211
- def to_json(state = nil, depth = 0, *)
212
- if state
213
- state = JSON.state.from_state(state)
214
- json_check_circular(state) { json_transform(state, depth) }
215
- else
216
- json_transform(state, depth)
217
- end
299
+ def to_json(state = nil, *)
300
+ state = State.from_state(state)
301
+ state.check_max_nesting
302
+ json_transform(state)
218
303
  end
219
304
 
220
305
  private
221
306
 
222
- def json_check_circular(state)
223
- if state
224
- state.seen?(self) and raise JSON::CircularDatastructure,
225
- "circular data structures not supported!"
226
- state.remember self
227
- end
228
- yield
229
- ensure
230
- state and state.forget self
231
- end
232
-
233
- def json_shift(state, depth)
234
- state and not state.array_nl.empty? or return ''
235
- state.indent * depth
236
- end
237
-
238
- def json_transform(state, depth)
307
+ def json_transform(state)
239
308
  delim = ','
240
- delim << state.array_nl if state
309
+ delim << state.array_nl
241
310
  result = '['
242
- result << state.array_nl if state
243
- result << map { |value|
244
- json_shift(state, depth + 1) << value.to_json(state, depth + 1)
245
- }.join(delim)
246
- result << state.array_nl if state
247
- result << json_shift(state, depth)
311
+ result << state.array_nl
312
+ depth = state.depth += 1
313
+ first = true
314
+ indent = !state.array_nl.empty?
315
+ each { |value|
316
+ result << delim unless first
317
+ result << state.indent * depth if indent
318
+ result << value.to_json(state)
319
+ first = false
320
+ }
321
+ depth = state.depth -= 1
322
+ result << state.array_nl
323
+ result << state.indent * depth if indent
248
324
  result << ']'
249
- result
250
325
  end
251
326
  end
252
327
 
@@ -257,22 +332,65 @@ module JSON
257
332
 
258
333
  module Float
259
334
  # Returns a JSON string representation for this Float number.
260
- def to_json(*) to_s end
335
+ def to_json(state = nil, *)
336
+ state = State.from_state(state)
337
+ case
338
+ when infinite?
339
+ if state.allow_nan?
340
+ to_s
341
+ else
342
+ raise GeneratorError, "#{self} not allowed in JSON"
343
+ end
344
+ when nan?
345
+ if state.allow_nan?
346
+ to_s
347
+ else
348
+ raise GeneratorError, "#{self} not allowed in JSON"
349
+ end
350
+ else
351
+ to_s
352
+ end
353
+ end
261
354
  end
262
355
 
263
356
  module String
264
- # This string should be encoded with UTF-8 A call to this method
265
- # returns a JSON string encoded with UTF16 big endian characters as
266
- # \u????.
267
- def to_json(*)
268
- '"' << JSON.utf8_to_json(self) << '"'
357
+ if defined?(::Encoding)
358
+ # This string should be encoded with UTF-8 A call to this method
359
+ # returns a JSON string encoded with UTF16 big endian characters as
360
+ # \u????.
361
+ def to_json(state = nil, *args)
362
+ state = State.from_state(state)
363
+ if encoding == ::Encoding::UTF_8
364
+ string = self
365
+ else
366
+ string = encode(::Encoding::UTF_8)
367
+ end
368
+ if state.ascii_only?
369
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
370
+ else
371
+ '"' << JSON.utf8_to_json(string) << '"'
372
+ end
373
+ end
374
+ else
375
+ # This string should be encoded with UTF-8 A call to this method
376
+ # returns a JSON string encoded with UTF16 big endian characters as
377
+ # \u????.
378
+ def to_json(state = nil, *args)
379
+ state = State.from_state(state)
380
+ if state.ascii_only?
381
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
382
+ else
383
+ '"' << JSON.utf8_to_json(self) << '"'
384
+ end
385
+ end
269
386
  end
270
387
 
271
388
  # Module that holds the extinding methods if, the String module is
272
389
  # included.
273
390
  module Extend
274
- # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
275
- # key "raw"). The Ruby String can be created by this module method.
391
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
392
+ # array for the key "raw"). The Ruby String can be created by this
393
+ # module method.
276
394
  def json_create(o)
277
395
  o['raw'].pack('C*')
278
396
  end
@@ -6,9 +6,12 @@ module JSON
6
6
  # into a Ruby data structure.
7
7
  class Parser < StringScanner
8
8
  STRING = /" ((?:[^\x0-\x1f"\\] |
9
+ # escaped special characters:
9
10
  \\["\\\/bfnrt] |
10
- \\u[0-9a-fA-F]{4})*)
11
- "/x
11
+ \\u[0-9a-fA-F]{4} |
12
+ # match all but escaped special characters:
13
+ \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
14
+ "/nx
12
15
  INTEGER = /(-?0|-?[1-9]\d*)/
13
16
  FLOAT = /(-?
14
17
  (?:0|[1-9]\d*)
@@ -18,6 +21,9 @@ module JSON
18
21
  (?i:e[+-]?\d+)
19
22
  )
20
23
  )/x
24
+ NAN = /NaN/
25
+ INFINITY = /Infinity/
26
+ MINUS_INFINITY = /-Infinity/
21
27
  OBJECT_OPEN = /\{/
22
28
  OBJECT_CLOSE = /\}/
23
29
  ARRAY_OPEN = /\[/
@@ -45,9 +51,75 @@ module JSON
45
51
  UNPARSED = Object.new
46
52
 
47
53
  # Creates a new JSON::Pure::Parser instance for the string _source_.
48
- def initialize(source)
49
- super
50
- @create_id = JSON.create_id
54
+ #
55
+ # It will be configured by the _opts_ hash. _opts_ can have the following
56
+ # keys:
57
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
58
+ # structures. Disable depth checking with :max_nesting => false|nil|0,
59
+ # it defaults to 19.
60
+ # * *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
+ # to false.
63
+ # * *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.
66
+ # * *create_additions*: If set to false, the Parser doesn't create
67
+ # additions even if a matchin class and create_id was found. This option
68
+ # defaults to true.
69
+ # * *object_class*: Defaults to Hash
70
+ # * *array_class*: Defaults to Array
71
+ def initialize(source, opts = {})
72
+ opts ||= {}
73
+ if defined?(::Encoding)
74
+ if source.encoding == ::Encoding::ASCII_8BIT
75
+ b = source[0, 4].bytes.to_a
76
+ source = case
77
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
78
+ source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
79
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
80
+ source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
81
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
82
+ source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
83
+
84
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
85
+ source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
86
+ else
87
+ source.dup
88
+ end
89
+ else
90
+ source = source.encode(::Encoding::UTF_8)
91
+ end
92
+ source.force_encoding(::Encoding::ASCII_8BIT)
93
+ else
94
+ b = source
95
+ source = case
96
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
97
+ JSON.iconv('utf-8', 'utf-32be', b)
98
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
99
+ JSON.iconv('utf-8', 'utf-16be', b)
100
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
101
+ JSON.iconv('utf-8', 'utf-32le', b)
102
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
103
+ JSON.iconv('utf-8', 'utf-16le', b)
104
+ else
105
+ b
106
+ end
107
+ end
108
+ super source
109
+ if !opts.key?(:max_nesting) # defaults to 19
110
+ @max_nesting = 19
111
+ elsif opts[:max_nesting]
112
+ @max_nesting = opts[:max_nesting]
113
+ else
114
+ @max_nesting = 0
115
+ end
116
+ @allow_nan = !!opts[:allow_nan]
117
+ @symbolize_names = !!opts[:symbolize_names]
118
+ ca = true
119
+ ca = opts[:create_additions] if opts.key?(:create_additions)
120
+ @create_id = ca ? JSON.create_id : nil
121
+ @object_class = opts[:object_class] || Hash
122
+ @array_class = opts[:array_class] || Array
51
123
  end
52
124
 
53
125
  alias source string
@@ -61,9 +133,11 @@ module JSON
61
133
  case
62
134
  when scan(OBJECT_OPEN)
63
135
  obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
136
+ @current_nesting = 1
64
137
  obj = parse_object
65
138
  when scan(ARRAY_OPEN)
66
139
  obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
140
+ @current_nesting = 1
67
141
  obj = parse_array
68
142
  when skip(IGNORE)
69
143
  ;
@@ -77,30 +151,40 @@ module JSON
77
151
 
78
152
  private
79
153
 
154
+ # Unescape characters in strings.
155
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
156
+ UNESCAPE_MAP.update({
157
+ ?" => '"',
158
+ ?\\ => '\\',
159
+ ?/ => '/',
160
+ ?b => "\b",
161
+ ?f => "\f",
162
+ ?n => "\n",
163
+ ?r => "\r",
164
+ ?t => "\t",
165
+ ?u => nil,
166
+ })
167
+
80
168
  def parse_string
81
169
  if scan(STRING)
82
170
  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
171
+ string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
172
+ if u = UNESCAPE_MAP[$&[1]]
173
+ u
174
+ else # \uXXXX
94
175
  bytes = ''
95
- c = $~[0]
96
176
  i = 0
97
- while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
177
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
98
178
  bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
99
179
  i += 1
100
180
  end
101
181
  JSON::UTF16toUTF8.iconv(bytes)
102
182
  end
103
183
  end
184
+ if string.respond_to?(:force_encoding)
185
+ string.force_encoding(::Encoding::UTF_8)
186
+ end
187
+ string
104
188
  else
105
189
  UNPARSED
106
190
  end
@@ -123,16 +207,30 @@ module JSON
123
207
  when (string = parse_string) != UNPARSED
124
208
  string
125
209
  when scan(ARRAY_OPEN)
126
- parse_array
210
+ @current_nesting += 1
211
+ ary = parse_array
212
+ @current_nesting -= 1
213
+ ary
127
214
  when scan(OBJECT_OPEN)
128
- parse_object
215
+ @current_nesting += 1
216
+ obj = parse_object
217
+ @current_nesting -= 1
218
+ obj
219
+ when @allow_nan && scan(NAN)
220
+ NaN
221
+ when @allow_nan && scan(INFINITY)
222
+ Infinity
223
+ when @allow_nan && scan(MINUS_INFINITY)
224
+ MinusInfinity
129
225
  else
130
226
  UNPARSED
131
227
  end
132
228
  end
133
229
 
134
230
  def parse_array
135
- result = []
231
+ raise NestingError, "nesting of #@current_nesting is too deep" if
232
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
233
+ result = @array_class.new
136
234
  delim = false
137
235
  until eos?
138
236
  case
@@ -162,7 +260,9 @@ module JSON
162
260
  end
163
261
 
164
262
  def parse_object
165
- result = {}
263
+ raise NestingError, "nesting of #@current_nesting is too deep" if
264
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
265
+ result = @object_class.new
166
266
  delim = false
167
267
  until eos?
168
268
  case
@@ -173,7 +273,7 @@ module JSON
173
273
  end
174
274
  skip(IGNORE)
175
275
  unless (value = parse_value).equal? UNPARSED
176
- result[string] = value
276
+ result[@symbolize_names ? string.to_sym : string] = value
177
277
  delim = false
178
278
  skip(IGNORE)
179
279
  if scan(COLLECTION_DELIMITER)
@@ -190,11 +290,10 @@ module JSON
190
290
  if delim
191
291
  raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
192
292
  end
193
- if klassname = result[@create_id]
293
+ if @create_id and klassname = result[@create_id]
194
294
  klass = JSON.deep_const_get klassname
195
295
  break unless klass and klass.json_creatable?
196
296
  result = klass.json_create(result)
197
- result
198
297
  end
199
298
  break
200
299
  when skip(IGNORE)