msgpack 0.5.10-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+
3
+ if RUBY_PLATFORM.include?('java')
4
+ # JRuby should use this library, MRI should use the standard gem
5
+ $: << File.expand_path('../../../lib', __FILE__)
6
+ end
7
+
8
+ require 'viiite'
9
+ require 'msgpack'
10
+ require 'json'
11
+ require 'bson'
12
+
13
+ if RUBY_PLATFORM.include?('java')
14
+ BSON_IMPL = BSON::BSON_JAVA
15
+ else
16
+ BSON_IMPL = BSON::BSON_C
17
+ end
18
+
19
+ OBJECT_STRUCTURE = {'x' => ['y', 34, 2**30 + 3, 2.1223423423356, {'hello' => 'world', '5' => [63, 'asjdl']}]}
20
+ ENCODED_MSGPACK = "\x81\xA1x\x95\xA1y\"\xCE@\x00\x00\x03\xCB@\x00\xFA\x8E\x9F9\xFA\xC1\x82\xA5hello\xA5world\xA15\x92?\xA5asjdl"
21
+ ENCODED_BSON = "d\x00\x00\x00\x04x\x00\\\x00\x00\x00\x020\x00\x02\x00\x00\x00y\x00\x101\x00\"\x00\x00\x00\x102\x00\x03\x00\x00@\x013\x00\xC1\xFA9\x9F\x8E\xFA\x00@\x034\x002\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x045\x00\x19\x00\x00\x00\x100\x00?\x00\x00\x00\x021\x00\x06\x00\x00\x00asjdl\x00\x00\x00\x00\x00"
22
+ ENCODED_JSON = '{"x":["y",34,1073741827,2.1223423423356,{"hello":"world","5":[63,"asjdl"]}]}'
23
+ ITERATIONS = 1_00_000
24
+ IMPLEMENTATIONS = ENV.fetch('IMPLEMENTATIONS', 'json,bson,msgpack').split(',').map(&:to_sym)
25
+
26
+ Viiite.bm do |b|
27
+ b.variation_point :ruby, Viiite.which_ruby
28
+
29
+ IMPLEMENTATIONS.each do |lib|
30
+ b.variation_point :lib, lib
31
+
32
+
33
+ b.report(:pack) do
34
+ ITERATIONS.times do
35
+ case lib
36
+ when :msgpack then MessagePack.pack(OBJECT_STRUCTURE)
37
+ when :bson then BSON_IMPL.serialize(OBJECT_STRUCTURE).to_s
38
+ when :json then OBJECT_STRUCTURE.to_json
39
+ end
40
+ end
41
+ end
42
+
43
+ b.report(:unpack) do
44
+ ITERATIONS.times do
45
+ case lib
46
+ when :msgpack then MessagePack.unpack(ENCODED_MSGPACK)
47
+ when :bson then BSON_IMPL.deserialize(ENCODED_BSON)
48
+ when :json then JSON.parse(ENCODED_JSON)
49
+ end
50
+ end
51
+ end
52
+
53
+ b.report(:pack_unpack) do
54
+ ITERATIONS.times do
55
+ case lib
56
+ when :msgpack then MessagePack.unpack(MessagePack.pack(OBJECT_STRUCTURE))
57
+ when :bson then BSON_IMPL.deserialize(BSON_IMPL.serialize(OBJECT_STRUCTURE).to_s)
58
+ when :json then JSON.parse(OBJECT_STRUCTURE.to_json)
59
+ end
60
+ end
61
+ end
62
+
63
+ b.report(:unpack_pack) do
64
+ ITERATIONS.times do
65
+ case lib
66
+ when :msgpack then MessagePack.pack(MessagePack.unpack(ENCODED_MSGPACK))
67
+ when :bson then BSON_IMPL.serialize(BSON_IMPL.deserialize(ENCODED_BSON)).to_s
68
+ when :json then OBJECT_STRUCTURE.to_json(JSON.parse(ENCODED_JSON))
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../../lib', __FILE__)
4
+
5
+ require 'viiite'
6
+ require 'msgpack'
7
+
8
+
9
+ iterations = 10_000
10
+ data = MessagePack.pack(:hello => 'world', :nested => ['structure', {:value => 42}])
11
+
12
+ Viiite.bm do |b|
13
+ b.report(:strings) do
14
+ iterations.times do
15
+ MessagePack.unpack(data)
16
+ end
17
+ end
18
+
19
+ b.report(:symbols) do
20
+ options = {:symbolize_keys => true}
21
+ iterations.times do
22
+ MessagePack.unpack(data, options)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,290 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'stringio'
4
+ require 'tempfile'
5
+ require 'spec_helper'
6
+
7
+
8
+ describe ::MessagePack::Unpacker do
9
+ def flatten(struct, results = [])
10
+ case struct
11
+ when Array
12
+ struct.each { |v| flatten(v, results) }
13
+ when Hash
14
+ struct.each { |k, v| flatten(v, flatten(k, results)) }
15
+ else
16
+ results << struct
17
+ end
18
+ results
19
+ end
20
+
21
+ subject do
22
+ described_class.new
23
+ end
24
+
25
+ let :buffer1 do
26
+ MessagePack.pack(:foo => 'bar')
27
+ end
28
+
29
+ let :buffer2 do
30
+ MessagePack.pack(:hello => {:world => [1, 2, 3]})
31
+ end
32
+
33
+ let :buffer3 do
34
+ MessagePack.pack(:x => 'y')
35
+ end
36
+
37
+ describe '#execute/#execute_limit/#finished?' do
38
+ let :buffer do
39
+ buffer1 + buffer2 + buffer3
40
+ end
41
+
42
+ it 'extracts an object from the buffer' do
43
+ subject.execute(buffer, 0)
44
+ subject.data.should == {'foo' => 'bar'}
45
+ end
46
+
47
+ it 'extracts an object from the buffer, starting at an offset' do
48
+ subject.execute(buffer, buffer1.length)
49
+ subject.data.should == {'hello' => {'world' => [1, 2, 3]}}
50
+ end
51
+
52
+ it 'extracts an object from the buffer, starting at an offset reading bytes up to a limit' do
53
+ subject.execute_limit(buffer, buffer1.length, buffer2.length)
54
+ subject.data.should == {'hello' => {'world' => [1, 2, 3]}}
55
+ end
56
+
57
+ it 'extracts nothing if the limit cuts an object in half' do
58
+ subject.execute_limit(buffer, buffer1.length, 3)
59
+ subject.data.should be_nil
60
+ end
61
+
62
+ it 'returns the offset where the object ended' do
63
+ subject.execute(buffer, 0).should == buffer1.length
64
+ subject.execute(buffer, buffer1.length).should == buffer1.length + buffer2.length
65
+ end
66
+
67
+ it 'is finished if #data returns an object' do
68
+ subject.execute_limit(buffer, buffer1.length, buffer2.length)
69
+ subject.should be_finished
70
+
71
+ subject.execute_limit(buffer, buffer1.length, 3)
72
+ subject.should_not be_finished
73
+ end
74
+ end
75
+
76
+ describe '#each' do
77
+ context 'with a buffer' do
78
+ it 'yields each object in the buffer' do
79
+ objects = []
80
+ subject.feed(buffer1)
81
+ subject.feed(buffer2)
82
+ subject.feed(buffer3)
83
+ subject.each do |obj|
84
+ objects << obj
85
+ end
86
+ objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
87
+ end
88
+
89
+ it 'returns an enumerator when no block is given' do
90
+ subject.feed(buffer1)
91
+ subject.feed(buffer2)
92
+ subject.feed(buffer3)
93
+ enum = subject.each
94
+ enum.map { |obj| obj.keys.first }.should == %w[foo hello x]
95
+ end
96
+ end
97
+
98
+ context 'with a StringIO stream' do
99
+ it 'yields each object in the stream' do
100
+ objects = []
101
+ subject.stream = StringIO.new(buffer1 + buffer2 + buffer3)
102
+ subject.each do |obj|
103
+ objects << obj
104
+ end
105
+ objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
106
+ end
107
+ end
108
+
109
+ context 'with a File stream' do
110
+ it 'yields each object in the stream' do
111
+ objects = []
112
+ file = Tempfile.new('msgpack')
113
+ file.write(buffer1)
114
+ file.write(buffer2)
115
+ file.write(buffer3)
116
+ file.open
117
+ subject.stream = file
118
+ subject.each do |obj|
119
+ objects << obj
120
+ end
121
+ objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
122
+ end
123
+ end
124
+
125
+ context 'with a stream passed to the constructor' do
126
+ it 'yields each object in the stream' do
127
+ objects = []
128
+ unpacker = described_class.new(StringIO.new(buffer1 + buffer2 + buffer3))
129
+ unpacker.each do |obj|
130
+ objects << obj
131
+ end
132
+ objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
133
+ end
134
+ end
135
+ end
136
+
137
+ describe '#feed_each' do
138
+ it 'feeds the buffer then runs #each' do
139
+ objects = []
140
+ subject.feed_each(buffer1 + buffer2 + buffer3) do |obj|
141
+ objects << obj
142
+ end
143
+ objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
144
+ end
145
+
146
+ it 'handles chunked data' do
147
+ objects = []
148
+ buffer = buffer1 + buffer2 + buffer3
149
+ buffer.chars.each do |ch|
150
+ subject.feed_each(ch) do |obj|
151
+ objects << obj
152
+ end
153
+ end
154
+ objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
155
+ end
156
+ end
157
+
158
+ describe '#fill' do
159
+ it 'is a no-op' do
160
+ subject.stream = StringIO.new(buffer1 + buffer2 + buffer3)
161
+ subject.fill
162
+ subject.each { |obj| }
163
+ end
164
+ end
165
+
166
+ describe '#reset' do
167
+ context 'with a buffer' do
168
+ it 'is unclear what it is supposed to do'
169
+ end
170
+
171
+ context 'with a stream' do
172
+ it 'is unclear what it is supposed to do'
173
+ end
174
+ end
175
+
176
+ context 'regressions' do
177
+ it 'handles massive arrays (issue #2)' do
178
+ array = ['foo'] * 10_000
179
+ MessagePack.unpack(MessagePack.pack(array)).should have(10_000).items
180
+ end
181
+ end
182
+
183
+ context 'encoding', :encodings do
184
+ before :all do
185
+ @default_internal = Encoding.default_internal
186
+ @default_external = Encoding.default_external
187
+ end
188
+
189
+ after :all do
190
+ Encoding.default_internal = @default_internal
191
+ Encoding.default_external = @default_external
192
+ end
193
+
194
+ let :buffer do
195
+ MessagePack.pack({'hello' => 'world', 'nested' => ['object', {"sk\xC3\xA5l".force_encoding('utf-8') => true}]})
196
+ end
197
+
198
+ let :unpacker do
199
+ described_class.new
200
+ end
201
+
202
+ before do
203
+ Encoding.default_internal = Encoding::UTF_8
204
+ Encoding.default_external = Encoding::ISO_8859_1
205
+ end
206
+
207
+ it 'produces results with default internal encoding' do
208
+ unpacker.execute(buffer, 0)
209
+ strings = flatten(unpacker.data).grep(String)
210
+ strings.map(&:encoding).uniq.should == [Encoding.default_internal]
211
+ end
212
+
213
+ it 'recodes to internal encoding' do
214
+ unpacker.execute(buffer, 0)
215
+ unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding.default_internal)]
216
+ end
217
+ end
218
+
219
+ context 'extensions' do
220
+ context 'symbolized keys' do
221
+ let :buffer do
222
+ MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]})
223
+ end
224
+
225
+ let :unpacker do
226
+ described_class.new(:symbolize_keys => true)
227
+ end
228
+
229
+ it 'can symbolize keys when using #execute' do
230
+ unpacker.execute(buffer, 0)
231
+ unpacker.data.should == {:hello => 'world', :nested => ['object', {:structure => true}]}
232
+ end
233
+
234
+ it 'can symbolize keys when using #each' do
235
+ objs = []
236
+ unpacker.feed(buffer)
237
+ unpacker.each do |obj|
238
+ objs << obj
239
+ end
240
+ objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}]
241
+ end
242
+
243
+ it 'can symbolize keys when using #feed_each' do
244
+ objs = []
245
+ unpacker.feed_each(buffer) do |obj|
246
+ objs << obj
247
+ end
248
+ objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}]
249
+ end
250
+ end
251
+
252
+ context 'encoding', :encodings do
253
+ let :buffer do
254
+ MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]})
255
+ end
256
+
257
+ let :unpacker do
258
+ described_class.new(:encoding => 'UTF-8')
259
+ end
260
+
261
+ it 'can hardcode encoding when using #execute' do
262
+ unpacker.execute(buffer, 0)
263
+ strings = flatten(unpacker.data).grep(String)
264
+ strings.should == %w[hello world nested object structure]
265
+ strings.map(&:encoding).uniq.should == [Encoding::UTF_8]
266
+ end
267
+
268
+ it 'can hardcode encoding when using #each' do
269
+ objs = []
270
+ unpacker.feed(buffer)
271
+ unpacker.each do |obj|
272
+ objs << obj
273
+ end
274
+ strings = flatten(objs).grep(String)
275
+ strings.should == %w[hello world nested object structure]
276
+ strings.map(&:encoding).uniq.should == [Encoding::UTF_8]
277
+ end
278
+
279
+ it 'can hardcode encoding when using #feed_each' do
280
+ objs = []
281
+ unpacker.feed_each(buffer) do |obj|
282
+ objs << obj
283
+ end
284
+ strings = flatten(objs).grep(String)
285
+ strings.should == %w[hello world nested object structure]
286
+ strings.map(&:encoding).uniq.should == [Encoding::UTF_8]
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,136 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'spec_helper'
4
+
5
+ def utf8enc(str)
6
+ str.encode('UTF-8')
7
+ end
8
+
9
+ def asciienc(str)
10
+ str.encode('ASCII-8BIT')
11
+ end
12
+
13
+ describe MessagePack do
14
+ tests = {
15
+ 'constant values' => [
16
+ ['true', true, "\xC3"],
17
+ ['false', false, "\xC2"],
18
+ ['nil', nil, "\xC0"]
19
+ ],
20
+ 'numbers' => [
21
+ ['zero', 0, "\x00"],
22
+ ['127', 127, "\x7F"],
23
+ ['128', 128, "\xCC\x80"],
24
+ ['256', 256, "\xCD\x01\x00"],
25
+ ['-1', -1, "\xFF"],
26
+ ['-33', -33, "\xD0\xDF"],
27
+ ['-129', -129, "\xD1\xFF\x7F"],
28
+ ['small integers', 42, "*"],
29
+ ['medium integers', 333, "\xCD\x01M"],
30
+ ['large integers', 2**31 - 1, "\xCE\x7F\xFF\xFF\xFF"],
31
+ ['huge integers', 2**64 - 1, "\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
32
+ ['negative integers', -1, "\xFF"],
33
+ ['1.0', 1.0, "\xcb\x3f\xf0\x00\x00\x00\x00\x00\x00"],
34
+ ['small floats', 3.14, "\xCB@\t\x1E\xB8Q\xEB\x85\x1F"],
35
+ ['big floats', Math::PI * 1_000_000_000_000_000_000, "\xCBC\xC5\xCC\x96\xEF\xD1\x19%"],
36
+ ['negative floats', -2.1, "\xCB\xC0\x00\xCC\xCC\xCC\xCC\xCC\xCD"]
37
+ ],
38
+ 'strings' => [
39
+ ['strings', utf8enc('hello world'), "\xABhello world"],
40
+ ['empty strings', utf8enc(''), "\xA0"]
41
+ ],
42
+ 'arrays' => [
43
+ ['empty arrays', [], "\x90"],
44
+ ['arrays with strings', [utf8enc("hello"), utf8enc("world")], "\x92\xA5hello\xA5world"],
45
+ ['arrays with mixed values', [utf8enc("hello"), utf8enc("world"), 42], "\x93\xA5hello\xA5world*"],
46
+ ['arrays of arrays', [[[[1, 2], 3], 4]], "\x91\x92\x92\x92\x01\x02\x03\x04"],
47
+ ['empty arrays', [], "\x90"]
48
+ ],
49
+ 'hashes' => [
50
+ ['empty hashes', {}, "\x80"],
51
+ ['hashes', {utf8enc('foo') => utf8enc('bar')}, "\x81\xA3foo\xA3bar"],
52
+ ['hashes with mixed keys and values', {utf8enc('foo') => utf8enc('bar'), 3 => utf8enc('three'), utf8enc('four') => 4, utf8enc('x') => [utf8enc('y')], utf8enc('a') => utf8enc('b')}, "\x85\xA3foo\xA3bar\x03\xA5three\xA4four\x04\xA1x\x91\xA1y\xA1a\xA1b"],
53
+ ['hashes of hashes', {{utf8enc('x') => {utf8enc('y') => utf8enc('z')}} => utf8enc('s')}, "\x81\x81\xA1x\x81\xA1y\xA1z\xA1s"],
54
+ ['hashes with nils', {utf8enc('foo') => nil}, "\x81\xA3foo\xC0"]
55
+ ]
56
+ }
57
+
58
+ tests.each do |ctx, its|
59
+ context("with #{ctx}") do
60
+ its.each do |desc, unpacked, packed|
61
+ it("encodes #{desc}") do
62
+ MessagePack.pack(unpacked).should == packed
63
+ end
64
+
65
+ it "decodes #{desc}" do
66
+ MessagePack.unpack(packed).should == unpacked
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'using other names for .pack and .unpack' do
73
+ it 'can unpack with .load' do
74
+ MessagePack.load("\xABhello world").should == 'hello world'
75
+ end
76
+
77
+ it 'can pack with .dump' do
78
+ MessagePack.dump(utf8enc('hello world')).should == "\xABhello world"
79
+ end
80
+ end
81
+
82
+ context 'with symbols' do
83
+ it 'encodes symbols as strings' do
84
+ MessagePack.pack(:symbol).should == "\xA6symbol"
85
+ end
86
+ end
87
+
88
+ context 'with different external encoding', :encodings do
89
+ before do
90
+ @default_external = Encoding.default_external
91
+ @default_internal = Encoding.default_internal
92
+ Encoding.default_external = Encoding::UTF_8
93
+ Encoding.default_internal = Encoding::ISO_8859_1
94
+ end
95
+
96
+ after do
97
+ Encoding.default_external = @default_external
98
+ Encoding.default_internal = @default_internal
99
+ end
100
+
101
+ it 'transcodes strings when encoding' do
102
+ input = "sk\xE5l".force_encoding(Encoding::ISO_8859_1)
103
+ MessagePack.pack(input).should == "\xA5sk\xC3\xA5l"
104
+ end
105
+ end
106
+
107
+ context 'with other things' do
108
+ it 'raises an error on #pack with an unsupported type' do
109
+ expect { MessagePack.pack(self) }.to raise_error(ArgumentError, /^Cannot pack type:/)
110
+ end
111
+
112
+ it 'rasies an error on #unpack with garbage' do
113
+ skip "???"
114
+ expect { MessagePack.unpack('asdka;sd') }.to raise_error(MessagePack::UnpackError)
115
+ end
116
+ end
117
+
118
+ context 'extensions' do
119
+ it 'can unpack hashes with symbolized keys' do
120
+ packed = MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]})
121
+ unpacked = MessagePack.unpack(packed, :symbolize_keys => true)
122
+ unpacked.should == {:hello => 'world', :nested => ['object', {:structure => true}]}
123
+ end
124
+
125
+ it 'can unpack strings with a specified encoding', :encodings do
126
+ packed = MessagePack.pack({utf8enc('hello') => utf8enc('world')})
127
+ unpacked = MessagePack.unpack(packed)
128
+ unpacked['hello'].encoding.should == Encoding::UTF_8
129
+ end
130
+
131
+ it 'can pack strings with a specified encoding', :encodings do
132
+ packed = MessagePack.pack({'hello' => "w\xE5rld".force_encoding(Encoding::ISO_8859_1)})
133
+ packed.index("w\xC3\xA5rld").should_not be_nil
134
+ end
135
+ end
136
+ end