json 1.1.9 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of json might be problematic. Click here for more details.

@@ -19,15 +19,19 @@
19
19
  #ifdef HAVE_RUBY_ENCODING_H
20
20
  #include "ruby/encoding.h"
21
21
  #define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
22
+ static VALUE mEncoding_ASCII_8BIT, mEncoding_UTF_8, mEncoding_UTF_16BE,
23
+ mEncoding_UTF_16LE, mEncoding_UTF_32BE, mEncoding_UTF_32LE;
24
+ static ID i_encoding, i_encode, i_encode_bang, i_force_encoding;
22
25
  #else
23
26
  #define FORCE_UTF8(obj)
27
+ static ID i_iconv;
24
28
  #endif
25
29
 
26
30
  static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
27
31
  static VALUE CNaN, CInfinity, CMinusInfinity;
28
32
 
29
33
  static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
30
- i_chr, i_max_nesting, i_allow_nan, i_object_class, i_array_class;
34
+ i_chr, i_max_nesting, i_allow_nan, i_object_class, i_array_class;
31
35
 
32
36
  #define MinusInfinity "-Infinity"
33
37
 
@@ -484,6 +488,54 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
484
488
  *
485
489
  */
486
490
 
491
+ inline static VALUE convert_encoding(VALUE source)
492
+ {
493
+ char *ptr = RSTRING_PTR(source);
494
+ long len = RSTRING_LEN(source);
495
+ if (len < 2) {
496
+ rb_raise(eParserError, "A JSON text must at least contain two octets!");
497
+ }
498
+ #ifdef HAVE_RUBY_ENCODING_H
499
+ {
500
+ VALUE encoding = rb_funcall(source, i_encoding, 0);
501
+ if (encoding == mEncoding_ASCII_8BIT) {
502
+ if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
503
+ source = rb_str_dup(source);
504
+ rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_32BE);
505
+ source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
506
+ } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
507
+ source = rb_str_dup(source);
508
+ rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_16BE);
509
+ source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
510
+ } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
511
+ source = rb_str_dup(source);
512
+ rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_32LE);
513
+ source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
514
+ } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
515
+ source = rb_str_dup(source);
516
+ rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_16LE);
517
+ source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
518
+ } else {
519
+ source = rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_8);
520
+ }
521
+ } else {
522
+ source = rb_funcall(source, i_encode, 1, mEncoding_UTF_8);
523
+ }
524
+ }
525
+ #else
526
+ if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
527
+ source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source);
528
+ } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
529
+ source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source);
530
+ } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
531
+ source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source);
532
+ } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
533
+ source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source);
534
+ }
535
+ #endif
536
+ return source;
537
+ }
538
+
487
539
  /*
488
540
  * call-seq: new(source, opts => {})
489
541
  *
@@ -514,12 +566,9 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
514
566
  VALUE source, opts;
515
567
  GET_STRUCT;
516
568
  rb_scan_args(argc, argv, "11", &source, &opts);
517
- source = StringValue(source);
569
+ source = convert_encoding(StringValue(source));
518
570
  ptr = RSTRING_PTR(source);
519
571
  len = RSTRING_LEN(source);
520
- if (len < 2) {
521
- rb_raise(eParserError, "A JSON text must at least contain two octets!");
522
- }
523
572
  if (!NIL_P(opts)) {
524
573
  opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
525
574
  if (NIL_P(opts)) {
@@ -576,18 +625,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
576
625
  json->array_class = Qnil;
577
626
  }
578
627
  json->current_nesting = 0;
579
- /*
580
- Convert these?
581
- if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
582
- rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
583
- } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
584
- rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
585
- } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
586
- rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
587
- } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
588
- rb_raise(eParserError, "Only UTF8 octet streams are supported atm!");
589
- }
590
- */
591
628
  json->len = len;
592
629
  json->source = ptr;
593
630
  json->Vsource = source;
@@ -683,4 +720,18 @@ void Init_parser()
683
720
  i_allow_nan = rb_intern("allow_nan");
684
721
  i_object_class = rb_intern("object_class");
685
722
  i_array_class = rb_intern("array_class");
723
+ #ifdef HAVE_RUBY_ENCODING_H
724
+ mEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
725
+ mEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
726
+ mEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
727
+ mEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
728
+ mEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
729
+ mEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
730
+ i_encoding = rb_intern("encoding");
731
+ i_encode = rb_intern("encode");
732
+ i_encode_bang = rb_intern("encode!");
733
+ i_force_encoding = rb_intern("force_encoding");
734
+ #else
735
+ i_iconv = rb_intern("iconv");
736
+ #endif
686
737
  }
@@ -105,7 +105,7 @@ char *JSON_convert_UTF16_to_UTF8 (
105
105
  } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
106
106
  ruby_xfree(tmp);
107
107
  rb_raise(rb_path2class("JSON::ParserError"),
108
- "source sequence is illegal/malformed near %s", source);
108
+ "\\uXXXX is illegal/malformed utf-16 near %s", source);
109
109
  }
110
110
  } else { /* We don't have the 16 bits following the high surrogate. */
111
111
  ruby_xfree(tmp);
@@ -118,7 +118,7 @@ char *JSON_convert_UTF16_to_UTF8 (
118
118
  if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
119
119
  ruby_xfree(tmp);
120
120
  rb_raise(rb_path2class("JSON::ParserError"),
121
- "source sequence is illegal/malformed near %s", source);
121
+ "\\uXXXX is illegal/malformed utf-16 near %s", source);
122
122
  }
123
123
  }
124
124
  /* Figure out how many bytes the result will require */
@@ -1,4 +1,5 @@
1
1
  require 'json/version'
2
+ require 'iconv'
2
3
 
3
4
  module JSON
4
5
  class << self
@@ -74,7 +75,7 @@ module JSON
74
75
  end
75
76
  self.create_id = 'json_class'
76
77
 
77
- NaN = (-1.0) ** 0.5
78
+ NaN = 0.0/0
78
79
 
79
80
  Infinity = 1.0/0
80
81
 
@@ -105,7 +106,7 @@ module JSON
105
106
 
106
107
  module_function
107
108
 
108
- # Parse the JSON string _source_ into a Ruby data structure and return it.
109
+ # Parse the JSON document _source_ into a Ruby data structure and return it.
109
110
  #
110
111
  # _opts_ can have the following
111
112
  # keys:
@@ -122,9 +123,9 @@ module JSON
122
123
  JSON.parser.new(source, opts).parse
123
124
  end
124
125
 
125
- # Parse the JSON string _source_ into a Ruby data structure and return it.
126
+ # Parse the JSON document _source_ into a Ruby data structure and return it.
126
127
  # The bang version of the parse method, defaults to the more dangerous values
127
- # for the _opts_ hash, so be sure only to parse trusted _source_ strings.
128
+ # for the _opts_ hash, so be sure only to parse trusted _source_ documents.
128
129
  #
129
130
  # _opts_ can have the following keys:
130
131
  # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
@@ -145,9 +146,8 @@ module JSON
145
146
  JSON.parser.new(source, opts).parse
146
147
  end
147
148
 
148
- # Unparse the Ruby data structure _obj_ into a single line JSON string and
149
- # return it. _state_ is
150
- # * a JSON::State object,
149
+ # Generate a JSON document from the Ruby data structure _obj_ and return
150
+ # it. _state_ is * a JSON::State object,
151
151
  # * or a Hash like object (responding to to_hash),
152
152
  # * an object convertible into a hash by a to_h method,
153
153
  # that is used as or to configure a State object.
@@ -180,7 +180,11 @@ module JSON
180
180
  else
181
181
  state = State.new
182
182
  end
183
- obj.to_json(state)
183
+ result = obj.to_json(state)
184
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
185
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
186
+ end
187
+ result
184
188
  end
185
189
 
186
190
  # :stopdoc:
@@ -190,14 +194,17 @@ module JSON
190
194
  module_function :unparse
191
195
  # :startdoc:
192
196
 
193
- # Unparse the Ruby data structure _obj_ into a single line JSON string and
194
- # return it. This method disables the checks for circles in Ruby objects, and
195
- # also generates NaN, Infinity, and, -Infinity float values.
197
+ # Generate a JSON document from the Ruby data structure _obj_ and return it.
198
+ # This method disables the checks for circles in Ruby objects.
196
199
  #
197
200
  # *WARNING*: Be careful not to pass any Ruby data structures with circles as
198
201
  # _obj_ argument, because this will cause JSON to go into an infinite loop.
199
202
  def fast_generate(obj)
200
- obj.to_json(nil)
203
+ result = obj.to_json(nil)
204
+ if result !~ /\A(?:\[.*\]|\{.*\})\Z/
205
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
206
+ end
207
+ result
201
208
  end
202
209
 
203
210
  # :stopdoc:
@@ -206,8 +213,9 @@ module JSON
206
213
  module_function :fast_unparse
207
214
  # :startdoc:
208
215
 
209
- # Unparse the Ruby data structure _obj_ into a JSON string and return it. The
210
- # returned string is a prettier form of the string returned by #unparse.
216
+ # Generate a JSON document from the Ruby data structure _obj_ and return it.
217
+ # The returned document is a prettier form of the document returned by
218
+ # #unparse.
211
219
  #
212
220
  # The _opts_ argument can be used to configure the generator, see the
213
221
  # generate method for a more detailed explanation.
@@ -229,7 +237,11 @@ module JSON
229
237
  end
230
238
  state.configure(opts)
231
239
  end
232
- obj.to_json(state)
240
+ result = obj.to_json(state)
241
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
242
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
243
+ end
244
+ result
233
245
  end
234
246
 
235
247
  # :stopdoc:
@@ -270,8 +282,6 @@ module JSON
270
282
  proc.call result
271
283
  end
272
284
  end
273
- private :recurse_proc
274
- module_function :recurse_proc
275
285
 
276
286
  alias restore load
277
287
  module_function :restore
@@ -307,6 +317,11 @@ module JSON
307
317
  rescue JSON::NestingError
308
318
  raise ArgumentError, "exceed depth limit"
309
319
  end
320
+
321
+ # Shortuct for iconv.
322
+ def self.iconv(to, from, string)
323
+ Iconv.iconv(to, from, string).first
324
+ end
310
325
  end
311
326
 
312
327
  module ::Kernel
@@ -38,11 +38,11 @@ module JSON
38
38
 
39
39
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
40
40
  # UTF16 big endian characters as \u????, and return it.
41
- if String.method_defined?(:force_encoding)
41
+ if defined?(::Encoding)
42
42
  def utf8_to_json(string) # :nodoc:
43
43
  string = string.dup
44
44
  string << '' # XXX workaround: avoid buffer sharing
45
- string.force_encoding(Encoding::ASCII_8BIT)
45
+ string.force_encoding(::Encoding::ASCII_8BIT)
46
46
  string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
47
47
  string.gsub!(/(
48
48
  (?:
@@ -56,7 +56,7 @@ module JSON
56
56
  s = JSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
57
57
  s.gsub!(/.{4}/n, '\\\\u\&')
58
58
  }
59
- string.force_encoding(Encoding::UTF_8)
59
+ string.force_encoding(::Encoding::UTF_8)
60
60
  string
61
61
  rescue Iconv::Failure => e
62
62
  raise GeneratorError, "Caught #{e.class}: #{e}"
@@ -351,13 +351,13 @@ module JSON
351
351
  def to_json(state = nil, *)
352
352
  case
353
353
  when infinite?
354
- if !state || state.allow_nan?
354
+ if state && state.allow_nan?
355
355
  to_s
356
356
  else
357
357
  raise GeneratorError, "#{self} not allowed in JSON"
358
358
  end
359
359
  when nan?
360
- if !state || state.allow_nan?
360
+ if state && state.allow_nan?
361
361
  to_s
362
362
  else
363
363
  raise GeneratorError, "#{self} not allowed in JSON"
@@ -369,11 +369,25 @@ module JSON
369
369
  end
370
370
 
371
371
  module String
372
- # This string should be encoded with UTF-8 A call to this method
373
- # returns a JSON string encoded with UTF16 big endian characters as
374
- # \u????.
375
- def to_json(*)
376
- '"' << JSON.utf8_to_json(self) << '"'
372
+ if defined?(::Encoding)
373
+ # This string should be encoded with UTF-8 A call to this method
374
+ # returns a JSON string encoded with UTF16 big endian characters as
375
+ # \u????.
376
+ def to_json(*)
377
+ if encoding == ::Encoding::UTF_8
378
+ '"' << JSON.utf8_to_json(self) << '"'
379
+ else
380
+ string = encode(::Encoding::UTF_8)
381
+ '"' << JSON.utf8_to_json(string) << '"'
382
+ end
383
+ end
384
+ else
385
+ # This string should be encoded with UTF-8 A call to this method
386
+ # returns a JSON string encoded with UTF16 big endian characters as
387
+ # \u????.
388
+ def to_json(*)
389
+ '"' << JSON.utf8_to_json(self) << '"'
390
+ end
377
391
  end
378
392
 
379
393
  # Module that holds the extinding methods if, the String module is
@@ -66,7 +66,41 @@ module JSON
66
66
  # * *object_class*: Defaults to Hash
67
67
  # * *array_class*: Defaults to Array
68
68
  def initialize(source, opts = {})
69
- super
69
+ if defined?(::Encoding)
70
+ if source.encoding == Encoding::ASCII_8BIT
71
+ b = source[0, 4].bytes.to_a
72
+ source = case
73
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
74
+ source.dup.force_encoding(Encoding::UTF_32BE).encode!(Encoding::UTF_8)
75
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
76
+ source.dup.force_encoding(Encoding::UTF_16BE).encode!(Encoding::UTF_8)
77
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
78
+ source.dup.force_encoding(Encoding::UTF_32LE).encode!(Encoding::UTF_8)
79
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
80
+ source.dup.force_encoding(Encoding::UTF_16LE).encode!(Encoding::UTF_8)
81
+ else
82
+ source.dup
83
+ end
84
+ else
85
+ source = source.encode(Encoding::UTF_8)
86
+ end
87
+ source.force_encoding(Encoding::ASCII_8BIT)
88
+ else
89
+ b = source
90
+ source = case
91
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
92
+ JSON.iconv('utf-8', 'utf-32be', b)
93
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
94
+ JSON.iconv('utf-8', 'utf-16be', b)
95
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
96
+ JSON.iconv('utf-8', 'utf-32le', b)
97
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
98
+ JSON.iconv('utf-8', 'utf-16le', b)
99
+ else
100
+ b
101
+ end
102
+ end
103
+ super source
70
104
  if !opts.key?(:max_nesting) # defaults to 19
71
105
  @max_nesting = 19
72
106
  elsif opts[:max_nesting]
@@ -1,6 +1,6 @@
1
1
  module JSON
2
2
  # JSON version
3
- VERSION = '1.1.9'
3
+ VERSION = '1.2.0'
4
4
  VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -9,6 +9,20 @@ else require 'json'
9
9
  end
10
10
  require 'stringio'
11
11
 
12
+ unless Array.method_defined?(:permutation)
13
+ begin
14
+ require 'enumerator'
15
+ require 'permutation'
16
+ class Array
17
+ def permutation
18
+ Permutation.for(self).to_enum.map { |x| x.project }
19
+ end
20
+ end
21
+ rescue LoadError
22
+ warn "Skipping permutation tests."
23
+ end
24
+ end
25
+
12
26
  class TC_JSON < Test::Unit::TestCase
13
27
  include JSON
14
28
 
@@ -94,30 +108,24 @@ class TC_JSON < Test::Unit::TestCase
94
108
  assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
95
109
  end
96
110
 
97
- begin
98
- require 'permutation'
111
+ if Array.method_defined?(:permutation)
99
112
  def test_parse_more_complex_arrays
100
113
  a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
101
- perms = Permutation.for a
102
- perms.each do |perm|
103
- orig_ary = perm.project
104
- json = pretty_generate(orig_ary)
105
- assert_equal orig_ary, parse(json)
114
+ a.permutation.each do |perm|
115
+ json = pretty_generate(perm)
116
+ assert_equal perm, parse(json)
106
117
  end
107
118
  end
108
119
 
109
120
  def test_parse_complex_objects
110
121
  a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
111
- perms = Permutation.for a
112
- perms.each do |perm|
122
+ a.permutation.each do |perm|
113
123
  s = "a"
114
- orig_obj = perm.project.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
124
+ orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
115
125
  json = pretty_generate(orig_obj)
116
126
  assert_equal orig_obj, parse(json)
117
127
  end
118
128
  end
119
- rescue LoadError
120
- warn "Skipping permutation tests."
121
129
  end
122
130
 
123
131
  def test_parse_arrays
@@ -222,27 +230,27 @@ EOT
222
230
  def test_backslash
223
231
  data = [ '\\.(?i:gif|jpe?g|png)$' ]
224
232
  json = '["\\\\.(?i:gif|jpe?g|png)$"]'
225
- assert_equal json, JSON.unparse(data)
233
+ assert_equal json, JSON.generate(data)
226
234
  assert_equal data, JSON.parse(json)
227
235
  #
228
236
  data = [ '\\"' ]
229
237
  json = '["\\\\\""]'
230
- assert_equal json, JSON.unparse(data)
238
+ assert_equal json, JSON.generate(data)
231
239
  assert_equal data, JSON.parse(json)
232
240
  #
233
241
  json = '["/"]'
234
242
  data = JSON.parse(json)
235
243
  assert_equal ['/'], data
236
- assert_equal json, JSON.unparse(data)
244
+ assert_equal json, JSON.generate(data)
237
245
  #
238
246
  json = '["\""]'
239
247
  data = JSON.parse(json)
240
248
  assert_equal ['"'], data
241
- assert_equal json, JSON.unparse(data)
249
+ assert_equal json, JSON.generate(data)
242
250
  json = '["\\\'"]'
243
251
  data = JSON.parse(json)
244
252
  assert_equal ["'"], data
245
- assert_equal '["\'"]', JSON.unparse(data)
253
+ assert_equal '["\'"]', JSON.generate(data)
246
254
  end
247
255
 
248
256
  def test_wrong_inputs