msgpack 1.5.6 → 1.8.0
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/ChangeLog +50 -0
- data/README.md +48 -12
- data/ext/java/org/msgpack/jruby/Buffer.java +3 -3
- data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +11 -20
- data/ext/java/org/msgpack/jruby/ExtensionValue.java +1 -1
- data/ext/java/org/msgpack/jruby/Factory.java +11 -50
- data/ext/java/org/msgpack/jruby/Packer.java +9 -24
- data/ext/java/org/msgpack/jruby/Unpacker.java +15 -32
- data/ext/msgpack/buffer.c +69 -56
- data/ext/msgpack/buffer.h +138 -44
- data/ext/msgpack/buffer_class.c +132 -31
- data/ext/msgpack/buffer_class.h +1 -0
- data/ext/msgpack/extconf.rb +20 -30
- data/ext/msgpack/factory_class.c +75 -86
- data/ext/msgpack/packer.c +13 -16
- data/ext/msgpack/packer.h +24 -21
- data/ext/msgpack/packer_class.c +72 -98
- data/ext/msgpack/packer_class.h +11 -0
- data/ext/msgpack/packer_ext_registry.c +31 -28
- data/ext/msgpack/packer_ext_registry.h +10 -14
- data/ext/msgpack/rbinit.c +1 -1
- data/ext/msgpack/rmem.c +3 -4
- data/ext/msgpack/sysdep.h +5 -2
- data/ext/msgpack/unpacker.c +201 -113
- data/ext/msgpack/unpacker.h +22 -15
- data/ext/msgpack/unpacker_class.c +87 -92
- data/ext/msgpack/unpacker_class.h +11 -0
- data/ext/msgpack/unpacker_ext_registry.c +4 -16
- data/ext/msgpack/unpacker_ext_registry.h +3 -7
- data/lib/msgpack/buffer.rb +9 -0
- data/lib/msgpack/factory.rb +90 -63
- data/lib/msgpack/packer.rb +10 -1
- data/lib/msgpack/unpacker.rb +14 -1
- data/lib/msgpack/version.rb +1 -1
- data/lib/msgpack.rb +1 -0
- data/msgpack.gemspec +8 -3
- metadata +21 -51
- data/.github/workflows/ci.yaml +0 -57
- data/.gitignore +0 -23
- data/.rubocop.yml +0 -36
- data/Gemfile +0 -9
- data/Rakefile +0 -70
- data/appveyor.yml +0 -18
- data/bench/bench.rb +0 -78
- data/doclib/msgpack/buffer.rb +0 -193
- data/doclib/msgpack/core_ext.rb +0 -101
- data/doclib/msgpack/error.rb +0 -19
- data/doclib/msgpack/extension_value.rb +0 -9
- data/doclib/msgpack/factory.rb +0 -145
- data/doclib/msgpack/packer.rb +0 -209
- data/doclib/msgpack/time.rb +0 -22
- data/doclib/msgpack/timestamp.rb +0 -44
- data/doclib/msgpack/unpacker.rb +0 -183
- data/doclib/msgpack.rb +0 -87
- data/msgpack.org.md +0 -46
- data/spec/bigint_spec.rb +0 -26
- data/spec/cases.json +0 -1
- data/spec/cases.msg +0 -0
- data/spec/cases_compact.msg +0 -0
- data/spec/cases_spec.rb +0 -39
- data/spec/cruby/buffer_io_spec.rb +0 -255
- data/spec/cruby/buffer_packer.rb +0 -29
- data/spec/cruby/buffer_spec.rb +0 -575
- data/spec/cruby/buffer_unpacker.rb +0 -19
- data/spec/cruby/unpacker_spec.rb +0 -70
- data/spec/ext_value_spec.rb +0 -99
- data/spec/exttypes.rb +0 -51
- data/spec/factory_spec.rb +0 -688
- data/spec/format_spec.rb +0 -301
- data/spec/jruby/benchmarks/shootout_bm.rb +0 -73
- data/spec/jruby/benchmarks/symbolize_keys_bm.rb +0 -25
- data/spec/jruby/unpacker_spec.rb +0 -186
- data/spec/msgpack_spec.rb +0 -214
- data/spec/pack_spec.rb +0 -61
- data/spec/packer_spec.rb +0 -575
- data/spec/random_compat.rb +0 -24
- data/spec/spec_helper.rb +0 -71
- data/spec/timestamp_spec.rb +0 -159
- data/spec/unpack_spec.rb +0 -57
- data/spec/unpacker_spec.rb +0 -859
data/spec/unpacker_spec.rb
DELETED
@@ -1,859 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
require 'tempfile'
|
3
|
-
require 'zlib'
|
4
|
-
|
5
|
-
require 'spec_helper'
|
6
|
-
|
7
|
-
describe MessagePack::Unpacker do
|
8
|
-
let :unpacker do
|
9
|
-
MessagePack::Unpacker.new
|
10
|
-
end
|
11
|
-
|
12
|
-
let :packer do
|
13
|
-
MessagePack::Packer.new
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'gets options to specify how to unpack values' do
|
17
|
-
u1 = MessagePack::Unpacker.new
|
18
|
-
u1.symbolize_keys?.should == false
|
19
|
-
u1.freeze?.should == false
|
20
|
-
u1.allow_unknown_ext?.should == false
|
21
|
-
|
22
|
-
u2 = MessagePack::Unpacker.new(symbolize_keys: true, freeze: true, allow_unknown_ext: true)
|
23
|
-
u2.symbolize_keys?.should == true
|
24
|
-
u2.freeze?.should == true
|
25
|
-
u2.allow_unknown_ext?.should == true
|
26
|
-
end
|
27
|
-
|
28
|
-
if automatic_string_keys_deduplication?
|
29
|
-
it 'ensure string hash keys are deduplicated' do
|
30
|
-
sample_data = [{"foo" => 1}, {"foo" => 2}]
|
31
|
-
sample_packed = MessagePack.pack(sample_data).force_encoding('ASCII-8BIT')
|
32
|
-
unpacker.feed(sample_packed)
|
33
|
-
hashes = nil
|
34
|
-
unpacker.each { |obj| hashes = obj }
|
35
|
-
expect(hashes[0].keys.first).to equal(hashes[1].keys.first)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'ensure strings are not deduplicated' do
|
39
|
-
sample_data = ["foo"]
|
40
|
-
sample_packed = MessagePack.pack(sample_data).force_encoding('ASCII-8BIT')
|
41
|
-
unpacker.feed(sample_packed)
|
42
|
-
ary = nil
|
43
|
-
unpacker.each { |obj| ary = obj }
|
44
|
-
expect(ary.first.frozen?).to eq(false)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'gets IO or object which has #read to read data from it' do
|
49
|
-
sample_data = {"message" => "morning!", "num" => 1}
|
50
|
-
sample_packed = MessagePack.pack(sample_data).force_encoding('ASCII-8BIT')
|
51
|
-
|
52
|
-
Tempfile.open("for_io") do |file|
|
53
|
-
file.sync = true
|
54
|
-
file.write sample_packed
|
55
|
-
file.rewind
|
56
|
-
|
57
|
-
u1 = MessagePack::Unpacker.new(file)
|
58
|
-
u1.each do |obj|
|
59
|
-
expect(obj).to eql(sample_data)
|
60
|
-
end
|
61
|
-
file.unlink
|
62
|
-
end
|
63
|
-
|
64
|
-
sio = StringIO.new(sample_packed)
|
65
|
-
u2 = MessagePack::Unpacker.new(sio)
|
66
|
-
u2.each do |obj|
|
67
|
-
expect(obj).to eql(sample_data)
|
68
|
-
end
|
69
|
-
|
70
|
-
dio = StringIO.new
|
71
|
-
Zlib::GzipWriter.wrap(dio){|gz| gz.write sample_packed }
|
72
|
-
reader = Zlib::GzipReader.new(StringIO.new(dio.string))
|
73
|
-
u3 = MessagePack::Unpacker.new(reader)
|
74
|
-
u3.each do |obj|
|
75
|
-
expect(obj).to eql(sample_data)
|
76
|
-
end
|
77
|
-
|
78
|
-
class DummyIO
|
79
|
-
def initialize
|
80
|
-
@buf = "".force_encoding('ASCII-8BIT')
|
81
|
-
@pos = 0
|
82
|
-
end
|
83
|
-
def write(val)
|
84
|
-
@buf << val.to_s
|
85
|
-
end
|
86
|
-
def read(length=nil,outbuf="")
|
87
|
-
if @pos == @buf.size
|
88
|
-
nil
|
89
|
-
elsif length.nil?
|
90
|
-
val = @buf[@pos..(@buf.size)]
|
91
|
-
@pos = @buf.size
|
92
|
-
outbuf << val
|
93
|
-
outbuf
|
94
|
-
else
|
95
|
-
val = @buf[@pos..(@pos + length)]
|
96
|
-
@pos += val.size
|
97
|
-
@pos = @buf.size if @pos > @buf.size
|
98
|
-
outbuf << val
|
99
|
-
outbuf
|
100
|
-
end
|
101
|
-
end
|
102
|
-
def flush
|
103
|
-
# nop
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
dio = DummyIO.new
|
108
|
-
dio.write sample_packed
|
109
|
-
u4 = MessagePack::Unpacker.new(dio)
|
110
|
-
u4.each do |obj|
|
111
|
-
expect(obj).to eql(sample_data)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'read_array_header succeeds' do
|
116
|
-
unpacker.feed("\x91")
|
117
|
-
unpacker.read_array_header.should == 1
|
118
|
-
end
|
119
|
-
|
120
|
-
it 'read_array_header fails' do
|
121
|
-
unpacker.feed("\x81")
|
122
|
-
lambda {
|
123
|
-
unpacker.read_array_header
|
124
|
-
}.should raise_error(MessagePack::TypeError) # TypeError is included in UnexpectedTypeError
|
125
|
-
lambda {
|
126
|
-
unpacker.read_array_header
|
127
|
-
}.should raise_error(MessagePack::UnexpectedTypeError)
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'read_map_header converts an map to key-value sequence' do
|
131
|
-
packer.write_array_header(2)
|
132
|
-
packer.write("e")
|
133
|
-
packer.write(1)
|
134
|
-
unpacker = MessagePack::Unpacker.new
|
135
|
-
unpacker.feed(packer.to_s)
|
136
|
-
unpacker.read_array_header.should == 2
|
137
|
-
unpacker.read.should == "e"
|
138
|
-
unpacker.read.should == 1
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'read_map_header succeeds' do
|
142
|
-
unpacker.feed("\x81")
|
143
|
-
unpacker.read_map_header.should == 1
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'read_map_header converts an map to key-value sequence' do
|
147
|
-
packer.write_map_header(1)
|
148
|
-
packer.write("k")
|
149
|
-
packer.write("v")
|
150
|
-
unpacker = MessagePack::Unpacker.new
|
151
|
-
unpacker.feed(packer.to_s)
|
152
|
-
unpacker.read_map_header.should == 1
|
153
|
-
unpacker.read.should == "k"
|
154
|
-
unpacker.read.should == "v"
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'read_map_header fails' do
|
158
|
-
unpacker.feed("\x91")
|
159
|
-
lambda {
|
160
|
-
unpacker.read_map_header
|
161
|
-
}.should raise_error(MessagePack::TypeError) # TypeError is included in UnexpectedTypeError
|
162
|
-
lambda {
|
163
|
-
unpacker.read_map_header
|
164
|
-
}.should raise_error(MessagePack::UnexpectedTypeError)
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'read raises EOFError before feeding' do
|
168
|
-
lambda {
|
169
|
-
unpacker.read
|
170
|
-
}.should raise_error(EOFError)
|
171
|
-
end
|
172
|
-
|
173
|
-
let :sample_object do
|
174
|
-
[1024, {["a","b"]=>["c","d"]}, ["e","f"], "d", 70000, 4.12, 1.5, 1.5, 1.5]
|
175
|
-
end
|
176
|
-
|
177
|
-
it 'feed and each continue internal state' do
|
178
|
-
raw = sample_object.to_msgpack.to_s * 4
|
179
|
-
objects = []
|
180
|
-
|
181
|
-
raw.split(//).each do |b|
|
182
|
-
unpacker.feed(b)
|
183
|
-
unpacker.each {|c|
|
184
|
-
objects << c
|
185
|
-
}
|
186
|
-
end
|
187
|
-
|
188
|
-
objects.should == [sample_object] * 4
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'feed_each continues internal state' do
|
192
|
-
raw = sample_object.to_msgpack.to_s * 4
|
193
|
-
objects = []
|
194
|
-
|
195
|
-
raw.split(//).each do |b|
|
196
|
-
unpacker.feed_each(b) {|c|
|
197
|
-
objects << c
|
198
|
-
}
|
199
|
-
end
|
200
|
-
|
201
|
-
objects.should == [sample_object] * 4
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'feed_each enumerator' do
|
205
|
-
raw = sample_object.to_msgpack.to_s * 4
|
206
|
-
|
207
|
-
enum = unpacker.feed_each(raw)
|
208
|
-
enum.should be_instance_of(Enumerator)
|
209
|
-
enum.to_a.should == [sample_object] * 4
|
210
|
-
end
|
211
|
-
|
212
|
-
it 'reset clears internal buffer' do
|
213
|
-
# 1-element array
|
214
|
-
unpacker.feed("\x91")
|
215
|
-
unpacker.reset
|
216
|
-
unpacker.feed("\x01")
|
217
|
-
|
218
|
-
unpacker.each.map {|x| x }.should == [1]
|
219
|
-
end
|
220
|
-
|
221
|
-
it 'reset clears internal state' do
|
222
|
-
# 1-element array
|
223
|
-
unpacker.feed("\x91")
|
224
|
-
unpacker.each.map {|x| x }.should == []
|
225
|
-
|
226
|
-
unpacker.reset
|
227
|
-
|
228
|
-
unpacker.feed("\x01")
|
229
|
-
unpacker.each.map {|x| x }.should == [1]
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'frozen short strings' do
|
233
|
-
raw = sample_object.to_msgpack.to_s.force_encoding('UTF-8')
|
234
|
-
lambda {
|
235
|
-
unpacker.feed_each(raw.freeze) { }
|
236
|
-
}.should_not raise_error
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'frozen long strings' do
|
240
|
-
raw = (sample_object.to_msgpack.to_s * 10240).force_encoding('UTF-8')
|
241
|
-
lambda {
|
242
|
-
unpacker.feed_each(raw.freeze) { }
|
243
|
-
}.should_not raise_error
|
244
|
-
end
|
245
|
-
|
246
|
-
it 'read raises invalid byte error' do
|
247
|
-
unpacker.feed("\xc1")
|
248
|
-
lambda {
|
249
|
-
unpacker.read
|
250
|
-
}.should raise_error(MessagePack::MalformedFormatError)
|
251
|
-
end
|
252
|
-
|
253
|
-
it "gc mark" do
|
254
|
-
raw = sample_object.to_msgpack.to_s * 4
|
255
|
-
|
256
|
-
n = 0
|
257
|
-
raw.split(//).each do |b|
|
258
|
-
GC.start
|
259
|
-
unpacker.feed_each(b) {|o|
|
260
|
-
GC.start
|
261
|
-
o.should == sample_object
|
262
|
-
n += 1
|
263
|
-
}
|
264
|
-
GC.start
|
265
|
-
end
|
266
|
-
|
267
|
-
n.should == 4
|
268
|
-
end
|
269
|
-
|
270
|
-
it "buffer" do
|
271
|
-
orig = "a"*32*1024*4
|
272
|
-
raw = orig.to_msgpack.to_s
|
273
|
-
|
274
|
-
n = 655
|
275
|
-
times = raw.size / n
|
276
|
-
times += 1 unless raw.size % n == 0
|
277
|
-
|
278
|
-
off = 0
|
279
|
-
parsed = false
|
280
|
-
|
281
|
-
times.times do
|
282
|
-
parsed.should == false
|
283
|
-
|
284
|
-
seg = raw[off, n]
|
285
|
-
off += seg.length
|
286
|
-
|
287
|
-
unpacker.feed_each(seg) {|obj|
|
288
|
-
parsed.should == false
|
289
|
-
obj.should == orig
|
290
|
-
parsed = true
|
291
|
-
}
|
292
|
-
end
|
293
|
-
|
294
|
-
parsed.should == true
|
295
|
-
end
|
296
|
-
|
297
|
-
it 'MessagePack.unpack symbolize_keys' do
|
298
|
-
symbolized_hash = {:a => 'b', :c => 'd'}
|
299
|
-
MessagePack.load(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash
|
300
|
-
MessagePack.unpack(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash
|
301
|
-
end
|
302
|
-
|
303
|
-
it 'MessagePack.unpack symbolize_keys preserve encoding' do
|
304
|
-
hash = { :ascii => 1, :utf8_é => 2}
|
305
|
-
loaded_hash = MessagePack.load(MessagePack.pack(hash), :symbolize_keys => true)
|
306
|
-
|
307
|
-
hash.keys[0].encoding.should == Encoding::US_ASCII # Ruby coerce symbols to US-ASCII when possible.
|
308
|
-
loaded_hash.keys[0].should == hash.keys[0]
|
309
|
-
loaded_hash.keys[0].encoding.should == hash.keys[0].encoding
|
310
|
-
|
311
|
-
hash.keys[1].encoding.should == Encoding::UTF_8
|
312
|
-
loaded_hash.keys[1].should == hash.keys[1]
|
313
|
-
loaded_hash.keys[1].encoding.should == hash.keys[1].encoding
|
314
|
-
|
315
|
-
MessagePack.unpack(MessagePack.pack(hash), :symbolize_keys => true).should == hash
|
316
|
-
end
|
317
|
-
|
318
|
-
it 'Unpacker#unpack symbolize_keys' do
|
319
|
-
unpacker = MessagePack::Unpacker.new(:symbolize_keys => true)
|
320
|
-
symbolized_hash = {:a => 'b', :c => 'd'}
|
321
|
-
unpacker.feed(MessagePack.pack(symbolized_hash)).read.should == symbolized_hash
|
322
|
-
end
|
323
|
-
|
324
|
-
it "msgpack str 8 type" do
|
325
|
-
MessagePack.unpack([0xd9, 0x00].pack('C*')).should == ""
|
326
|
-
MessagePack.unpack([0xd9, 0x00].pack('C*')).encoding.should == Encoding::UTF_8
|
327
|
-
MessagePack.unpack([0xd9, 0x01].pack('C*') + 'a').should == "a"
|
328
|
-
MessagePack.unpack([0xd9, 0x02].pack('C*') + 'aa').should == "aa"
|
329
|
-
end
|
330
|
-
|
331
|
-
it "msgpack str 16 type" do
|
332
|
-
MessagePack.unpack([0xda, 0x00, 0x00].pack('C*')).should == ""
|
333
|
-
MessagePack.unpack([0xda, 0x00, 0x00].pack('C*')).encoding.should == Encoding::UTF_8
|
334
|
-
MessagePack.unpack([0xda, 0x00, 0x01].pack('C*') + 'a').should == "a"
|
335
|
-
MessagePack.unpack([0xda, 0x00, 0x02].pack('C*') + 'aa').should == "aa"
|
336
|
-
end
|
337
|
-
|
338
|
-
it "msgpack str 32 type" do
|
339
|
-
MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x00].pack('C*')).should == ""
|
340
|
-
MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x00].pack('C*')).encoding.should == Encoding::UTF_8
|
341
|
-
MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a"
|
342
|
-
MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa"
|
343
|
-
end
|
344
|
-
|
345
|
-
it "msgpack bin 8 type" do
|
346
|
-
MessagePack.unpack([0xc4, 0x00].pack('C*')).should == ""
|
347
|
-
MessagePack.unpack([0xc4, 0x00].pack('C*')).encoding.should == Encoding::ASCII_8BIT
|
348
|
-
MessagePack.unpack([0xc4, 0x01].pack('C*') + 'a').should == "a"
|
349
|
-
MessagePack.unpack([0xc4, 0x02].pack('C*') + 'aa').should == "aa"
|
350
|
-
end
|
351
|
-
|
352
|
-
it "msgpack bin 16 type" do
|
353
|
-
MessagePack.unpack([0xc5, 0x00, 0x00].pack('C*')).should == ""
|
354
|
-
MessagePack.unpack([0xc5, 0x00, 0x00].pack('C*')).encoding.should == Encoding::ASCII_8BIT
|
355
|
-
MessagePack.unpack([0xc5, 0x00, 0x01].pack('C*') + 'a').should == "a"
|
356
|
-
MessagePack.unpack([0xc5, 0x00, 0x02].pack('C*') + 'aa').should == "aa"
|
357
|
-
end
|
358
|
-
|
359
|
-
it "msgpack bin 32 type" do
|
360
|
-
MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x00].pack('C*')).should == ""
|
361
|
-
MessagePack.unpack([0xc6, 0x0, 0x00, 0x00, 0x000].pack('C*')).encoding.should == Encoding::ASCII_8BIT
|
362
|
-
MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a"
|
363
|
-
MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa"
|
364
|
-
end
|
365
|
-
|
366
|
-
describe "ext formats" do
|
367
|
-
let(:unpacker) { MessagePack::Unpacker.new(allow_unknown_ext: true) }
|
368
|
-
|
369
|
-
[1, 2, 4, 8, 16].zip([0xd4, 0xd5, 0xd6, 0xd7, 0xd8]).each do |n,b|
|
370
|
-
it "msgpack fixext #{n} format" do
|
371
|
-
unpacker.feed([b, 1].pack('CC') + "a"*n).unpack.should == MessagePack::ExtensionValue.new(1, "a"*n)
|
372
|
-
unpacker.feed([b, -1].pack('CC') + "a"*n).unpack.should == MessagePack::ExtensionValue.new(-1, "a"*n)
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
it "msgpack ext 8 format" do
|
377
|
-
unpacker.feed([0xc7, 0, 1].pack('CCC')).unpack.should == MessagePack::ExtensionValue.new(1, "")
|
378
|
-
unpacker.feed([0xc7, 255, -1].pack('CCC') + "a"*255).unpack.should == MessagePack::ExtensionValue.new(-1, "a"*255)
|
379
|
-
end
|
380
|
-
|
381
|
-
it "msgpack ext 16 format" do
|
382
|
-
unpacker.feed([0xc8, 0, 1].pack('CnC')).unpack.should == MessagePack::ExtensionValue.new(1, "")
|
383
|
-
unpacker.feed([0xc8, 256, -1].pack('CnC') + "a"*256).unpack.should == MessagePack::ExtensionValue.new(-1, "a"*256)
|
384
|
-
end
|
385
|
-
|
386
|
-
it "msgpack ext 32 format" do
|
387
|
-
unpacker.feed([0xc9, 0, 1].pack('CNC')).unpack.should == MessagePack::ExtensionValue.new(1, "")
|
388
|
-
unpacker.feed([0xc9, 256, -1].pack('CNC') + "a"*256).unpack.should == MessagePack::ExtensionValue.new(-1, "a"*256)
|
389
|
-
unpacker.feed([0xc9, 65536, -1].pack('CNC') + "a"*65536).unpack.should == MessagePack::ExtensionValue.new(-1, "a"*65536)
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
class ValueOne
|
394
|
-
attr_reader :num
|
395
|
-
def initialize(num)
|
396
|
-
@num = num
|
397
|
-
end
|
398
|
-
def ==(obj)
|
399
|
-
self.num == obj.num
|
400
|
-
end
|
401
|
-
def num
|
402
|
-
@num
|
403
|
-
end
|
404
|
-
def to_msgpack_ext
|
405
|
-
@num.to_msgpack
|
406
|
-
end
|
407
|
-
def self.from_msgpack_ext(data)
|
408
|
-
self.new(MessagePack.unpack(data))
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
class ValueTwo
|
413
|
-
attr_reader :num_s
|
414
|
-
def initialize(num)
|
415
|
-
@num_s = num.to_s
|
416
|
-
end
|
417
|
-
def ==(obj)
|
418
|
-
self.num_s == obj.num_s
|
419
|
-
end
|
420
|
-
def num
|
421
|
-
@num_s.to_i
|
422
|
-
end
|
423
|
-
def to_msgpack_ext
|
424
|
-
@num_s.to_msgpack
|
425
|
-
end
|
426
|
-
def self.from_msgpack_ext(data)
|
427
|
-
self.new(MessagePack.unpack(data))
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
describe '#type_registered?' do
|
432
|
-
it 'receive Class or Integer, and return bool' do
|
433
|
-
expect(subject.type_registered?(0x00)).to be_falsy
|
434
|
-
expect(subject.type_registered?(0x01)).to be_falsy
|
435
|
-
expect(subject.type_registered?(::ValueOne)).to be_falsy
|
436
|
-
end
|
437
|
-
|
438
|
-
it 'returns true if specified type or class is already registered' do
|
439
|
-
subject.register_type(0x30, ::ValueOne, :from_msgpack_ext)
|
440
|
-
subject.register_type(0x31, ::ValueTwo, :from_msgpack_ext)
|
441
|
-
|
442
|
-
expect(subject.type_registered?(0x00)).to be_falsy
|
443
|
-
expect(subject.type_registered?(0x01)).to be_falsy
|
444
|
-
|
445
|
-
expect(subject.type_registered?(0x30)).to be_truthy
|
446
|
-
expect(subject.type_registered?(0x31)).to be_truthy
|
447
|
-
expect(subject.type_registered?(::ValueOne)).to be_truthy
|
448
|
-
expect(subject.type_registered?(::ValueTwo)).to be_truthy
|
449
|
-
end
|
450
|
-
|
451
|
-
it 'cannot detect unpack rule with block, not method' do
|
452
|
-
subject.register_type(0x40){|data| ValueOne.from_msgpack_ext(data) }
|
453
|
-
|
454
|
-
expect(subject.type_registered?(0x40)).to be_truthy
|
455
|
-
expect(subject.type_registered?(ValueOne)).to be_falsy
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
context 'with ext definitions' do
|
460
|
-
it 'get type and class mapping for packing' do
|
461
|
-
unpacker = MessagePack::Unpacker.new
|
462
|
-
unpacker.register_type(0x01){|data| ValueOne.from_msgpack_ext }
|
463
|
-
unpacker.register_type(0x02){|data| ValueTwo.from_msgpack_ext(data) }
|
464
|
-
|
465
|
-
unpacker = MessagePack::Unpacker.new
|
466
|
-
unpacker.register_type(0x01, ValueOne, :from_msgpack_ext)
|
467
|
-
unpacker.register_type(0x02, ValueTwo, :from_msgpack_ext)
|
468
|
-
end
|
469
|
-
|
470
|
-
it 'returns a Array of Hash which contains :type, :class and :unpacker' do
|
471
|
-
unpacker = MessagePack::Unpacker.new
|
472
|
-
unpacker.register_type(0x02, ValueTwo, :from_msgpack_ext)
|
473
|
-
unpacker.register_type(0x01, ValueOne, :from_msgpack_ext)
|
474
|
-
|
475
|
-
list = unpacker.registered_types
|
476
|
-
|
477
|
-
expect(list).to be_a(Array)
|
478
|
-
expect(list.size).to eq(2)
|
479
|
-
|
480
|
-
one = list[0]
|
481
|
-
expect(one.keys.sort).to eq([:type, :class, :unpacker].sort)
|
482
|
-
expect(one[:type]).to eq(0x01)
|
483
|
-
expect(one[:class]).to eq(ValueOne)
|
484
|
-
expect(one[:unpacker]).to eq(:from_msgpack_ext)
|
485
|
-
|
486
|
-
two = list[1]
|
487
|
-
expect(two.keys.sort).to eq([:type, :class, :unpacker].sort)
|
488
|
-
expect(two[:type]).to eq(0x02)
|
489
|
-
expect(two[:class]).to eq(ValueTwo)
|
490
|
-
expect(two[:unpacker]).to eq(:from_msgpack_ext)
|
491
|
-
end
|
492
|
-
|
493
|
-
it 'returns a Array of Hash, which contains nil for class if block unpacker specified' do
|
494
|
-
unpacker = MessagePack::Unpacker.new
|
495
|
-
unpacker.register_type(0x01){|data| ValueOne.from_msgpack_ext }
|
496
|
-
unpacker.register_type(0x02, &ValueTwo.method(:from_msgpack_ext))
|
497
|
-
|
498
|
-
list = unpacker.registered_types
|
499
|
-
|
500
|
-
expect(list).to be_a(Array)
|
501
|
-
expect(list.size).to eq(2)
|
502
|
-
|
503
|
-
one = list[0]
|
504
|
-
expect(one.keys.sort).to eq([:type, :class, :unpacker].sort)
|
505
|
-
expect(one[:type]).to eq(0x01)
|
506
|
-
expect(one[:class]).to be_nil
|
507
|
-
expect(one[:unpacker]).to be_instance_of(Proc)
|
508
|
-
|
509
|
-
two = list[1]
|
510
|
-
expect(two.keys.sort).to eq([:type, :class, :unpacker].sort)
|
511
|
-
expect(two[:type]).to eq(0x02)
|
512
|
-
expect(two[:class]).to be_nil
|
513
|
-
expect(two[:unpacker]).to be_instance_of(Proc)
|
514
|
-
end
|
515
|
-
|
516
|
-
describe "registering an ext type for a module" do
|
517
|
-
subject { unpacker.feed("\xc7\x06\x00module").unpack }
|
518
|
-
|
519
|
-
let(:unpacker) { MessagePack::Unpacker.new }
|
520
|
-
|
521
|
-
before do
|
522
|
-
mod = Module.new do
|
523
|
-
def self.from_msgpack_ext(data)
|
524
|
-
"unpacked #{data}"
|
525
|
-
end
|
526
|
-
end
|
527
|
-
stub_const('Mod', mod)
|
528
|
-
end
|
529
|
-
|
530
|
-
before { unpacker.register_type(0x00, Mod, :from_msgpack_ext) }
|
531
|
-
it { is_expected.to eq "unpacked module" }
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
|
-
def flatten(struct, results = [])
|
536
|
-
case struct
|
537
|
-
when Array
|
538
|
-
struct.each { |v| flatten(v, results) }
|
539
|
-
when Hash
|
540
|
-
struct.each { |k, v| flatten(v, flatten(k, results)) }
|
541
|
-
else
|
542
|
-
results << struct
|
543
|
-
end
|
544
|
-
results
|
545
|
-
end
|
546
|
-
|
547
|
-
subject do
|
548
|
-
described_class.new
|
549
|
-
end
|
550
|
-
|
551
|
-
let :buffer1 do
|
552
|
-
MessagePack.pack(:foo => 'bar')
|
553
|
-
end
|
554
|
-
|
555
|
-
let :buffer2 do
|
556
|
-
MessagePack.pack(:hello => {:world => [1, 2, 3]})
|
557
|
-
end
|
558
|
-
|
559
|
-
let :buffer3 do
|
560
|
-
MessagePack.pack(:x => 'y')
|
561
|
-
end
|
562
|
-
|
563
|
-
describe '#read' do
|
564
|
-
context 'with a buffer' do
|
565
|
-
it 'reads objects' do
|
566
|
-
objects = []
|
567
|
-
subject.feed(buffer1)
|
568
|
-
subject.feed(buffer2)
|
569
|
-
subject.feed(buffer3)
|
570
|
-
objects << subject.read
|
571
|
-
objects << subject.read
|
572
|
-
objects << subject.read
|
573
|
-
objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
|
574
|
-
end
|
575
|
-
|
576
|
-
it 'reads map header' do
|
577
|
-
subject.feed({}.to_msgpack)
|
578
|
-
subject.read_map_header.should == 0
|
579
|
-
end
|
580
|
-
|
581
|
-
it 'reads array header' do
|
582
|
-
subject.feed([].to_msgpack)
|
583
|
-
subject.read_array_header.should == 0
|
584
|
-
end
|
585
|
-
end
|
586
|
-
end
|
587
|
-
|
588
|
-
describe '#each' do
|
589
|
-
context 'with a buffer' do
|
590
|
-
it 'yields each object in the buffer' do
|
591
|
-
objects = []
|
592
|
-
subject.feed(buffer1)
|
593
|
-
subject.feed(buffer2)
|
594
|
-
subject.feed(buffer3)
|
595
|
-
subject.each do |obj|
|
596
|
-
objects << obj
|
597
|
-
end
|
598
|
-
objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
|
599
|
-
end
|
600
|
-
|
601
|
-
it 'returns an enumerator when no block is given' do
|
602
|
-
subject.feed(buffer1)
|
603
|
-
subject.feed(buffer2)
|
604
|
-
subject.feed(buffer3)
|
605
|
-
enum = subject.each
|
606
|
-
enum.map { |obj| obj.keys.first }.should == %w[foo hello x]
|
607
|
-
end
|
608
|
-
end
|
609
|
-
|
610
|
-
context 'with a stream passed to the constructor' do
|
611
|
-
it 'yields each object in the stream' do
|
612
|
-
objects = []
|
613
|
-
unpacker = described_class.new(StringIO.new(buffer1 + buffer2 + buffer3))
|
614
|
-
unpacker.each do |obj|
|
615
|
-
objects << obj
|
616
|
-
end
|
617
|
-
objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
context 'with a stream and symbolize_keys passed to the constructor' do
|
622
|
-
it 'yields each object in the stream, with symbolized keys' do
|
623
|
-
objects = []
|
624
|
-
unpacker = described_class.new(StringIO.new(buffer1 + buffer2 + buffer3), symbolize_keys: true)
|
625
|
-
unpacker.each do |obj|
|
626
|
-
objects << obj
|
627
|
-
end
|
628
|
-
objects.should == [{:foo => 'bar'}, {:hello => {:world => [1, 2, 3]}}, {:x => 'y'}]
|
629
|
-
end
|
630
|
-
end
|
631
|
-
end
|
632
|
-
|
633
|
-
describe '#feed_each' do
|
634
|
-
it 'feeds the buffer then runs #each' do
|
635
|
-
objects = []
|
636
|
-
subject.feed_each(buffer1 + buffer2 + buffer3) do |obj|
|
637
|
-
objects << obj
|
638
|
-
end
|
639
|
-
objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
|
640
|
-
end
|
641
|
-
|
642
|
-
it 'handles chunked data' do
|
643
|
-
objects = []
|
644
|
-
buffer = buffer1 + buffer2 + buffer3
|
645
|
-
buffer.chars.each do |ch|
|
646
|
-
subject.feed_each(ch) do |obj|
|
647
|
-
objects << obj
|
648
|
-
end
|
649
|
-
end
|
650
|
-
objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}]
|
651
|
-
end
|
652
|
-
end
|
653
|
-
|
654
|
-
context 'regressions' do
|
655
|
-
it 'handles massive arrays (issue #2)' do
|
656
|
-
array = ['foo'] * 10_000
|
657
|
-
MessagePack.unpack(MessagePack.pack(array)).size.should == 10_000
|
658
|
-
end
|
659
|
-
|
660
|
-
it 'preserves string encoding (issue #200)' do
|
661
|
-
string = 'a'.force_encoding(Encoding::UTF_8)
|
662
|
-
MessagePack.unpack(MessagePack.pack(string)).encoding.should == string.encoding
|
663
|
-
|
664
|
-
string *= 256
|
665
|
-
MessagePack.unpack(MessagePack.pack(string)).encoding.should == string.encoding
|
666
|
-
end
|
667
|
-
end
|
668
|
-
|
669
|
-
context 'extensions' do
|
670
|
-
context 'symbolized keys' do
|
671
|
-
let :buffer do
|
672
|
-
MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]})
|
673
|
-
end
|
674
|
-
|
675
|
-
let :unpacker do
|
676
|
-
described_class.new(:symbolize_keys => true)
|
677
|
-
end
|
678
|
-
|
679
|
-
it 'can symbolize keys when using #each' do
|
680
|
-
objs = []
|
681
|
-
unpacker.feed(buffer)
|
682
|
-
unpacker.each do |obj|
|
683
|
-
objs << obj
|
684
|
-
end
|
685
|
-
objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}]
|
686
|
-
end
|
687
|
-
|
688
|
-
it 'can symbolize keys when using #feed_each' do
|
689
|
-
objs = []
|
690
|
-
unpacker.feed_each(buffer) do |obj|
|
691
|
-
objs << obj
|
692
|
-
end
|
693
|
-
objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}]
|
694
|
-
end
|
695
|
-
end
|
696
|
-
|
697
|
-
context 'freeze' do
|
698
|
-
let :struct do
|
699
|
-
{'hello' => 'world', 'nested' => ['object', {'structure' => true}]}
|
700
|
-
end
|
701
|
-
|
702
|
-
let :buffer do
|
703
|
-
MessagePack.pack(struct)
|
704
|
-
end
|
705
|
-
|
706
|
-
let :unpacker do
|
707
|
-
described_class.new(:freeze => true)
|
708
|
-
end
|
709
|
-
|
710
|
-
if (-"test").equal?(-"test") # RUBY_VERSION >= "2.5"
|
711
|
-
it 'dedups strings' do
|
712
|
-
interned_str = -"test"
|
713
|
-
roundtrip = MessagePack.unpack(MessagePack.pack(interned_str), freeze: true)
|
714
|
-
expect(roundtrip).to be interned_str
|
715
|
-
|
716
|
-
interned_str = -""
|
717
|
-
roundtrip = MessagePack.unpack(MessagePack.pack(interned_str), freeze: true)
|
718
|
-
expect(roundtrip).to be interned_str
|
719
|
-
end
|
720
|
-
end
|
721
|
-
|
722
|
-
it 'can freeze objects when using .unpack' do
|
723
|
-
parsed_struct = MessagePack.unpack(buffer, freeze: true)
|
724
|
-
parsed_struct.should == struct
|
725
|
-
|
726
|
-
parsed_struct.should be_frozen
|
727
|
-
parsed_struct['hello'].should be_frozen
|
728
|
-
parsed_struct['nested'].should be_frozen
|
729
|
-
parsed_struct['nested'][0].should be_frozen
|
730
|
-
parsed_struct['nested'][1].should be_frozen
|
731
|
-
|
732
|
-
if string_deduplication?
|
733
|
-
parsed_struct.keys[0].should be_equal('hello'.freeze)
|
734
|
-
parsed_struct.keys[1].should be_equal('nested'.freeze)
|
735
|
-
parsed_struct.values[0].should be_equal('world'.freeze)
|
736
|
-
parsed_struct.values[1][0].should be_equal('object'.freeze)
|
737
|
-
parsed_struct.values[1][1].keys[0].should be_equal('structure'.freeze)
|
738
|
-
end
|
739
|
-
end
|
740
|
-
|
741
|
-
it 'can freeze objects when using #each' do
|
742
|
-
objs = []
|
743
|
-
unpacker.feed(buffer)
|
744
|
-
unpacker.each do |obj|
|
745
|
-
objs << obj
|
746
|
-
end
|
747
|
-
|
748
|
-
parsed_struct = objs.first
|
749
|
-
parsed_struct.should == struct
|
750
|
-
|
751
|
-
parsed_struct.should be_frozen
|
752
|
-
parsed_struct['hello'].should be_frozen
|
753
|
-
parsed_struct['nested'].should be_frozen
|
754
|
-
parsed_struct['nested'][0].should be_frozen
|
755
|
-
parsed_struct['nested'][1].should be_frozen
|
756
|
-
|
757
|
-
if string_deduplication?
|
758
|
-
parsed_struct.keys[0].should be_equal('hello'.freeze)
|
759
|
-
parsed_struct.keys[1].should be_equal('nested'.freeze)
|
760
|
-
parsed_struct.values[0].should be_equal('world'.freeze)
|
761
|
-
parsed_struct.values[1][0].should be_equal('object'.freeze)
|
762
|
-
parsed_struct.values[1][1].keys[0].should be_equal('structure'.freeze)
|
763
|
-
end
|
764
|
-
end
|
765
|
-
|
766
|
-
it 'can freeze objects when using #feed_each' do
|
767
|
-
objs = []
|
768
|
-
unpacker.feed_each(buffer) do |obj|
|
769
|
-
objs << obj
|
770
|
-
end
|
771
|
-
|
772
|
-
parsed_struct = objs.first
|
773
|
-
parsed_struct.should == struct
|
774
|
-
|
775
|
-
parsed_struct.should be_frozen
|
776
|
-
parsed_struct['hello'].should be_frozen
|
777
|
-
parsed_struct['nested'].should be_frozen
|
778
|
-
parsed_struct['nested'][0].should be_frozen
|
779
|
-
parsed_struct['nested'][1].should be_frozen
|
780
|
-
|
781
|
-
if string_deduplication?
|
782
|
-
parsed_struct.keys[0].should be_equal('hello'.freeze)
|
783
|
-
parsed_struct.keys[1].should be_equal('nested'.freeze)
|
784
|
-
parsed_struct.values[0].should be_equal('world'.freeze)
|
785
|
-
parsed_struct.values[1][0].should be_equal('object'.freeze)
|
786
|
-
parsed_struct.values[1][1].keys[0].should be_equal('structure'.freeze)
|
787
|
-
end
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
context 'binary encoding', :encodings do
|
792
|
-
let :buffer do
|
793
|
-
MessagePack.pack({
|
794
|
-
'hello'.b => 'world'.b,
|
795
|
-
'nested'.b => [
|
796
|
-
'object'.b,
|
797
|
-
{'structure'.b => true},
|
798
|
-
]
|
799
|
-
})
|
800
|
-
end
|
801
|
-
|
802
|
-
let :unpacker do
|
803
|
-
described_class.new()
|
804
|
-
end
|
805
|
-
|
806
|
-
it 'decodes binary as ascii-8bit when using #feed' do
|
807
|
-
objs = []
|
808
|
-
unpacker.feed(buffer)
|
809
|
-
unpacker.each do |obj|
|
810
|
-
objs << obj
|
811
|
-
end
|
812
|
-
strings = flatten(objs).grep(String)
|
813
|
-
strings.should == %w[hello world nested object structure]
|
814
|
-
strings.map(&:encoding).uniq.should == [Encoding::ASCII_8BIT]
|
815
|
-
end
|
816
|
-
|
817
|
-
it 'decodes binary as ascii-8bit when using #feed_each' do
|
818
|
-
objs = []
|
819
|
-
unpacker.feed_each(buffer) do |obj|
|
820
|
-
objs << obj
|
821
|
-
end
|
822
|
-
strings = flatten(objs).grep(String)
|
823
|
-
strings.should == %w[hello world nested object structure]
|
824
|
-
strings.map(&:encoding).uniq.should == [Encoding::ASCII_8BIT]
|
825
|
-
end
|
826
|
-
end
|
827
|
-
|
828
|
-
context 'string encoding', :encodings do
|
829
|
-
let :buffer do
|
830
|
-
MessagePack.pack({'hello'.force_encoding(Encoding::UTF_8) => 'world'.force_encoding(Encoding::UTF_8), 'nested'.force_encoding(Encoding::UTF_8) => ['object'.force_encoding(Encoding::UTF_8), {'structure'.force_encoding(Encoding::UTF_8) => true}]})
|
831
|
-
end
|
832
|
-
|
833
|
-
let :unpacker do
|
834
|
-
described_class.new()
|
835
|
-
end
|
836
|
-
|
837
|
-
it 'decodes string as utf-8 when using #feed' do
|
838
|
-
objs = []
|
839
|
-
unpacker.feed(buffer)
|
840
|
-
unpacker.each do |obj|
|
841
|
-
objs << obj
|
842
|
-
end
|
843
|
-
strings = flatten(objs).grep(String)
|
844
|
-
strings.should == %w[hello world nested object structure]
|
845
|
-
strings.map(&:encoding).uniq.should == [Encoding::UTF_8]
|
846
|
-
end
|
847
|
-
|
848
|
-
it 'decodes binary as ascii-8bit when using #feed_each' do
|
849
|
-
objs = []
|
850
|
-
unpacker.feed_each(buffer) do |obj|
|
851
|
-
objs << obj
|
852
|
-
end
|
853
|
-
strings = flatten(objs).grep(String)
|
854
|
-
strings.should == %w[hello world nested object structure]
|
855
|
-
strings.map(&:encoding).uniq.should == [Encoding::UTF_8]
|
856
|
-
end
|
857
|
-
end
|
858
|
-
end
|
859
|
-
end
|