json_pure 1.1.9 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 */
data/lib/json/common.rb CHANGED
@@ -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]
data/lib/json/version.rb CHANGED
@@ -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:
data/tests/test_json.rb CHANGED
@@ -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