jbangert-bindata 1.5.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.
- data/.gitignore +1 -0
- data/BSDL +22 -0
- data/COPYING +52 -0
- data/ChangeLog.rdoc +204 -0
- data/Gemfile +2 -0
- data/INSTALL +11 -0
- data/NEWS.rdoc +164 -0
- data/README.md +54 -0
- data/Rakefile +13 -0
- data/bindata.gemspec +31 -0
- data/doc/manual.haml +407 -0
- data/doc/manual.md +1649 -0
- data/examples/NBT.txt +149 -0
- data/examples/gzip.rb +161 -0
- data/examples/ip_address.rb +22 -0
- data/examples/list.rb +124 -0
- data/examples/nbt.rb +178 -0
- data/lib/bindata.rb +33 -0
- data/lib/bindata/alignment.rb +83 -0
- data/lib/bindata/array.rb +335 -0
- data/lib/bindata/base.rb +388 -0
- data/lib/bindata/base_primitive.rb +214 -0
- data/lib/bindata/bits.rb +87 -0
- data/lib/bindata/choice.rb +216 -0
- data/lib/bindata/count_bytes_remaining.rb +35 -0
- data/lib/bindata/deprecated.rb +50 -0
- data/lib/bindata/dsl.rb +312 -0
- data/lib/bindata/float.rb +80 -0
- data/lib/bindata/int.rb +184 -0
- data/lib/bindata/io.rb +274 -0
- data/lib/bindata/lazy.rb +105 -0
- data/lib/bindata/offset.rb +91 -0
- data/lib/bindata/params.rb +135 -0
- data/lib/bindata/primitive.rb +135 -0
- data/lib/bindata/record.rb +110 -0
- data/lib/bindata/registry.rb +92 -0
- data/lib/bindata/rest.rb +35 -0
- data/lib/bindata/sanitize.rb +290 -0
- data/lib/bindata/skip.rb +48 -0
- data/lib/bindata/string.rb +145 -0
- data/lib/bindata/stringz.rb +96 -0
- data/lib/bindata/struct.rb +388 -0
- data/lib/bindata/trace.rb +94 -0
- data/lib/bindata/version.rb +3 -0
- data/setup.rb +1585 -0
- data/spec/alignment_spec.rb +61 -0
- data/spec/array_spec.rb +331 -0
- data/spec/base_primitive_spec.rb +238 -0
- data/spec/base_spec.rb +376 -0
- data/spec/bits_spec.rb +163 -0
- data/spec/choice_spec.rb +263 -0
- data/spec/count_bytes_remaining_spec.rb +38 -0
- data/spec/deprecated_spec.rb +31 -0
- data/spec/example.rb +21 -0
- data/spec/float_spec.rb +37 -0
- data/spec/int_spec.rb +216 -0
- data/spec/io_spec.rb +352 -0
- data/spec/lazy_spec.rb +217 -0
- data/spec/primitive_spec.rb +202 -0
- data/spec/record_spec.rb +530 -0
- data/spec/registry_spec.rb +108 -0
- data/spec/rest_spec.rb +26 -0
- data/spec/skip_spec.rb +27 -0
- data/spec/spec_common.rb +58 -0
- data/spec/string_spec.rb +300 -0
- data/spec/stringz_spec.rb +118 -0
- data/spec/struct_spec.rb +350 -0
- data/spec/system_spec.rb +380 -0
- data/tasks/manual.rake +36 -0
- data/tasks/rspec.rake +17 -0
- metadata +208 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
|
4
|
+
require 'bindata/stringz'
|
5
|
+
|
6
|
+
describe BinData::Stringz, "when empty" do
|
7
|
+
its(:value) { should == "" }
|
8
|
+
its(:num_bytes) { should == 1 }
|
9
|
+
its(:to_binary_s) { should == "\0" }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe BinData::Stringz, "with value set" do
|
13
|
+
subject { BinData::Stringz.new("abcd") }
|
14
|
+
|
15
|
+
its(:value) { should == "abcd" }
|
16
|
+
its(:num_bytes) { should == 5 }
|
17
|
+
its(:to_binary_s) { should == "abcd\0" }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe BinData::Stringz, "when reading" do
|
21
|
+
it "stops at the first zero byte" do
|
22
|
+
io = StringIO.new("abcd\0xyz\0")
|
23
|
+
subject.read(io)
|
24
|
+
io.pos.should == 5
|
25
|
+
subject.should == "abcd"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "handles a zero length string" do
|
29
|
+
io = StringIO.new("\0abcd")
|
30
|
+
subject.read(io)
|
31
|
+
io.pos.should == 1
|
32
|
+
subject.should == ""
|
33
|
+
end
|
34
|
+
|
35
|
+
it "fails if no zero byte is found" do
|
36
|
+
expect {subject.read("abcd") }.to raise_error(EOFError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe BinData::Stringz, "when setting the value" do
|
41
|
+
it "includes the zero byte in num_bytes total" do
|
42
|
+
subject.assign("abcd")
|
43
|
+
subject.num_bytes.should == 5
|
44
|
+
end
|
45
|
+
|
46
|
+
it "accepts empty strings" do
|
47
|
+
subject.assign("")
|
48
|
+
subject.should == ""
|
49
|
+
end
|
50
|
+
|
51
|
+
it "accepts strings that aren't zero terminated" do
|
52
|
+
subject.assign("abcd")
|
53
|
+
subject.should == "abcd"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "accepts strings that are zero terminated" do
|
57
|
+
subject.assign("abcd\0")
|
58
|
+
subject.should == "abcd"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "accepts up to the first zero byte" do
|
62
|
+
subject.assign("abcd\0xyz\0")
|
63
|
+
subject.should == "abcd"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe BinData::Stringz, "with max_length" do
|
68
|
+
subject { BinData::Stringz.new(:max_length => 5) }
|
69
|
+
|
70
|
+
it "reads less than max_length" do
|
71
|
+
io = StringIO.new("abc\0xyz")
|
72
|
+
subject.read(io)
|
73
|
+
subject.should == "abc"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "reads exactly max_length" do
|
77
|
+
io = StringIO.new("abcd\0xyz")
|
78
|
+
subject.read(io)
|
79
|
+
subject.should == "abcd"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "reads no more than max_length" do
|
83
|
+
io = StringIO.new("abcdefg\0xyz")
|
84
|
+
subject.read(io)
|
85
|
+
io.pos.should == 5
|
86
|
+
subject.should == "abcd"
|
87
|
+
end
|
88
|
+
|
89
|
+
it "accepts values less than max_length" do
|
90
|
+
subject.assign("abc")
|
91
|
+
subject.should == "abc"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "accepts values exactly max_length" do
|
95
|
+
subject.assign("abcd")
|
96
|
+
subject.should == "abcd"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "trims values greater than max_length" do
|
100
|
+
subject.assign("abcde")
|
101
|
+
subject.should == "abcd"
|
102
|
+
end
|
103
|
+
|
104
|
+
it "writes values greater than max_length" do
|
105
|
+
subject.assign("abcde")
|
106
|
+
subject.to_binary_s.should == "abcd\0"
|
107
|
+
end
|
108
|
+
|
109
|
+
it "writes values less than max_length" do
|
110
|
+
subject.assign("abc")
|
111
|
+
subject.to_binary_s.should == "abc\0"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "writes values exactly max_length" do
|
115
|
+
subject.assign("abcd")
|
116
|
+
subject.to_binary_s.should == "abcd\0"
|
117
|
+
end
|
118
|
+
end
|
data/spec/struct_spec.rb
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
|
4
|
+
require 'bindata'
|
5
|
+
|
6
|
+
describe BinData::Struct, "when initializing" do
|
7
|
+
it "fails on non registered types" do
|
8
|
+
params = {:fields => [[:non_registered_type, :a]]}
|
9
|
+
expect {
|
10
|
+
BinData::Struct.new(params)
|
11
|
+
}.to raise_error(BinData::UnRegisteredTypeError)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "fails on duplicate names" do
|
15
|
+
params = {:fields => [[:int8, :a], [:int8, :b], [:int8, :a]]}
|
16
|
+
expect {
|
17
|
+
BinData::Struct.new(params)
|
18
|
+
}.to raise_error(NameError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "fails on reserved names" do
|
22
|
+
# note that #invert is from Hash.instance_methods
|
23
|
+
params = {:fields => [[:int8, :a], [:int8, :invert]]}
|
24
|
+
expect {
|
25
|
+
BinData::Struct.new(params)
|
26
|
+
}.to raise_error(NameError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "fails when field name shadows an existing method" do
|
30
|
+
params = {:fields => [[:int8, :object_id]]}
|
31
|
+
expect {
|
32
|
+
BinData::Struct.new(params)
|
33
|
+
}.to raise_error(NameError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "fails on unknown endian" do
|
37
|
+
params = {:endian => 'bad value', :fields => []}
|
38
|
+
expect {
|
39
|
+
BinData::Struct.new(params)
|
40
|
+
}.to raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe BinData::Struct, "with anonymous fields" do
|
45
|
+
subject {
|
46
|
+
params = { :fields => [
|
47
|
+
[:int8, :a, {:initial_value => 5}],
|
48
|
+
[:int8, nil],
|
49
|
+
[:int8, '', {:value => :a}]
|
50
|
+
] }
|
51
|
+
BinData::Struct.new(params)
|
52
|
+
}
|
53
|
+
|
54
|
+
it "only shows non anonymous fields" do
|
55
|
+
subject.field_names.should == ["a"]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "does not include anonymous fields in snapshot" do
|
59
|
+
subject.a = 5
|
60
|
+
subject.snapshot.should == {"a" => 5}
|
61
|
+
end
|
62
|
+
|
63
|
+
it "writes anonymous fields" do
|
64
|
+
subject.read("\001\002\003")
|
65
|
+
subject.a.clear
|
66
|
+
subject.to_binary_s.should == "\005\002\005"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe BinData::Struct, "with hidden fields" do
|
71
|
+
subject {
|
72
|
+
params = { :hide => [:b, :c],
|
73
|
+
:fields => [
|
74
|
+
[:int8, :a],
|
75
|
+
[:int8, 'b', {:initial_value => 5}],
|
76
|
+
[:int8, :c],
|
77
|
+
[:int8, :d, {:value => :b}]] }
|
78
|
+
BinData::Struct.new(params)
|
79
|
+
}
|
80
|
+
|
81
|
+
it "only shows fields that aren't hidden" do
|
82
|
+
subject.field_names.should == ["a", "d"]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "accesses hidden fields directly" do
|
86
|
+
subject.b.should == 5
|
87
|
+
subject.c = 15
|
88
|
+
subject.c.should == 15
|
89
|
+
|
90
|
+
subject.should respond_to(:b=)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "does not include hidden fields in snapshot" do
|
94
|
+
subject.b = 7
|
95
|
+
subject.snapshot.should == {"a" => 0, "d" => 7}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "detects hidden fields with has_key?" do
|
99
|
+
subject.should have_key("b")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe BinData::Struct, "with multiple fields" do
|
104
|
+
let(:params) { { :fields => [ [:int8, :a], [:int8, :b] ] } }
|
105
|
+
subject { BinData::Struct.new({:a => 1, :b => 2}, params) }
|
106
|
+
|
107
|
+
its(:field_names) { should == ["a", "b"] }
|
108
|
+
its(:to_binary_s) { should == "\x01\x02" }
|
109
|
+
|
110
|
+
it "returns num_bytes" do
|
111
|
+
subject.a.num_bytes.should == 1
|
112
|
+
subject.b.num_bytes.should == 1
|
113
|
+
subject.num_bytes.should == 2
|
114
|
+
end
|
115
|
+
|
116
|
+
it "identifies accepted parameters" do
|
117
|
+
BinData::Struct.accepted_parameters.all.should include(:fields)
|
118
|
+
BinData::Struct.accepted_parameters.all.should include(:hide)
|
119
|
+
BinData::Struct.accepted_parameters.all.should include(:endian)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "clears" do
|
123
|
+
subject.a = 6
|
124
|
+
subject.clear
|
125
|
+
subject.should be_clear
|
126
|
+
end
|
127
|
+
|
128
|
+
it "clears individual elements" do
|
129
|
+
subject.a = 6
|
130
|
+
subject.b = 7
|
131
|
+
subject.a.clear
|
132
|
+
subject.a.should be_clear
|
133
|
+
subject.b.should_not be_clear
|
134
|
+
end
|
135
|
+
|
136
|
+
it "reads elements dynamically" do
|
137
|
+
subject[:a].should == 1
|
138
|
+
end
|
139
|
+
|
140
|
+
it "writes elements dynamically" do
|
141
|
+
subject[:a] = 2
|
142
|
+
subject.a.should == 2
|
143
|
+
end
|
144
|
+
|
145
|
+
it "implements has_key?" do
|
146
|
+
subject.should have_key("a")
|
147
|
+
end
|
148
|
+
|
149
|
+
it "reads ordered" do
|
150
|
+
subject.read("\x03\x04")
|
151
|
+
|
152
|
+
subject.a.should == 3
|
153
|
+
subject.b.should == 4
|
154
|
+
end
|
155
|
+
|
156
|
+
it "returns a snapshot" do
|
157
|
+
snap = subject.snapshot
|
158
|
+
snap.a.should == 1
|
159
|
+
snap.b.should == 2
|
160
|
+
snap.should == { "a" => 1, "b" => 2 }
|
161
|
+
end
|
162
|
+
|
163
|
+
it "assigns from partial hash" do
|
164
|
+
subject.assign("a" => 3)
|
165
|
+
subject.a.should == 3
|
166
|
+
subject.b.should == 0
|
167
|
+
end
|
168
|
+
|
169
|
+
it "assigns from hash" do
|
170
|
+
subject.assign("a" => 3, "b" => 4)
|
171
|
+
subject.a.should == 3
|
172
|
+
subject.b.should == 4
|
173
|
+
end
|
174
|
+
|
175
|
+
it "assigns from nil" do
|
176
|
+
subject.assign(nil)
|
177
|
+
subject.should be_clear
|
178
|
+
end
|
179
|
+
|
180
|
+
it "assigns from Struct" do
|
181
|
+
src = BinData::Struct.new(params)
|
182
|
+
src.a = 3
|
183
|
+
src.b = 4
|
184
|
+
|
185
|
+
subject.assign(src)
|
186
|
+
subject.a.should == 3
|
187
|
+
subject.b.should == 4
|
188
|
+
end
|
189
|
+
|
190
|
+
it "assigns from snapshot" do
|
191
|
+
src = BinData::Struct.new(params)
|
192
|
+
src.a = 3
|
193
|
+
src.b = 4
|
194
|
+
|
195
|
+
subject.assign(src.snapshot)
|
196
|
+
subject.a.should == 3
|
197
|
+
subject.b.should == 4
|
198
|
+
end
|
199
|
+
|
200
|
+
it "fails on unknown method call" do
|
201
|
+
expect { subject.does_not_exist }.to raise_error(NoMethodError)
|
202
|
+
end
|
203
|
+
|
204
|
+
context "#snapshot" do
|
205
|
+
it "has ordered #keys" do
|
206
|
+
subject.snapshot.keys.should == ["a", "b"]
|
207
|
+
end
|
208
|
+
|
209
|
+
it "has ordered #each" do
|
210
|
+
keys = []
|
211
|
+
subject.snapshot.each { |el| keys << el[0] }
|
212
|
+
keys.should == ["a", "b"]
|
213
|
+
end
|
214
|
+
|
215
|
+
it "has ordered #each_pair" do
|
216
|
+
keys = []
|
217
|
+
subject.snapshot.each_pair { |k, v| keys << k }
|
218
|
+
keys.should == ["a", "b"]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe BinData::Struct, "with nested structs" do
|
224
|
+
subject {
|
225
|
+
inner1 = [ [:int8, :w, {:initial_value => 3}],
|
226
|
+
[:int8, :x, {:value => :the_val}] ]
|
227
|
+
|
228
|
+
inner2 = [ [:int8, :y, {:value => lambda { parent.b.w }}],
|
229
|
+
[:int8, :z] ]
|
230
|
+
|
231
|
+
params = { :fields => [
|
232
|
+
[:int8, :a, {:initial_value => 6}],
|
233
|
+
[:struct, :b, {:fields => inner1, :the_val => :a}],
|
234
|
+
[:struct, :c, {:fields => inner2}]] }
|
235
|
+
BinData::Struct.new(params)
|
236
|
+
}
|
237
|
+
|
238
|
+
its(:field_names) { should == ["a", "b", "c"] }
|
239
|
+
|
240
|
+
it "returns num_bytes" do
|
241
|
+
subject.b.num_bytes.should == 2
|
242
|
+
subject.c.num_bytes.should == 2
|
243
|
+
subject.num_bytes.should == 5
|
244
|
+
end
|
245
|
+
|
246
|
+
it "accesses nested fields" do
|
247
|
+
subject.a.should == 6
|
248
|
+
subject.b.w.should == 3
|
249
|
+
subject.b.x.should == 6
|
250
|
+
subject.c.y.should == 3
|
251
|
+
subject.c.z.should == 0
|
252
|
+
end
|
253
|
+
|
254
|
+
it "returns correct offset" do
|
255
|
+
subject.b.offset.should == 1
|
256
|
+
subject.b.w.offset.should == 1
|
257
|
+
subject.c.offset.should == 3
|
258
|
+
subject.c.z.offset.should == 4
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe BinData::Struct, "with an endian defined" do
|
263
|
+
subject {
|
264
|
+
BinData::Struct.new(:endian => :little,
|
265
|
+
:fields => [
|
266
|
+
[:uint16, :a],
|
267
|
+
[:float, :b],
|
268
|
+
[:array, :c,
|
269
|
+
{:type => :int8, :initial_length => 2}],
|
270
|
+
[:choice, :d,
|
271
|
+
{:choices => [[:uint16], [:uint32]],
|
272
|
+
:selection => 1}],
|
273
|
+
[:struct, :e,
|
274
|
+
{:fields => [[:uint16, :f],
|
275
|
+
[:uint32be, :g]]}],
|
276
|
+
[:struct, :h,
|
277
|
+
{:fields => [
|
278
|
+
[:struct, :i,
|
279
|
+
{:fields => [[:uint16, :j]]}]]}]])
|
280
|
+
}
|
281
|
+
|
282
|
+
it "uses correct endian" do
|
283
|
+
subject.a = 1
|
284
|
+
subject.b = 2.0
|
285
|
+
subject.c[0] = 3
|
286
|
+
subject.c[1] = 4
|
287
|
+
subject.d = 5
|
288
|
+
subject.e.f = 6
|
289
|
+
subject.e.g = 7
|
290
|
+
subject.h.i.j = 8
|
291
|
+
|
292
|
+
expected = [1, 2.0, 3, 4, 5, 6, 7, 8].pack('veCCVvNv')
|
293
|
+
|
294
|
+
subject.to_binary_s.should == expected
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe BinData::Struct, "with bit fields" do
|
299
|
+
subject {
|
300
|
+
params = { :fields => [ [:bit1le, :a], [:bit2le, :b], [:uint8, :c], [:bit1le, :d] ] }
|
301
|
+
BinData::Struct.new({:a => 1, :b => 2, :c => 3, :d => 1}, params)
|
302
|
+
}
|
303
|
+
|
304
|
+
its(:num_bytes) { should == 3 }
|
305
|
+
its(:to_binary_s) { should == [0b0000_0101, 3, 1].pack("C*") }
|
306
|
+
|
307
|
+
it "reads" do
|
308
|
+
str = [0b0000_0110, 5, 0].pack("C*")
|
309
|
+
subject.read(str)
|
310
|
+
subject.a.should == 0
|
311
|
+
subject.b.should == 3
|
312
|
+
subject.c.should == 5
|
313
|
+
subject.d.should == 0
|
314
|
+
end
|
315
|
+
|
316
|
+
it "has correct offsets" do
|
317
|
+
subject.a.offset.should == 0
|
318
|
+
subject.b.offset.should == 0
|
319
|
+
subject.c.offset.should == 1
|
320
|
+
subject.d.offset.should == 2
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
describe BinData::Struct, "with nested endian" do
|
325
|
+
it "uses correct endian" do
|
326
|
+
nested_params = { :endian => :little,
|
327
|
+
:fields => [[:int16, :b], [:int16, :c]] }
|
328
|
+
params = { :endian => :big,
|
329
|
+
:fields => [[:int16, :a],
|
330
|
+
[:struct, :s, nested_params],
|
331
|
+
[:int16, :d]] }
|
332
|
+
subject = BinData::Struct.new(params)
|
333
|
+
subject.read("\x00\x01\x02\x00\x03\x00\x00\x04")
|
334
|
+
|
335
|
+
subject.a.should == 1
|
336
|
+
subject.s.b.should == 2
|
337
|
+
subject.s.c.should == 3
|
338
|
+
subject.d.should == 4
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
describe BinData::Struct, "with dynamically named types" do
|
343
|
+
it "instantiates" do
|
344
|
+
dyn = BinData::Struct.new(:name => :my_struct, :fields => [[:int8, :a, {:initial_value => 3}]])
|
345
|
+
|
346
|
+
obj = BinData::Struct.new(:fields => [[:my_struct, :v]])
|
347
|
+
|
348
|
+
obj.v.a.should == 3
|
349
|
+
end
|
350
|
+
end
|