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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +50 -0
  3. data/README.md +48 -12
  4. data/ext/java/org/msgpack/jruby/Buffer.java +3 -3
  5. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +11 -20
  6. data/ext/java/org/msgpack/jruby/ExtensionValue.java +1 -1
  7. data/ext/java/org/msgpack/jruby/Factory.java +11 -50
  8. data/ext/java/org/msgpack/jruby/Packer.java +9 -24
  9. data/ext/java/org/msgpack/jruby/Unpacker.java +15 -32
  10. data/ext/msgpack/buffer.c +69 -56
  11. data/ext/msgpack/buffer.h +138 -44
  12. data/ext/msgpack/buffer_class.c +132 -31
  13. data/ext/msgpack/buffer_class.h +1 -0
  14. data/ext/msgpack/extconf.rb +20 -30
  15. data/ext/msgpack/factory_class.c +75 -86
  16. data/ext/msgpack/packer.c +13 -16
  17. data/ext/msgpack/packer.h +24 -21
  18. data/ext/msgpack/packer_class.c +72 -98
  19. data/ext/msgpack/packer_class.h +11 -0
  20. data/ext/msgpack/packer_ext_registry.c +31 -28
  21. data/ext/msgpack/packer_ext_registry.h +10 -14
  22. data/ext/msgpack/rbinit.c +1 -1
  23. data/ext/msgpack/rmem.c +3 -4
  24. data/ext/msgpack/sysdep.h +5 -2
  25. data/ext/msgpack/unpacker.c +201 -113
  26. data/ext/msgpack/unpacker.h +22 -15
  27. data/ext/msgpack/unpacker_class.c +87 -92
  28. data/ext/msgpack/unpacker_class.h +11 -0
  29. data/ext/msgpack/unpacker_ext_registry.c +4 -16
  30. data/ext/msgpack/unpacker_ext_registry.h +3 -7
  31. data/lib/msgpack/buffer.rb +9 -0
  32. data/lib/msgpack/factory.rb +90 -63
  33. data/lib/msgpack/packer.rb +10 -1
  34. data/lib/msgpack/unpacker.rb +14 -1
  35. data/lib/msgpack/version.rb +1 -1
  36. data/lib/msgpack.rb +1 -0
  37. data/msgpack.gemspec +8 -3
  38. metadata +21 -51
  39. data/.github/workflows/ci.yaml +0 -57
  40. data/.gitignore +0 -23
  41. data/.rubocop.yml +0 -36
  42. data/Gemfile +0 -9
  43. data/Rakefile +0 -70
  44. data/appveyor.yml +0 -18
  45. data/bench/bench.rb +0 -78
  46. data/doclib/msgpack/buffer.rb +0 -193
  47. data/doclib/msgpack/core_ext.rb +0 -101
  48. data/doclib/msgpack/error.rb +0 -19
  49. data/doclib/msgpack/extension_value.rb +0 -9
  50. data/doclib/msgpack/factory.rb +0 -145
  51. data/doclib/msgpack/packer.rb +0 -209
  52. data/doclib/msgpack/time.rb +0 -22
  53. data/doclib/msgpack/timestamp.rb +0 -44
  54. data/doclib/msgpack/unpacker.rb +0 -183
  55. data/doclib/msgpack.rb +0 -87
  56. data/msgpack.org.md +0 -46
  57. data/spec/bigint_spec.rb +0 -26
  58. data/spec/cases.json +0 -1
  59. data/spec/cases.msg +0 -0
  60. data/spec/cases_compact.msg +0 -0
  61. data/spec/cases_spec.rb +0 -39
  62. data/spec/cruby/buffer_io_spec.rb +0 -255
  63. data/spec/cruby/buffer_packer.rb +0 -29
  64. data/spec/cruby/buffer_spec.rb +0 -575
  65. data/spec/cruby/buffer_unpacker.rb +0 -19
  66. data/spec/cruby/unpacker_spec.rb +0 -70
  67. data/spec/ext_value_spec.rb +0 -99
  68. data/spec/exttypes.rb +0 -51
  69. data/spec/factory_spec.rb +0 -688
  70. data/spec/format_spec.rb +0 -301
  71. data/spec/jruby/benchmarks/shootout_bm.rb +0 -73
  72. data/spec/jruby/benchmarks/symbolize_keys_bm.rb +0 -25
  73. data/spec/jruby/unpacker_spec.rb +0 -186
  74. data/spec/msgpack_spec.rb +0 -214
  75. data/spec/pack_spec.rb +0 -61
  76. data/spec/packer_spec.rb +0 -575
  77. data/spec/random_compat.rb +0 -24
  78. data/spec/spec_helper.rb +0 -71
  79. data/spec/timestamp_spec.rb +0 -159
  80. data/spec/unpack_spec.rb +0 -57
  81. data/spec/unpacker_spec.rb +0 -859
@@ -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