msgpack 0.6.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.travis.yml +26 -0
- data/ChangeLog +129 -0
- data/Dockerfile +62 -0
- data/LICENSE +177 -0
- data/README.rdoc +141 -0
- data/Rakefile +68 -0
- data/bench/pack.rb +23 -0
- data/bench/pack_log.rb +33 -0
- data/bench/pack_log_long.rb +65 -0
- data/bench/run.sh +14 -0
- data/bench/run_long.sh +35 -0
- data/bench/unpack.rb +21 -0
- data/bench/unpack_log.rb +34 -0
- data/bench/unpack_log_long.rb +67 -0
- data/doclib/msgpack.rb +77 -0
- data/doclib/msgpack/buffer.rb +193 -0
- data/doclib/msgpack/core_ext.rb +101 -0
- data/doclib/msgpack/error.rb +14 -0
- data/doclib/msgpack/packer.rb +134 -0
- data/doclib/msgpack/unpacker.rb +146 -0
- data/ext/java/org/msgpack/jruby/Buffer.java +221 -0
- data/ext/java/org/msgpack/jruby/Decoder.java +201 -0
- data/ext/java/org/msgpack/jruby/Encoder.java +308 -0
- data/ext/java/org/msgpack/jruby/ExtensionValue.java +136 -0
- data/ext/java/org/msgpack/jruby/MessagePackLibrary.java +107 -0
- data/ext/java/org/msgpack/jruby/Packer.java +78 -0
- data/ext/java/org/msgpack/jruby/Types.java +37 -0
- data/ext/java/org/msgpack/jruby/Unpacker.java +170 -0
- data/ext/msgpack/buffer.c +695 -0
- data/ext/msgpack/buffer.h +447 -0
- data/ext/msgpack/buffer_class.c +507 -0
- data/ext/msgpack/buffer_class.h +32 -0
- data/ext/msgpack/compat.h +114 -0
- data/ext/msgpack/core_ext.c +129 -0
- data/ext/msgpack/core_ext.h +26 -0
- data/ext/msgpack/extconf.rb +30 -0
- data/ext/msgpack/packer.c +168 -0
- data/ext/msgpack/packer.h +441 -0
- data/ext/msgpack/packer_class.c +302 -0
- data/ext/msgpack/packer_class.h +30 -0
- data/ext/msgpack/rbinit.c +33 -0
- data/ext/msgpack/rmem.c +94 -0
- data/ext/msgpack/rmem.h +109 -0
- data/ext/msgpack/sysdep.h +115 -0
- data/ext/msgpack/sysdep_endian.h +50 -0
- data/ext/msgpack/sysdep_types.h +46 -0
- data/ext/msgpack/unpacker.c +771 -0
- data/ext/msgpack/unpacker.h +122 -0
- data/ext/msgpack/unpacker_class.c +405 -0
- data/ext/msgpack/unpacker_class.h +32 -0
- data/lib/msgpack.rb +13 -0
- data/lib/msgpack/version.rb +3 -0
- data/msgpack.gemspec +31 -0
- data/msgpack.org.md +46 -0
- data/spec/cases.json +1 -0
- data/spec/cases.msg +0 -0
- data/spec/cases_compact.msg +0 -0
- data/spec/cases_spec.rb +39 -0
- data/spec/cruby/buffer_io_spec.rb +256 -0
- data/spec/cruby/buffer_packer.rb +29 -0
- data/spec/cruby/buffer_spec.rb +572 -0
- data/spec/cruby/buffer_unpacker.rb +19 -0
- data/spec/cruby/packer_spec.rb +120 -0
- data/spec/cruby/unpacker_spec.rb +305 -0
- data/spec/format_spec.rb +282 -0
- data/spec/jruby/benchmarks/shootout_bm.rb +73 -0
- data/spec/jruby/benchmarks/symbolize_keys_bm.rb +25 -0
- data/spec/jruby/msgpack/unpacker_spec.rb +290 -0
- data/spec/jruby/msgpack_spec.rb +142 -0
- data/spec/pack_spec.rb +67 -0
- data/spec/random_compat.rb +24 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/unpack_spec.rb +60 -0
- metadata +208 -0
@@ -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,142 @@
|
|
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 'does not symbolize keys even if other options are present' do
|
126
|
+
packed = MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]})
|
127
|
+
unpacked = MessagePack.unpack(packed, :other_option => false)
|
128
|
+
unpacked.should == {'hello' => 'world', 'nested' => ['object', {'structure' => true}]}
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'can unpack strings with a specified encoding', :encodings do
|
132
|
+
packed = MessagePack.pack({utf8enc('hello') => utf8enc('world')})
|
133
|
+
unpacked = MessagePack.unpack(packed)
|
134
|
+
unpacked['hello'].encoding.should == Encoding::UTF_8
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'can pack strings with a specified encoding', :encodings do
|
138
|
+
packed = MessagePack.pack({'hello' => "w\xE5rld".force_encoding(Encoding::ISO_8859_1)})
|
139
|
+
packed.index("w\xC3\xA5rld").should_not be_nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|