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.
Files changed (71) hide show
  1. data/.gitignore +1 -0
  2. data/BSDL +22 -0
  3. data/COPYING +52 -0
  4. data/ChangeLog.rdoc +204 -0
  5. data/Gemfile +2 -0
  6. data/INSTALL +11 -0
  7. data/NEWS.rdoc +164 -0
  8. data/README.md +54 -0
  9. data/Rakefile +13 -0
  10. data/bindata.gemspec +31 -0
  11. data/doc/manual.haml +407 -0
  12. data/doc/manual.md +1649 -0
  13. data/examples/NBT.txt +149 -0
  14. data/examples/gzip.rb +161 -0
  15. data/examples/ip_address.rb +22 -0
  16. data/examples/list.rb +124 -0
  17. data/examples/nbt.rb +178 -0
  18. data/lib/bindata.rb +33 -0
  19. data/lib/bindata/alignment.rb +83 -0
  20. data/lib/bindata/array.rb +335 -0
  21. data/lib/bindata/base.rb +388 -0
  22. data/lib/bindata/base_primitive.rb +214 -0
  23. data/lib/bindata/bits.rb +87 -0
  24. data/lib/bindata/choice.rb +216 -0
  25. data/lib/bindata/count_bytes_remaining.rb +35 -0
  26. data/lib/bindata/deprecated.rb +50 -0
  27. data/lib/bindata/dsl.rb +312 -0
  28. data/lib/bindata/float.rb +80 -0
  29. data/lib/bindata/int.rb +184 -0
  30. data/lib/bindata/io.rb +274 -0
  31. data/lib/bindata/lazy.rb +105 -0
  32. data/lib/bindata/offset.rb +91 -0
  33. data/lib/bindata/params.rb +135 -0
  34. data/lib/bindata/primitive.rb +135 -0
  35. data/lib/bindata/record.rb +110 -0
  36. data/lib/bindata/registry.rb +92 -0
  37. data/lib/bindata/rest.rb +35 -0
  38. data/lib/bindata/sanitize.rb +290 -0
  39. data/lib/bindata/skip.rb +48 -0
  40. data/lib/bindata/string.rb +145 -0
  41. data/lib/bindata/stringz.rb +96 -0
  42. data/lib/bindata/struct.rb +388 -0
  43. data/lib/bindata/trace.rb +94 -0
  44. data/lib/bindata/version.rb +3 -0
  45. data/setup.rb +1585 -0
  46. data/spec/alignment_spec.rb +61 -0
  47. data/spec/array_spec.rb +331 -0
  48. data/spec/base_primitive_spec.rb +238 -0
  49. data/spec/base_spec.rb +376 -0
  50. data/spec/bits_spec.rb +163 -0
  51. data/spec/choice_spec.rb +263 -0
  52. data/spec/count_bytes_remaining_spec.rb +38 -0
  53. data/spec/deprecated_spec.rb +31 -0
  54. data/spec/example.rb +21 -0
  55. data/spec/float_spec.rb +37 -0
  56. data/spec/int_spec.rb +216 -0
  57. data/spec/io_spec.rb +352 -0
  58. data/spec/lazy_spec.rb +217 -0
  59. data/spec/primitive_spec.rb +202 -0
  60. data/spec/record_spec.rb +530 -0
  61. data/spec/registry_spec.rb +108 -0
  62. data/spec/rest_spec.rb +26 -0
  63. data/spec/skip_spec.rb +27 -0
  64. data/spec/spec_common.rb +58 -0
  65. data/spec/string_spec.rb +300 -0
  66. data/spec/stringz_spec.rb +118 -0
  67. data/spec/struct_spec.rb +350 -0
  68. data/spec/system_spec.rb +380 -0
  69. data/tasks/manual.rake +36 -0
  70. data/tasks/rspec.rake +17 -0
  71. 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
@@ -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