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 +4 -4
- data/bin/ffi-yajl-bench +1 -0
- data/ext/ffi_yajl/ext/parser/parser.c +50 -23
- data/lib/ffi_yajl/benchmark/encode.rb +12 -7
- data/lib/ffi_yajl/benchmark/parse.rb +13 -0
- data/lib/ffi_yajl/ext.rb +1 -0
- data/lib/ffi_yajl/ffi.rb +1 -1
- data/lib/ffi_yajl/ffi/parser.rb +21 -3
- data/lib/ffi_yajl/json_gem.rb +1 -2
- data/lib/ffi_yajl/parser.rb +20 -1
- data/lib/ffi_yajl/version.rb +1 -1
- data/spec/ffi_yajl/json_gem_spec.rb +21 -27
- data/spec/ffi_yajl/parser_spec.rb +444 -50
- data/spec/spec_helper.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07c450eaba7a2fb80f921d896a2fa36bbfca3a09
|
4
|
+
data.tar.gz: 1368094d6809554303390c8dc38e33b837d6da5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8cd88bcb2e24c647b76e54b5662811fa577980a039eb3ac4e9588f344231ffd863213a4e09a41e4d39e7ab19314ce8e40783b3faff5e78a586c3129c69d2f08
|
7
|
+
data.tar.gz: 690d495c1fe962fefae46cb73520e8690151ab40424cb2ebca0f162fe2b5bbcf1cac481be5b0ce73f408e4c03eaa676ae7284601e0b992e6b85c8e13449e1919
|
data/bin/ffi-yajl-bench
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
-
|
124
|
-
memcpy(buf, stringVal, stringLen);
|
125
|
-
str = rb_str_new2(buf);
|
116
|
+
if ( ((CTX *)ctx)->symbolizeKeys ) {
|
126
117
|
#ifdef HAVE_RUBY_ENCODING_H
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
str =
|
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
|
-
|
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
|
-
|
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
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, :
|
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
|
|
data/lib/ffi_yajl/ffi/parser.rb
CHANGED
@@ -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 =~
|
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,
|
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)
|
data/lib/ffi_yajl/json_gem.rb
CHANGED
data/lib/ffi_yajl/parser.rb
CHANGED
@@ -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
|
data/lib/ffi_yajl/version.rb
CHANGED
@@ -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.
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
7
|
+
shared_examples_for "correct json parsing" do
|
8
|
+
context "when json has 23456789012E666" do
|
9
|
+
let(:json) { '{"key": 23456789012E666}' }
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
it "should not parse" do
|
24
|
+
expect{parser}.to raise_error(FFI_Yajl::ParseError)
|
25
|
+
end
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
41
|
-
json
|
42
|
-
|
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
|
-
|
46
|
-
json
|
47
|
-
|
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
|
-
|
51
|
-
json
|
52
|
-
|
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
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
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
|
77
|
-
|
78
|
-
|
79
|
-
|
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
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.
|
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-
|
11
|
+
date: 2014-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|