json 0.4.3 → 1.0.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.

Files changed (74) hide show
  1. data/CHANGES +6 -1
  2. data/README +49 -7
  3. data/Rakefile +216 -52
  4. data/TODO +1 -0
  5. data/VERSION +1 -1
  6. data/benchmarks/benchmark.txt +133 -0
  7. data/benchmarks/benchmark_generator.rb +44 -0
  8. data/benchmarks/benchmark_parser.rb +22 -0
  9. data/benchmarks/benchmark_rails.rb +26 -0
  10. data/data/example.json +1 -0
  11. data/data/index.html +37 -0
  12. data/data/prototype.js +2515 -0
  13. data/ext/json/ext/generator/Makefile +149 -0
  14. data/ext/json/ext/generator/extconf.rb +9 -0
  15. data/ext/json/ext/generator/generator.c +729 -0
  16. data/ext/json/ext/generator/unicode.c +184 -0
  17. data/ext/json/ext/generator/unicode.h +40 -0
  18. data/ext/json/ext/parser/Makefile +149 -0
  19. data/ext/json/ext/parser/extconf.rb +9 -0
  20. data/ext/json/ext/parser/parser.c +1551 -0
  21. data/ext/json/ext/parser/parser.rl +515 -0
  22. data/ext/json/ext/parser/unicode.c +156 -0
  23. data/ext/json/ext/parser/unicode.h +44 -0
  24. data/install.rb +13 -8
  25. data/lib/json.rb +101 -614
  26. data/lib/json/common.rb +184 -0
  27. data/lib/json/editor.rb +19 -10
  28. data/lib/json/ext.rb +13 -0
  29. data/lib/json/pure.rb +75 -0
  30. data/lib/json/pure/generator.rb +321 -0
  31. data/lib/json/pure/parser.rb +210 -0
  32. data/lib/json/version.rb +8 -0
  33. data/tests/fixtures/fail1.json +1 -0
  34. data/tests/fixtures/fail10.json +1 -0
  35. data/tests/fixtures/fail11.json +1 -0
  36. data/tests/fixtures/fail12.json +1 -0
  37. data/tests/fixtures/fail13.json +1 -0
  38. data/tests/fixtures/fail14.json +1 -0
  39. data/tests/fixtures/fail15.json +1 -0
  40. data/tests/fixtures/fail16.json +1 -0
  41. data/tests/fixtures/fail17.json +1 -0
  42. data/tests/fixtures/fail19.json +1 -0
  43. data/tests/fixtures/fail2.json +1 -0
  44. data/tests/fixtures/fail20.json +1 -0
  45. data/tests/fixtures/fail21.json +1 -0
  46. data/tests/fixtures/fail22.json +1 -0
  47. data/tests/fixtures/fail23.json +1 -0
  48. data/tests/fixtures/fail24.json +1 -0
  49. data/tests/fixtures/fail25.json +1 -0
  50. data/tests/fixtures/fail26.json +1 -0
  51. data/tests/fixtures/fail27.json +2 -0
  52. data/tests/fixtures/fail28.json +2 -0
  53. data/tests/fixtures/fail3.json +1 -0
  54. data/tests/fixtures/fail4.json +1 -0
  55. data/tests/fixtures/fail5.json +1 -0
  56. data/tests/fixtures/fail6.json +1 -0
  57. data/tests/fixtures/fail7.json +1 -0
  58. data/tests/fixtures/fail8.json +1 -0
  59. data/tests/fixtures/fail9.json +1 -0
  60. data/tests/fixtures/pass1.json +56 -0
  61. data/tests/fixtures/pass18.json +1 -0
  62. data/tests/fixtures/pass2.json +1 -0
  63. data/tests/fixtures/pass3.json +6 -0
  64. data/tests/runner.rb +8 -2
  65. data/tests/test_json.rb +102 -154
  66. data/tests/test_json_addition.rb +94 -0
  67. data/tests/test_json_fixtures.rb +30 -0
  68. data/tests/test_json_generate.rb +81 -0
  69. data/tests/test_json_unicode.rb +55 -0
  70. data/tools/fuzz.rb +133 -0
  71. data/tools/server.rb +62 -0
  72. metadata +87 -10
  73. data/bla.json.tmp +0 -0
  74. data/lib/json.rb.orig +0 -708
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'json'
5
+
6
+ class TC_JSONAddition < Test::Unit::TestCase
7
+ include JSON
8
+
9
+ class A
10
+ def initialize(a)
11
+ @a = a
12
+ end
13
+
14
+ attr_reader :a
15
+
16
+ def ==(other)
17
+ a == other.a
18
+ end
19
+
20
+ def self.json_create(object)
21
+ new(*object['args'])
22
+ end
23
+
24
+ def to_json(*args)
25
+ {
26
+ 'json_class' => self.class,
27
+ 'args' => [ @a ],
28
+ }.to_json(*args)
29
+ end
30
+ end
31
+
32
+ class B
33
+ def to_json(*args)
34
+ {
35
+ 'json_class' => self.class,
36
+ }.to_json(*args)
37
+ end
38
+ end
39
+
40
+ class C
41
+ def to_json(*args)
42
+ {
43
+ 'json_class' => 'TC_JSONAddition::Nix',
44
+ }.to_json(*args)
45
+ end
46
+ end
47
+
48
+ def setup
49
+ $KCODE = 'UTF8'
50
+ end
51
+
52
+ def test_extended_json
53
+ a = A.new(666)
54
+ assert A.json_creatable?
55
+ json = generate(a)
56
+ a_again = JSON.parse(json)
57
+ assert_kind_of a.class, a_again
58
+ assert_equal a, a_again
59
+ end
60
+
61
+ def test_extended_json_fail
62
+ b = B.new
63
+ assert !B.json_creatable?
64
+ json = generate(b)
65
+ assert_equal({ 'json_class' => B.name }, JSON.parse(json))
66
+ end
67
+
68
+ def test_extended_json_fail
69
+ c = C.new
70
+ assert !C.json_creatable?
71
+ json = generate(c)
72
+ assert_raises(ArgumentError) { JSON.parse(json) }
73
+ end
74
+
75
+ def test_raw_strings
76
+ raw = ''
77
+ raw_array = []
78
+ for i in 0..255
79
+ raw << i
80
+ raw_array << i
81
+ end
82
+ json = raw.to_json_raw
83
+ json_raw_object = raw.to_json_raw_object
84
+ hash = { 'json_class' => 'String', 'raw'=> raw_array }
85
+ assert_equal hash, json_raw_object
86
+ json_raw = <<EOT.chomp
87
+ {\"json_class\":\"String\",\"raw\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]}
88
+ EOT
89
+ # "
90
+ assert_equal json_raw, json
91
+ raw_again = JSON.parse(json)
92
+ assert_equal raw, raw_again
93
+ end
94
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'json'
5
+
6
+ class TC_JSONFixtures < Test::Unit::TestCase
7
+ def setup
8
+ $KCODE = 'UTF8'
9
+ fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.json')
10
+ passed, failed = Dir[fixtures].partition { |f| f['pass'] }
11
+ @passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
12
+ @failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
13
+ end
14
+
15
+ def test_passing
16
+ for (name, source) in @passed
17
+ assert JSON.parse(source),
18
+ "Did not pass for fixture '#{name}'"
19
+ end
20
+ end
21
+
22
+ def test_failing
23
+ for (name, source) in @failed
24
+ assert_raises(JSON::ParserError,
25
+ "Did not fail for fixture '#{name}'") do
26
+ JSON.parse(source)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,81 @@
1
+ require 'test/unit'
2
+ require 'json'
3
+
4
+ class TC_JSONGenerate < Test::Unit::TestCase
5
+ include JSON
6
+
7
+ def setup
8
+ $KCODE = 'UTF8'
9
+ @hash = {
10
+ 'a' => 2,
11
+ 'b' => 3.141,
12
+ 'c' => 'c',
13
+ 'd' => [ 1, "b", 3.14 ],
14
+ 'e' => { 'foo' => 'bar' },
15
+ 'g' => "\"\0\037",
16
+ 'h' => 1000.0,
17
+ 'i' => 0.001
18
+ }
19
+ @json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
20
+ '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
21
+ @json3 = <<'EOT'.chomp
22
+ {
23
+ "a": 2,
24
+ "b": 3.141,
25
+ "c": "c",
26
+ "d": [
27
+ 1,
28
+ "b",
29
+ 3.14
30
+ ],
31
+ "e": {
32
+ "foo": "bar"
33
+ },
34
+ "g": "\"\u0000\u001f",
35
+ "h": 1000.0,
36
+ "i": 0.001
37
+ }
38
+ EOT
39
+ end
40
+
41
+ def test_unparse
42
+ json = unparse(@hash)
43
+ assert_equal(@json2, json)
44
+ parsed_json = parse(json)
45
+ assert_equal(@hash, parsed_json)
46
+ json = generate({1=>2})
47
+ assert_equal('{"1":2}', json)
48
+ parsed_json = parse(json)
49
+ assert_equal({"1"=>2}, parsed_json)
50
+ end
51
+
52
+ def test_unparse_pretty
53
+ json = pretty_unparse(@hash)
54
+ assert_equal(@json3, json)
55
+ parsed_json = parse(json)
56
+ assert_equal(@hash, parsed_json)
57
+ json = pretty_generate({1=>2})
58
+ assert_equal(<<'EOT'.chomp, json)
59
+ {
60
+ "1": 2
61
+ }
62
+ EOT
63
+ parsed_json = parse(json)
64
+ assert_equal({"1"=>2}, parsed_json)
65
+ end
66
+
67
+ def test_states
68
+ json = generate({1=>2}, nil)
69
+ assert_equal('{"1":2}', json)
70
+ s = JSON.state.new(:check_circular => true)
71
+ #assert s.check_circular
72
+ h = { 1=>2 }
73
+ h[3] = h
74
+ assert_raises(JSON::CircularDatastructure) { generate(h, s) }
75
+ s = JSON.state.new(:check_circular => true)
76
+ #assert s.check_circular
77
+ a = [ 1, 2 ]
78
+ a << a
79
+ assert_raises(JSON::CircularDatastructure) { generate(a, s) }
80
+ end
81
+ end
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'json'
5
+
6
+ class TC_JSONUnicode < Test::Unit::TestCase
7
+ include JSON
8
+
9
+ def setup
10
+ $KCODE = 'UTF8'
11
+ end
12
+
13
+ def test_unicode
14
+ assert_equal '""', ''.to_json
15
+ assert_equal '"\\b"', "\b".to_json
16
+ assert_equal '"\u0001"', 0x1.chr.to_json
17
+ assert_equal '"\u001f"', 0x1f.chr.to_json
18
+ assert_equal '" "', ' '.to_json
19
+ assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
20
+ utf8 = [ "© ≠ €! \01" ]
21
+ json = '["\u00a9 \u2260 \u20ac! \u0001"]'
22
+ assert_equal json, utf8.to_json
23
+ assert_equal utf8, parse(json)
24
+ utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
25
+ json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
26
+ assert_equal json, utf8.to_json
27
+ assert_equal utf8, parse(json)
28
+ utf8 = ['საქართველო']
29
+ json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
30
+ assert_equal json, utf8.to_json
31
+ assert_equal utf8, parse(json)
32
+ assert_equal '["\\u00c3"]', JSON.generate(["Ã"])
33
+ assert_equal ["€"], JSON.parse('["\u20ac"]')
34
+ utf8 = ["\xf0\xa0\x80\x81"]
35
+ json = '["\ud840\udc01"]'
36
+ assert_equal json, JSON.generate(utf8)
37
+ assert_equal utf8, JSON.parse(json)
38
+ end
39
+
40
+ def test_chars
41
+ (0..0x7f).each do |i|
42
+ json = '["\u%04x"]' % i
43
+ assert_equal i, JSON.parse(json).first[0]
44
+ if [?\b, ?\n, ?\r, ?\t, ?\f].include?(i)
45
+ assert_equal '[' << ('' << i).dump << ']', JSON.generate(["" << i])
46
+ elsif i < 0x20
47
+ assert_equal json, JSON.generate(["" << i])
48
+ end
49
+ end
50
+ assert_raises(JSON::GeneratorError) do
51
+ JSON.generate(["" << 0x80])
52
+ end
53
+ assert_equal "\302\200", JSON.parse('["\u0080"]').first
54
+ end
55
+ end
@@ -0,0 +1,133 @@
1
+ $KCODE='UTF8'
2
+ require 'json'
3
+ require 'irb'
4
+ require 'pp'
5
+
6
+ class Fuzzer
7
+ def initialize(n, freqs = {})
8
+ sum = freqs.inject(0.0) { |s, x| s + x.last }
9
+ freqs.each_key { |x| freqs[x] /= sum }
10
+ s = 0.0
11
+ freqs.each_key do |x|
12
+ freqs[x] = s .. (s + t = freqs[x])
13
+ s += t
14
+ end
15
+ @freqs = freqs
16
+ @n = n
17
+ @alpha = (0..0x7f).to_a
18
+ end
19
+
20
+ def random_string
21
+ s = ''
22
+ 30.times { s << @alpha[rand(@alpha.size)] }
23
+ s
24
+ end
25
+
26
+ def pick
27
+ r = rand
28
+ found = @freqs.find { |k, f| f.include? rand }
29
+ found && found.first
30
+ end
31
+
32
+ def make_pick
33
+ k = pick
34
+ case
35
+ when k == Hash, k == Array
36
+ k.new
37
+ when k == true, k == false, k == nil
38
+ k
39
+ when k == String
40
+ random_string
41
+ when k == Fixnum
42
+ rand(2 ** 30) - 2 ** 29
43
+ when k == Bignum
44
+ rand(2 ** 70) - 2 ** 69
45
+ end
46
+ end
47
+
48
+ def fuzz(current = nil)
49
+ if @n > 0
50
+ case current
51
+ when nil
52
+ @n -= 1
53
+ current = fuzz [ Hash, Array ][rand(2)].new
54
+ when Array
55
+ while @n > 0
56
+ @n -= 1
57
+ current << case p = make_pick
58
+ when Array, Hash
59
+ fuzz(p)
60
+ else
61
+ p
62
+ end
63
+ end
64
+ when Hash
65
+ while @n > 0
66
+ @n -= 1
67
+ current[random_string] = case p = make_pick
68
+ when Array, Hash
69
+ fuzz(p)
70
+ else
71
+ p
72
+ end
73
+ end
74
+ end
75
+ end
76
+ current
77
+ end
78
+ end
79
+
80
+ class MyState < JSON.state
81
+ WS = " \r\t\n"
82
+
83
+ def initialize
84
+ super(
85
+ :indent => make_spaces,
86
+ :space => make_spaces,
87
+ :space_before => make_spaces,
88
+ :object_nl => make_spaces,
89
+ :array_nl => make_spaces
90
+ )
91
+ end
92
+
93
+ def make_spaces
94
+ s = ''
95
+ rand(1).times { s << WS[rand(WS.size)] }
96
+ s
97
+ end
98
+ end
99
+
100
+ n = (ARGV.shift || 500).to_i
101
+ loop do
102
+ fuzzer = Fuzzer.new(n,
103
+ Hash => 25,
104
+ Array => 25,
105
+ String => 10,
106
+ Fixnum => 10,
107
+ Bignum => 10,
108
+ nil => 5,
109
+ true => 5,
110
+ false => 5
111
+ )
112
+ o1 = fuzzer.fuzz
113
+ json = JSON.generate o1, MyState.new
114
+ if $DEBUG
115
+ puts "-" * 80
116
+ puts json, json.size
117
+ else
118
+ puts json.size
119
+ end
120
+ begin
121
+ o2 = JSON.parse(json)
122
+ rescue JSON::ParserError => e
123
+ puts "Caught #{e.class}: #{e.message}\n#{e.backtrace * "\n"}"
124
+ puts "o1 = #{o1.inspect}", "json = #{json}", "json_str = #{json.inspect}"
125
+ puts "locals = #{local_variables.inspect}"
126
+ exit
127
+ end
128
+ if o1 != o2
129
+ puts "mismatch", "o1 = #{o1.inspect}", "o2 = #{o2.inspect}",
130
+ "json = #{json}", "json_str = #{json.inspect}"
131
+ puts "locals = #{local_variables.inspect}"
132
+ end
133
+ end
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $KCODE='UTF8'
4
+ require 'webrick'
5
+ include WEBrick
6
+ $:.unshift 'ext'
7
+ $:.unshift 'lib'
8
+ require 'json'
9
+
10
+ class JSONServlet < HTTPServlet::AbstractServlet
11
+ @@count = 1
12
+
13
+ def do_GET(req, res)
14
+ obj = {
15
+ "TIME" => Time.now.strftime("%FT%T"),
16
+ "foo" => "Bär",
17
+ "bar" => "© ≠ €!",
18
+ 'a' => 2,
19
+ 'b' => 3.141,
20
+ 'COUNT' => @@count += 1,
21
+ 'c' => 'c',
22
+ 'd' => [ 1, "b", 3.14 ],
23
+ 'e' => { 'foo' => 'bar' },
24
+ 'g' => "松本行弘",
25
+ 'h' => 1000.0,
26
+ 'i' => 0.001,
27
+ 'j' => "\xf0\xa0\x80\x81",
28
+ }
29
+ res.body = JSON.generate obj
30
+ res['Content-Type'] = "application/json"
31
+ end
32
+ end
33
+
34
+ def create_server(err, dir, port)
35
+ dir = File.expand_path(dir)
36
+ err.puts "Surf to:", "http://#{Socket.gethostname}:#{port}"
37
+
38
+ s = HTTPServer.new(
39
+ :Port => port,
40
+ :DocumentRoot => dir,
41
+ :Logger => WEBrick::Log.new(err),
42
+ :AccessLog => [
43
+ [ err, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
44
+ [ err, WEBrick::AccessLog::REFERER_LOG_FORMAT ],
45
+ [ err, WEBrick::AccessLog::AGENT_LOG_FORMAT ]
46
+ ]
47
+ )
48
+ s.mount("/json", JSONServlet)
49
+ s
50
+ end
51
+
52
+ default_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'data'))
53
+ dir = ARGV.shift || default_dir
54
+ port = (ARGV.shift || 6666).to_i
55
+ s = create_server(STDERR, dir, 6666)
56
+ t = Thread.new { s.start }
57
+ trap(:INT) do
58
+ s.shutdown
59
+ t.join
60
+ exit
61
+ end
62
+ sleep