ffi-yajl 0.1.7-universal-java → 0.2.0-universal-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f81763696eea5a3b5cb4c13f03bcfafa1ba75883
4
- data.tar.gz: 6c7f329234428cd07ec9fbf73b1040c27d82fab3
3
+ metadata.gz: 07c450eaba7a2fb80f921d896a2fa36bbfca3a09
4
+ data.tar.gz: 1368094d6809554303390c8dc38e33b837d6da5d
5
5
  SHA512:
6
- metadata.gz: 7a4b4112c08b390a78848ffcdac91e56e9e1009425cdcfb196cb69e3e983c9abd356217ad1e3fd4c73d398570c0b46fa61dc46347d582a93847fdfd080dbefac
7
- data.tar.gz: a40243af1ddc3095562a3af781bcf73c2cb04691ce4cb2a090ca85e4987508b0673e8e89f0176364dbed765e0369ba3d4185b3eafcba52f8e4056d41e37da487
6
+ metadata.gz: b8cd88bcb2e24c647b76e54b5662811fa577980a039eb3ac4e9588f344231ffd863213a4e09a41e4d39e7ab19314ce8e40783b3faff5e78a586c3129c69d2f08
7
+ data.tar.gz: 690d495c1fe962fefae46cb73520e8690151ab40424cb2ebca0f162fe2b5bbcf1cac481be5b0ce73f408e4c03eaa676ae7284601e0b992e6b85c8e13449e1919
data/bin/ffi-yajl-bench CHANGED
@@ -33,4 +33,5 @@ if opts[:profile]
33
33
  FFI_Yajl::Benchmark::ParseProfileRubyProf.new().run()
34
34
  else
35
35
  FFI_Yajl::Benchmark::Parse.new().run()
36
+ FFI_Yajl::Benchmark::Encode.new().run()
36
37
  end
@@ -10,6 +10,7 @@ static VALUE mFFI_Yajl, mExt, mParser, cParseError;
10
10
 
11
11
  typedef struct {
12
12
  VALUE self;
13
+ int symbolizeKeys;
13
14
  } CTX;
14
15
 
15
16
  void set_value(CTX *ctx, VALUE val) {
@@ -74,7 +75,7 @@ int double_callback(void *ctx, double doubleVal) {
74
75
  }
75
76
 
76
77
  int number_callback(void *ctx, const char *numberVal, size_t numberLen) {
77
- char buf[numberLen+1];
78
+ char *buf = (char *)malloc(numberLen+1);
78
79
  buf[numberLen] = 0;
79
80
  memcpy(buf, numberVal, numberLen);
80
81
  if (memchr(buf, '.', numberLen) ||
@@ -84,21 +85,14 @@ int number_callback(void *ctx, const char *numberVal, size_t numberLen) {
84
85
  } else {
85
86
  set_value(ctx, rb_cstr2inum(buf, 10));
86
87
  }
88
+ free(buf);
87
89
  return 1;
88
90
  }
89
91
 
90
92
  int string_callback(void *ctx, const unsigned char *stringVal, size_t stringLen) {
91
- char buf[stringLen+1];
92
- VALUE str;
93
+ VALUE str = rb_str_new((const char *)stringVal, stringLen);
93
94
  #ifdef HAVE_RUBY_ENCODING_H
94
- rb_encoding *default_internal_enc;
95
- #endif
96
-
97
- buf[stringLen] = 0;
98
- memcpy(buf, stringVal, stringLen);
99
- str = rb_str_new2(buf);
100
- #ifdef HAVE_RUBY_ENCODING_H
101
- default_internal_enc = rb_default_internal_encoding();
95
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
102
96
  rb_enc_associate(str, utf8Encoding);
103
97
  if (default_internal_enc) {
104
98
  str = rb_str_export_to_enc(str, default_internal_enc);
@@ -114,23 +108,30 @@ int start_map_callback(void *ctx) {
114
108
  }
115
109
 
116
110
  int map_key_callback(void *ctx, const unsigned char *stringVal, size_t stringLen) {
117
- char buf[stringLen+1];
118
- VALUE str;
111
+ VALUE key;
119
112
  #ifdef HAVE_RUBY_ENCODING_H
120
113
  rb_encoding *default_internal_enc;
121
114
  #endif
122
115
 
123
- buf[stringLen] = 0;
124
- memcpy(buf, stringVal, stringLen);
125
- str = rb_str_new2(buf);
116
+ if ( ((CTX *)ctx)->symbolizeKeys ) {
126
117
  #ifdef HAVE_RUBY_ENCODING_H
127
- default_internal_enc = rb_default_internal_encoding();
128
- rb_enc_associate(str, utf8Encoding);
129
- if (default_internal_enc) {
130
- str = rb_str_export_to_enc(str, default_internal_enc);
131
- }
118
+ ID id = rb_intern3((const char *)stringVal, stringLen, utf8Encoding);
119
+ key = ID2SYM(id);
120
+ #else
121
+ VALUE str = rb_str_new((const char *)stringVal, stringLen);
122
+ key = rb_str_intern(str);
123
+ #endif
124
+ } else {
125
+ key = rb_str_new((const char *)stringVal, stringLen);
126
+ #ifdef HAVE_RUBY_ENCODING_H
127
+ default_internal_enc = rb_default_internal_encoding();
128
+ rb_enc_associate(key, utf8Encoding);
129
+ if (default_internal_enc) {
130
+ key = rb_str_export_to_enc(key, default_internal_enc);
131
+ }
132
132
  #endif
133
- set_key(ctx,str);
133
+ }
134
+ set_key(ctx, key);
134
135
  return 1;
135
136
  }
136
137
 
@@ -163,7 +164,15 @@ static yajl_callbacks callbacks = {
163
164
  end_array_callback,
164
165
  };
165
166
 
166
- static VALUE mParser_do_yajl_parse(VALUE self, VALUE str, VALUE opts) {
167
+ int get_opts_key(VALUE self, char *key) {
168
+ VALUE opts = rb_iv_get(self, "@opts");
169
+ if (TYPE(opts) != T_HASH) {
170
+ rb_raise(rb_eTypeError, "opts is not a valid hash");
171
+ }
172
+ return rb_hash_aref(opts, ID2SYM(rb_intern(key))) == Qtrue;
173
+ }
174
+
175
+ static VALUE mParser_do_yajl_parse(VALUE self, VALUE str, VALUE yajl_opts) {
167
176
  yajl_handle hand;
168
177
  yajl_status stat;
169
178
  unsigned char *err;
@@ -173,8 +182,26 @@ static VALUE mParser_do_yajl_parse(VALUE self, VALUE str, VALUE opts) {
173
182
  rb_ivar_set(self, rb_intern("key_stack"), rb_ary_new());
174
183
 
175
184
  ctx.self = self;
185
+ ctx.symbolizeKeys = get_opts_key(self, "symbolize_keys");
176
186
 
177
187
  hand = yajl_alloc(&callbacks, NULL, (void *)&ctx);
188
+
189
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_comments"))) == Qtrue) {
190
+ yajl_config(hand, yajl_allow_comments, 1);
191
+ }
192
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_dont_validate_strings"))) == Qtrue) {
193
+ yajl_config(hand, yajl_dont_validate_strings, 1);
194
+ }
195
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_trailing_garbage"))) == Qtrue) {
196
+ yajl_config(hand, yajl_allow_trailing_garbage, 1);
197
+ }
198
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_multiple_values"))) == Qtrue) {
199
+ yajl_config(hand, yajl_allow_multiple_values, 1);
200
+ }
201
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_partial_values"))) == Qtrue) {
202
+ yajl_config(hand, yajl_allow_partial_values, 1);
203
+ }
204
+
178
205
  if ((stat = yajl_parse(hand, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str))) != yajl_status_ok) {
179
206
  err = yajl_get_error(hand, 1, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str));
180
207
  goto raise;
@@ -101,6 +101,11 @@ module FFI_Yajl
101
101
  JSON.generate(hash)
102
102
  }
103
103
  }
104
+ x.report("JSON.fast_generate") {
105
+ times.times {
106
+ JSON.fast_generate(hash)
107
+ }
108
+ }
104
109
  end
105
110
  if defined?(Psych)
106
111
  x.report("Psych.to_json") {
@@ -120,13 +125,13 @@ module FFI_Yajl
120
125
  }
121
126
  end
122
127
  end
123
- if defined?(ActiveSupport::JSON)
124
- x.report("ActiveSupport::JSON.encode") {
125
- times.times {
126
- ActiveSupport::JSON.encode(hash)
127
- }
128
- }
129
- end
128
+ # if defined?(ActiveSupport::JSON)
129
+ # x.report("ActiveSupport::JSON.encode") {
130
+ # times.times {
131
+ # ActiveSupport::JSON.encode(hash)
132
+ # }
133
+ # }
134
+ # end
130
135
  }
131
136
  end
132
137
  end
@@ -28,6 +28,10 @@ begin
28
28
  require 'active_support'
29
29
  rescue LoadError
30
30
  end
31
+ begin
32
+ require 'oj'
33
+ rescue LoadError
34
+ end
31
35
 
32
36
  class FFI_Yajl::Benchmark::Parse
33
37
 
@@ -79,6 +83,15 @@ class FFI_Yajl::Benchmark::Parse
79
83
  }
80
84
  }
81
85
  end
86
+ if defined?(Oj)
87
+ x.report {
88
+ puts "Oj.load"
89
+ times.times {
90
+ json.rewind
91
+ Oj.load(json.read)
92
+ }
93
+ }
94
+ end
82
95
  if defined?(JSON)
83
96
  x.report {
84
97
  puts "JSON.parse"
data/lib/ffi_yajl/ext.rb CHANGED
@@ -24,6 +24,7 @@ module FFI_Yajl
24
24
  elsif defined?(DL) && DL.respond_to?(:dlopen)
25
25
  ::DL.dlopen(libpath)
26
26
  else
27
+ extend ::FFI::Library
27
28
  ffi_lib libpath
28
29
  end
29
30
 
data/lib/ffi_yajl/ffi.rb CHANGED
@@ -87,7 +87,7 @@ module FFI_Yajl
87
87
  attach_function :yajl_free_error, [:yajl_handle, :ustring], :void
88
88
 
89
89
  #
90
- attach_function :yajl_config, [:yajl_handle, :yajl_option, :int], :int
90
+ attach_function :yajl_config, [:yajl_handle, :yajl_option, :varargs], :int
91
91
 
92
92
  attach_function :yajl_gen_config, [:yajl_gen, :yajl_gen_option, :varargs], :int
93
93
 
@@ -51,7 +51,7 @@ module FFI_Yajl
51
51
  s = stringval.slice(0,stringlen)
52
52
  s.force_encoding('UTF-8') if defined? Encoding
53
53
  # XXX: I can't think of a better way to do this right now. need to call to_f if and only if its a float.
54
- v = ( s =~ /\./ ) ? s.to_f : s.to_i
54
+ v = ( s =~ /[\.eE]/ ) ? s.to_f : s.to_i
55
55
  set_value(v)
56
56
  1
57
57
  end
@@ -73,7 +73,7 @@ module FFI_Yajl
73
73
  @map_key_callback = ::FFI::Function.new(:int, [:pointer, :string, :size_t]) do |ctx, key, keylen|
74
74
  s = key.slice(0,keylen)
75
75
  s.force_encoding('UTF-8') if defined? Encoding
76
- self.key = s
76
+ self.key = @opts[:symbolize_keys] ? s.to_sym : s
77
77
  1
78
78
  end
79
79
  @end_map_callback = ::FFI::Function.new(:int, [:pointer]) do |ctx|
@@ -94,7 +94,7 @@ module FFI_Yajl
94
94
  end
95
95
 
96
96
 
97
- def do_yajl_parse(str, opts = {})
97
+ def do_yajl_parse(str, yajl_opts = {})
98
98
  setup_callbacks
99
99
  callback_ptr = ::FFI::MemoryPointer.new(::FFI_Yajl::YajlCallbacks)
100
100
  callbacks = ::FFI_Yajl::YajlCallbacks.new(callback_ptr)
@@ -110,6 +110,24 @@ module FFI_Yajl
110
110
  callbacks[:yajl_start_array] = @start_array_callback
111
111
  callbacks[:yajl_end_array] = @end_array_callback
112
112
  yajl_handle = ::FFI_Yajl.yajl_alloc(callback_ptr, nil, nil)
113
+
114
+ # configure the yajl parser
115
+ if yajl_opts[:yajl_allow_comments]
116
+ ::FFI_Yajl.yajl_config(yajl_handle, :yajl_allow_comments, :int, 1)
117
+ end
118
+ if yajl_opts[:yajl_dont_validate_strings]
119
+ ::FFI_Yajl.yajl_config(yajl_handle, :yajl_dont_validate_strings, :int, 1)
120
+ end
121
+ if yajl_opts[:yajl_allow_trailing_garbage]
122
+ ::FFI_Yajl.yajl_config(yajl_handle, :yajl_allow_trailing_garbage, :int, 1)
123
+ end
124
+ if yajl_opts[:yajl_allow_multiple_values]
125
+ ::FFI_Yajl.yajl_config(yajl_handle, :yajl_allow_multiple_values, :int, 1)
126
+ end
127
+ if yajl_opts[:yajl_allow_partial_values]
128
+ ::FFI_Yajl.yajl_config(yajl_handle, :yajl_allow_partial_values, :int, 1)
129
+ end
130
+
113
131
  if ( stat = ::FFI_Yajl.yajl_parse(yajl_handle, str, str.bytesize) != :yajl_status_ok )
114
132
  # FIXME: dup the error and call yajl_free_error?
115
133
  error = ::FFI_Yajl.yajl_get_error(yajl_handle, 1, str, str.bytesize)
@@ -37,8 +37,7 @@ module JSON
37
37
  end
38
38
 
39
39
  def self.default_options
40
- #@default_options ||= {:symbolize_keys => false}
41
- @default_options ||= {}
40
+ @default_options ||= {:symbolize_keys => false}
42
41
  end
43
42
 
44
43
  def self.parse(str, opts=JSON.default_options)
@@ -25,16 +25,35 @@ module FFI_Yajl
25
25
  end
26
26
 
27
27
  def initialize(opts = {})
28
- @opts = opts
28
+ @opts = opts ? opts.dup : {}
29
+ # JSON gem uses 'symbolize_names' and ruby-yajl supports this as well
30
+ @opts[:symbolize_keys] = true if @opts[:symbolize_names]
29
31
  end
30
32
 
31
33
  def parse(str)
32
34
  # initialization that we can do in pure ruby
33
35
  yajl_opts = {}
34
36
 
37
+ if @opts[:check_utf8] == false && @opts[:dont_validate_strings] == false
38
+ raise ArgumentError, "options check_utf8 and dont_validate_strings are both false which conflict"
39
+ end
40
+ if @opts[:check_utf8] == true && @opts[:dont_validate_strings] == true
41
+ raise ArgumentError, "options check_utf8 and dont_validate_strings are both true which conflict"
42
+ end
43
+
44
+ yajl_opts[:yajl_allow_comments] = @opts[:allow_comments]
45
+ yajl_opts[:yajl_dont_validate_strings] = (@opts[:check_utf8] == false || @opts[:dont_validate_strings])
46
+ yajl_opts[:yajl_allow_trailing_garbage] = @opts[:allow_trailing_garbage]
47
+ yajl_opts[:yajl_allow_multiple_values] = @opts[:allow_multiple_values]
48
+ yajl_opts[:yajl_allow_partial_values] = @opts[:allow_partial_values]
49
+
35
50
  # XXX: bug-compat with ruby-yajl
36
51
  return nil if str == ""
37
52
 
53
+ if str.respond_to?(:read)
54
+ str = str.read()
55
+ end
56
+
38
57
  # call either the ext or ffi hook
39
58
  do_yajl_parse(str, yajl_opts)
40
59
  end
@@ -1,3 +1,3 @@
1
1
  module FFI_Yajl
2
- VERSION = "0.1.7"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -11,7 +11,7 @@ describe "JSON Gem Compat API" do
11
11
 
12
12
  # Magic to make the before loading tests actually run before loading
13
13
  RSpec.configure do |config|
14
- config.order_groups_and_examples do |list|
14
+ config.register_ordering(:global) do |list|
15
15
  list.sort_by { |item| item.description }
16
16
  end
17
17
  end
@@ -19,39 +19,39 @@ describe "JSON Gem Compat API" do
19
19
  context "A: before loading the compat library" do
20
20
  it "should not mixin #to_json on random objects" do
21
21
  d = Dummy.new
22
- expect(d.respond_to?(:to_json)).to be_false
22
+ expect(d.respond_to?(:to_json)).to be false
23
23
  end
24
24
 
25
25
  it "should not mixin #to_json to a string" do
26
- expect("".respond_to?(:to_json)).to be_false
26
+ expect("".respond_to?(:to_json)).to be false
27
27
  end
28
28
 
29
29
  it "should not mixin #to_json to a fixnum" do
30
- expect(1.respond_to?(:to_json)).to be_false
30
+ expect(1.respond_to?(:to_json)).to be false
31
31
  end
32
32
 
33
33
  it "should not mixin #to_json on a float" do
34
- expect("1.5".to_f.respond_to?(:to_json)).to be_false
34
+ expect("1.5".to_f.respond_to?(:to_json)).to be false
35
35
  end
36
36
 
37
37
  it "should not mixin #to_json on an array" do
38
- expect([].respond_to?(:to_json)).to be_false
38
+ expect([].respond_to?(:to_json)).to be false
39
39
  end
40
40
 
41
41
  it "should not mixin #to_json on a hash" do
42
- expect({ :foo => "bar" }.respond_to?(:to_json)).to be_false
42
+ expect({ :foo => "bar" }.respond_to?(:to_json)).to be false
43
43
  end
44
44
 
45
45
  it "should not mixin #to_json on a trueclass" do
46
- expect(true.respond_to?(:to_json)).to be_false
46
+ expect(true.respond_to?(:to_json)).to be false
47
47
  end
48
48
 
49
49
  it "should not mixin #to_json on a falseclass" do
50
- expect(false.respond_to?(:to_json)).to be_false
50
+ expect(false.respond_to?(:to_json)).to be false
51
51
  end
52
52
 
53
53
  it "should not mixin #to_json on a nilclass" do
54
- expect(nil.respond_to?(:to_json)).to be_false
54
+ expect(nil.respond_to?(:to_json)).to be false
55
55
  end
56
56
  end
57
57
 
@@ -62,66 +62,60 @@ describe "JSON Gem Compat API" do
62
62
  end
63
63
 
64
64
  it "should define JSON class" do
65
- expect(defined?(JSON)).to be_true
65
+ expect(defined?(JSON)).to be_truthy
66
66
  end
67
67
 
68
68
  it "should implement JSON#parse" do
69
- expect(JSON.respond_to?(:parse)).to be_true
69
+ expect(JSON.respond_to?(:parse)).to be true
70
70
  end
71
71
 
72
72
  it "should implement JSON#generate" do
73
- expect(JSON.respond_to?(:generate)).to be_true
73
+ expect(JSON.respond_to?(:generate)).to be true
74
74
  end
75
75
 
76
76
  it "should implement JSON#pretty_generate" do
77
- expect(JSON.respond_to?(:pretty_generate)).to be_true
77
+ expect(JSON.respond_to?(:pretty_generate)).to be true
78
78
  end
79
79
 
80
80
  it "should implement JSON#load" do
81
- expect(JSON.respond_to?(:load)).to be_true
81
+ expect(JSON.respond_to?(:load)).to be true
82
82
  end
83
83
 
84
84
  it "should implement JSON#dump" do
85
- expect(JSON.respond_to?(:dump)).to be_true
85
+ expect(JSON.respond_to?(:dump)).to be true
86
86
  end
87
87
 
88
88
  context "when setting symbolize_keys via JSON.default_options" do
89
- before(:all) { @saved_default = JSON.default_options[:symbolize_keys] }
90
- after(:all) { JSON.default_options[:symbolize_keys] = @saved_default }
89
+ after(:each) { JSON.default_options[:symbolize_keys] = false }
91
90
 
92
91
  it "the default behavior should be to not symbolize keys" do
93
92
  expect(JSON.parse('{"foo": 1234}')).to eq( "foo" => 1234 )
94
93
  end
95
94
 
96
95
  it "changing the default_options should change the behavior to true" do
97
- skip("implement symbolize keys")
98
96
  JSON.default_options[:symbolize_keys] = true
99
97
  expect(JSON.parse('{"foo": 1234}')).to eq( :foo => 1234 )
100
98
  end
101
99
  end
102
100
 
103
101
  context "when setting symbolize_names via JSON.default_options" do
104
- before(:all) { @saved_default = JSON.default_options[:symbolize_names] }
105
- after(:all) { JSON.default_options[:symbolize_names] = @saved_default }
102
+ after { JSON.default_options.delete(:symbolize_names)}
106
103
 
107
104
  it "the default behavior should be to not symbolize keys" do
108
105
  expect(JSON.parse('{"foo": 1234}')).to eq( "foo" => 1234 )
109
106
  end
110
107
 
111
108
  it "changing the default_options should change the behavior to true" do
112
- skip("implement symbolize keys")
113
109
  JSON.default_options[:symbolize_names] = true
114
110
  expect(JSON.parse('{"foo": 1234}')).to eq( :foo => 1234 )
115
111
  end
116
112
  end
117
113
 
118
114
  it "should support passing symbolize_names to JSON.parse" do
119
- skip("implement symbolize keys")
120
115
  expect(JSON.parse('{"foo": 1234}', :symbolize_names => true)).to eq( :foo => 1234 )
121
116
  end
122
117
 
123
118
  it "should support passing symbolize_keys to JSON.parse" do
124
- skip("implement symbolize keys")
125
119
  expect(JSON.parse('{"foo": 1234}', :symbolize_keys => true)).to eq( :foo => 1234 )
126
120
  end
127
121
 
@@ -149,13 +143,13 @@ describe "JSON Gem Compat API" do
149
143
 
150
144
  context "JSON exception classes" do
151
145
  it "should define JSON::JSONError as a StandardError" do
152
- expect(JSON::JSONError.new.is_a?(StandardError)).to be_true
146
+ expect(JSON::JSONError.new.is_a?(StandardError)).to be true
153
147
  end
154
148
  it "should define JSON::ParserError as a JSON::JSONError" do
155
- expect(JSON::ParserError.new.is_a?(JSON::JSONError)).to be_true
149
+ expect(JSON::ParserError.new.is_a?(JSON::JSONError)).to be true
156
150
  end
157
151
  it "should define JSON::GeneratorError as a JSON::JSONError" do
158
- expect(JSON::GeneratorError.new.is_a?(JSON::JSONError)).to be_true
152
+ expect(JSON::GeneratorError.new.is_a?(JSON::JSONError)).to be true
159
153
  end
160
154
  it "should raise JSON::ParserError on a bad parse" do
161
155
  expect{ JSON.parse("blah") }.to raise_error(JSON::ParserError)
@@ -4,79 +4,473 @@ require 'spec_helper'
4
4
 
5
5
  describe "FFI_Yajl::Parser" do
6
6
 
7
- let(:parser) { FFI_Yajl::Parser.new }
7
+ shared_examples_for "correct json parsing" do
8
+ context "when json has 23456789012E666" do
9
+ let(:json) { '{"key": 23456789012E666}' }
8
10
 
9
- it "returns nil for an empty string (compatibility with yajl-ruby)" do
10
- json = ''
11
- expect( parser.parse(json) ).to be_nil
12
- end
11
+ it "should return infinity" do
12
+ infinity = (1.0/0)
13
+ expect(parser).to eq({"key" => infinity})
14
+ end
15
+ end
13
16
 
14
- it "throws an exception when trailing braces are missing" do
15
- json = '{{"foo": 1234}'
16
- expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError)
17
- end
17
+ context "when json has comments" do
18
+ let(:json) { '{"key": /* this is a comment */ "value"}' }
18
19
 
19
- it "throws an exception when trailing brackets are missing" do
20
- json = '[["foo", "bar"]'
21
- expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError)
22
- end
20
+ context "when allow_comments is false" do
21
+ let(:options) { { :allow_comments => false } }
23
22
 
24
- it "throws an exception when it has an extra brace" do
25
- json = '{{"foo": 1234}}}'
26
- expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError)
27
- end
23
+ it "should not parse" do
24
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
25
+ end
26
+ end
28
27
 
29
- it "throws an exception when it has an extra bracket" do
30
- json = '[["foo", "bar"]]]'
31
- expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError)
32
- end
28
+ context "when allow_comments is true" do
29
+ let(:options) { { :allow_comments => true } }
30
+
31
+ it "should parse" do
32
+ expect(parser).to eq({"key"=>"value"})
33
+ end
34
+ end
35
+ end
36
+
37
+ context "when json has multiline comments" do
38
+ let(:json) { %Q{{"key": \n/*\n this is a multiline comment \n*/\n "value"}} }
39
+
40
+ context "when allow_comments is false" do
41
+ let(:options) { { :allow_comments => false } }
42
+
43
+ it "should not parse" do
44
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
45
+ end
46
+ end
47
+
48
+ context "when allow_comments is true" do
49
+ let(:options) { { :allow_comments => true } }
50
+
51
+ it "should parse" do
52
+ expect(parser).to eq({"key"=>"value"})
53
+ end
54
+ end
55
+ end
56
+
57
+ context "when json has inline comments" do
58
+ let(:json) { %Q{{"key": \n// this is an inline comment\n "value"}} }
59
+
60
+ context "when allow_comments is false" do
61
+ let(:options) { { :allow_comments => false } }
62
+
63
+ it "should not parse" do
64
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
65
+ end
66
+ end
67
+
68
+ context "when allow_comments is true" do
69
+ let(:options) { { :allow_comments => true } }
70
+
71
+ it "should parse" do
72
+ expect(parser).to eq({"key"=>"value"})
73
+ end
74
+ end
75
+ end
76
+
77
+ context "when json is invalid UTF8" do
78
+ let(:json) { "[\"#{"\201\203"}\"]" }
79
+
80
+ it "should not parse by default" do
81
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
82
+ end
83
+
84
+ context "when :dont_validate_strings is set to true" do
85
+ let(:options) { { :dont_validate_strings => true } }
86
+
87
+ it "should parse" do
88
+ expect(parser).to eq(["\x81\x83"])
89
+ end
90
+ end
91
+
92
+ context "when :dont_validate_strings is set to false" do
93
+ let(:options) { { :dont_validate_strings => false } }
94
+
95
+ it "should not parse" do
96
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
97
+ end
98
+ end
99
+
100
+ context "when :check_utf8 is set to true" do
101
+ let(:options) { { :check_utf8 => true } }
102
+
103
+ it "should not parse" do
104
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
105
+ end
106
+
107
+ context "when :dont_validate_strings is set to true" do
108
+ let(:options) { { :check_utf8 => true, :dont_validate_strings => true } }
109
+
110
+ it "should raise an ArgumentError" do
111
+ expect{parser}.to raise_error(ArgumentError)
112
+ end
113
+ end
114
+
115
+ context "when :dont_validate_strings is set to false" do
116
+ let(:options) { { :check_utf8 => true, :dont_validate_strings => false } }
117
+
118
+ it "should not parse" do
119
+ expect{parser}.to raise_error(FFI_Yajl::ParseError)
120
+ end
121
+ end
122
+ end
123
+
124
+ context "when :check_utf8 is set to false" do
125
+ let(:options) { { :check_utf8 => false } }
126
+
127
+ it "should parse" do
128
+ expect(parser).to eq(["\x81\x83"])
129
+ end
130
+
131
+ context "when :dont_validate_strings is set to true" do
132
+ let(:options) { { :check_utf8 => false, :dont_validate_strings => true } }
133
+
134
+ it "should parse" do
135
+ expect(parser).to eq(["\x81\x83"])
136
+ end
137
+ end
138
+
139
+ context "when :dont_validate_strings is set to false" do
140
+ let(:options) { { :check_utf8 => false, :dont_validate_strings => false } }
141
+
142
+ it "should raise an ArgumentError" do
143
+ expect{parser}.to raise_error(ArgumentError)
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ context "when JSON is a StringIO" do
150
+ let(:json) { StringIO.new('{"key": 1234}') }
151
+
152
+ it "should parse" do
153
+ expect(parser).to eq({"key" => 1234})
154
+ end
155
+ end
156
+
157
+ context "when parsing a JSON string" do
158
+ let(:json) { '{"key": 1234}' }
159
+
160
+ it "should parse correctly" do
161
+ expect(parser).to eq({"key" => 1234})
162
+ end
163
+
164
+ context "when symbolize_keys is true" do
165
+ let(:options) { { :symbolize_keys => true } }
166
+
167
+ it "should symbolize keys correctly" do
168
+ expect(parser).to eq({:key => 1234})
169
+ end
170
+ end
171
+
172
+ context "when passing a block" do
173
+ it "should parse correctly" do
174
+ skip "handle blocks"
175
+ output = nil
176
+ parser do |obj|
177
+ output = obj
178
+ end
179
+ expect(output).to eq({"key" => 1234})
180
+ end
181
+ end
182
+ end
183
+
184
+ context "when parsing a JSON hash with only strings" do
185
+ let(:json) { '{"key": "value"}' }
186
+
187
+ if RUBY_VERSION.to_f >= 1.9
188
+ context "when Encoding.default_internal is nil" do
189
+ before do
190
+ @saved_encoding = Encoding.default_internal
191
+ Encoding.default_internal = nil
192
+ end
193
+ after do
194
+ Encoding.default_internal = @saved_encoding
195
+ end
196
+ it "encodes keys to UTF-8" do
197
+ expect(parser.keys.first.encoding).to eql(Encoding.find('utf-8'))
198
+ end
199
+ it "encodes values to UTF-8" do
200
+ expect(parser.values.first.encoding).to eql(Encoding.find('utf-8'))
201
+ end
202
+ end
203
+
204
+ %w{utf-8 us-ascii}.each do |encoding|
205
+ context "when Encoding.default_internal is #{encoding}" do
206
+ before do
207
+ @saved_encoding = Encoding.default_internal
208
+ Encoding.default_internal = nil
209
+ end
210
+ after do
211
+ Encoding.default_internal = @saved_encoding
212
+ end
213
+ it "encodes keys to #{encoding}" do
214
+ skip "fix us-ascii" if encoding == "us-ascii"
215
+ expect(parser.keys.first.encoding).to eql(Encoding.find(encoding))
216
+ end
217
+ it "encodes values to #{encoding}" do
218
+ skip "fix us-ascii" if encoding == "us-ascii"
219
+ expect(parser.values.first.encoding).to eql(Encoding.find(encoding))
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ context "when a parsed key has utf-8 multibyte characters" do
227
+ let(:json) { '{"日本語": 1234}' }
228
+
229
+ it "should parse correctly" do
230
+ expect(parser).to eq({"日本語" => 1234})
231
+ end
232
+
233
+ context "when symbolize_keys is true" do
234
+ let(:options) { { :symbolize_keys => true } }
235
+
236
+ it "should symbolize keys correctly" do
237
+ expect(parser).to eq({:"日本語" => 1234})
238
+ end
33
239
 
34
- context "when parsing floats" do
35
- it "parses simple floating point values" do
36
- json = '{"foo": 3.14159265358979}'
37
- expect(parser.parse(json)).to eql( "foo" => 3.14159265358979 )
240
+ if RUBY_VERSION.to_f >= 1.9
241
+ it "should parse non-ascii symbols in UTF-8" do
242
+ expect(parser.keys.fetch(0).encoding).to eq(Encoding::UTF_8)
243
+ end
244
+ end
245
+ end
38
246
  end
39
247
 
40
- it "parses simple negative floating point values" do
41
- json = '{"foo":-2.00231930436153}'
42
- expect(parser.parse(json)).to eql( "foo" => -2.00231930436153 )
248
+ context "when parsing 2147483649" do
249
+ let(:json) { "{\"id\": 2147483649}" }
250
+
251
+ it "should parse corectly" do
252
+ expect(parser).to eql({"id" => 2147483649})
253
+ end
254
+ end
255
+
256
+ context "when parsing 5687389800" do
257
+ let(:json) { "{\"id\": 5687389800}" }
258
+
259
+ it "should parse corectly" do
260
+ expect(parser).to eql({"id" => 5687389800})
261
+ end
262
+ end
263
+
264
+ context "when parsing 1046289770033519442869495707521600000000" do
265
+ let(:json) { "{\"id\": 1046289770033519442869495707521600000000}" }
266
+
267
+ it "should parse corectly" do
268
+ expect(parser).to eql({"id" => 1046289770033519442869495707521600000000})
269
+ end
270
+ end
271
+
272
+ # NOTE: we are choosing to be compatible with yajl-ruby here vs. JSON
273
+ # gem and libyajl C behavior (which is to throw an exception in this case)
274
+ context "when the JSON is empty string" do
275
+ let(:json) { '' }
276
+
277
+ it "returns nil" do
278
+ expect(parser).to be_nil
279
+ end
280
+ end
281
+
282
+ # NOTE: this fixes yajl-ruby being too permissive
283
+ context "when dealing with too much or too little input" do
284
+ context "when trailing braces are missing" do
285
+ let(:json) { '{"foo":{"foo": 1234}' }
286
+
287
+ it "raises an exception" do
288
+ expect { parser }.to raise_error(FFI_Yajl::ParseError)
289
+ end
290
+ end
291
+
292
+ context "when trailing brackets are missing" do
293
+ let(:json) { '[["foo", "bar"]' }
294
+
295
+ it "raises an exception" do
296
+ expect { parser }.to raise_error(FFI_Yajl::ParseError)
297
+ end
298
+ end
299
+
300
+ context "when an extra brace is present" do
301
+ let(:json) { '{"foo":{"foo": 1234}}}' }
302
+
303
+ it "raises an exception" do
304
+ expect { parser }.to raise_error(FFI_Yajl::ParseError)
305
+ end
306
+
307
+ context "with allow_trailing_garbage" do
308
+ let(:options) { { :allow_trailing_garbage => true } }
309
+ it "parses" do
310
+ expect(parser).to eq({"foo"=>{"foo"=>1234}})
311
+ end
312
+ end
313
+
314
+ end
315
+
316
+ context "when an extra bracket is present" do
317
+ let(:json) { '[["foo", "bar"]]]' }
318
+
319
+ it "raises an exception" do
320
+ expect { parser }.to raise_error(FFI_Yajl::ParseError)
321
+ end
322
+ end
323
+ end
324
+
325
+ context "when parsing heavy metal umlauts in keys" do
326
+ let(:json) { '{"München": "Bayern"}' }
327
+
328
+ it "correctly parses" do
329
+ expect(parser).to eql( "München" => "Bayern" )
330
+ end
331
+ end
332
+
333
+ context "when parsing floats" do
334
+ context "parses simple floating point values" do
335
+ let(:json) { '{"foo": 3.14159265358979}' }
336
+
337
+ it "correctly parses" do
338
+ expect(parser).to eql( "foo" => 3.14159265358979 )
339
+ end
340
+ end
341
+
342
+ context "parses simple negative floating point values" do
343
+ let(:json) { '{"foo":-2.00231930436153}' }
344
+
345
+ it "correctly parses" do
346
+ expect(parser).to eql( "foo" => -2.00231930436153 )
347
+ end
348
+ end
349
+
350
+ context "parses floats with negative exponents and a large E" do
351
+ let(:json) { '{"foo": 1.602176565E-19}' }
352
+
353
+ it "correctly parses" do
354
+ expect(parser).to eql( "foo" => 1.602176565e-19 )
355
+ end
356
+ end
357
+
358
+ context "parses floats with negative exponents and a small e" do
359
+ let(:json) { '{"foo": 6.6260689633e-34 }' }
360
+
361
+ it "correctly parses" do
362
+ expect(parser).to eql( "foo" => 6.6260689633e-34 )
363
+ end
364
+ end
365
+
366
+ context "parses floats with positive exponents and a large E" do
367
+ let(:json) { '{"foo": 6.0221413E+23}' }
368
+
369
+ it "correctly parses" do
370
+ expect(parser).to eql( "foo" => 6.0221413e+23 )
371
+ end
372
+ end
373
+
374
+ context "parses floats with positive exponents and a small e" do
375
+ let(:json) { '{"foo": 8.9875517873681764e+9 }' }
376
+
377
+ it "correctly parses" do
378
+ expect(parser).to eql( "foo" => 8.9875517873681764e+9 )
379
+ end
380
+ end
381
+
382
+ context "parses floats with an exponent without a sign and a large E" do
383
+ let(:json) { '{"foo": 2.99792458E8 }' }
384
+
385
+ it "correctly parses" do
386
+ expect(parser).to eql( "foo" => 2.99792458e+8 )
387
+ end
388
+ end
389
+
390
+ context "parses floats with an exponent without a sign and a small e" do
391
+ let(:json) { '{"foo": 1.0973731568539e7 }' }
392
+
393
+ it "correctly parses" do
394
+ expect(parser).to eql( "foo" => 1.0973731568539e+7 )
395
+ end
396
+ end
43
397
  end
44
398
 
45
- it "parses floats with negative exponents and a large E" do
46
- json = '{"foo": 1.602176565E-19}'
47
- expect(parser.parse(json)).to eql( "foo" => 1.602176565e-19 )
399
+ context "when parsing big floats", :ruby_gte_193 => true do
400
+ let(:json) { '[0.' + '1' * 2**23 + ']' }
401
+
402
+ it "parses" do
403
+ expect{ parser }.not_to raise_error
404
+ end
48
405
  end
49
406
 
50
- it "parses floats with negative exponents and a small e" do
51
- json = '{"foo": 6.6260689633e-34 }'
52
- expect(parser.parse(json)).to eql( "foo" => 6.6260689633e-34 )
407
+ context "when parsing long hash keys with symbolize_keys option", :ruby_gte_193 => true do
408
+ let(:json) { '{"' + 'a' * 2**23 + '": 0}' }
409
+ let(:options) { { :symbolize_keys => true } }
410
+
411
+ it "parses" do
412
+ expect{ parser }.not_to raise_error
413
+ end
53
414
  end
415
+ end
416
+
417
+ context "when options are set to empty hash" do
418
+ let(:options) { {} }
419
+
420
+ context "when using a parsing object" do
421
+ let(:parser) { FFI_Yajl::Parser.new(options).parse(json) }
54
422
 
55
- it "parses floats with positive exponents and a large E" do
56
- json = '{"foo": 6.0221413E+23}'
57
- expect(parser.parse(json)).to eql( "foo" => 6.0221413e+23 )
423
+ it_behaves_like "correct json parsing"
58
424
  end
59
425
 
60
- it "parses floats with positive exponents and a small e" do
61
- json = '{"foo": 8.9875517873681764e+9 }'
62
- expect(parser.parse(json)).to eql( "foo" => 8.9875517873681764e+9 )
426
+ context "when using the class method" do
427
+ let(:parser) { FFI_Yajl::Parser.parse(json, options) }
428
+
429
+ it_behaves_like "correct json parsing"
63
430
  end
431
+ end
432
+
433
+ context "when options are set to nil" do
434
+ let(:options) { nil }
64
435
 
65
- it "parses floats with an exponent without a sign and a large E" do
66
- json = '{"foo": 2.99792458E8 }'
67
- expect(parser.parse(json)).to eql( "foo" => 2.99792458e+8 )
436
+ context "when using a parsing object" do
437
+ let(:parser) { FFI_Yajl::Parser.new(options).parse(json) }
438
+
439
+ it_behaves_like "correct json parsing"
68
440
  end
69
441
 
70
- it "parses floats with an exponent without a sign and a small e" do
71
- json = '{"foo": 1.0973731568539e7 }'
72
- expect(parser.parse(json)).to eql( "foo" => 1.0973731568539e+7 )
442
+ context "when using the class method" do
443
+ let(:parser) { FFI_Yajl::Parser.parse(json, options) }
444
+
445
+ it_behaves_like "correct json parsing"
73
446
  end
74
447
  end
75
448
 
76
- context "when parsing unicode in hash keys" do
77
- it "handles heavy metal umlauts in keys" do
78
- json = '{"München": "Bayern"}'
79
- expect(parser.parse(json)).to eql( "München" => "Bayern" )
449
+ context "when options default to nothing" do
450
+ let(:options) { nil }
451
+
452
+ context "when using a parsing object" do
453
+ let(:parser) do
454
+ if options.nil?
455
+ FFI_Yajl::Parser.new.parse(json)
456
+ else
457
+ FFI_Yajl::Parser.new(options).parse(json)
458
+ end
459
+ end
460
+
461
+ it_behaves_like "correct json parsing"
462
+ end
463
+
464
+ context "when using the class method" do
465
+ let(:parser) do
466
+ if options.nil?
467
+ FFI_Yajl::Parser.parse(json)
468
+ else
469
+ FFI_Yajl::Parser.parse(json, options)
470
+ end
471
+ end
472
+
473
+ it_behaves_like "correct json parsing"
80
474
  end
81
475
  end
82
476
  end
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,7 @@ require 'ffi_yajl'
4
4
 
5
5
  RSpec.configure do |c|
6
6
  c.filter_run_excluding :ruby_gte_19 => true unless RUBY_VERSION.to_f >= 1.9
7
+ c.filter_run_excluding :ruby_gte_193 => true unless RUBY_VERSION.to_f >= 2.0 || RUBY_VERSION =~ /^1\.9\.3/
7
8
 
8
9
  c.order = 'random'
9
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-yajl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: universal-java
6
6
  authors:
7
7
  - Lamont Granquist
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-14 00:00:00.000000000 Z
11
+ date: 2014-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake