json 1.0.0 → 2.7.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.
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: