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,263 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "example"))
5
+ require 'bindata/choice'
6
+ require 'bindata/int'
7
+
8
+ class Chooser
9
+ attr_accessor :choice
10
+ end
11
+
12
+ class BinData::Choice
13
+ def set_chooser(chooser)
14
+ @chooser = chooser
15
+ end
16
+ def choice=(s)
17
+ @chooser.choice = s
18
+ end
19
+ end
20
+
21
+ def create_choice(choices, options = {})
22
+ chooser = Chooser.new
23
+ params = {:choices => choices, :selection => lambda { chooser.choice } }.merge(options)
24
+ choice = BinData::Choice.new(params)
25
+ choice.set_chooser(chooser)
26
+ choice
27
+ end
28
+
29
+ describe BinData::Choice, "when instantiating" do
30
+ it "ensures mandatory parameters are supplied" do
31
+ args = {}
32
+ expect { BinData::Choice.new(args) }.to raise_error(ArgumentError)
33
+
34
+ args = {:selection => 1}
35
+ expect { BinData::Choice.new(args) }.to raise_error(ArgumentError)
36
+
37
+ args = {:choices => []}
38
+ expect { BinData::Choice.new(args) }.to raise_error(ArgumentError)
39
+ end
40
+
41
+ it "fails when a given type is unknown" do
42
+ args = {:choices => [:does_not_exist], :selection => 0}
43
+ expect { BinData::Choice.new(args) }.to raise_error(BinData::UnRegisteredTypeError)
44
+ end
45
+
46
+ it "fails when a given type is unknown" do
47
+ args = {:choices => {0 => :does_not_exist}, :selection => 0}
48
+ expect { BinData::Choice.new(args) }.to raise_error(BinData::UnRegisteredTypeError)
49
+ end
50
+
51
+ it "fails when :choices Hash has a symbol as key" do
52
+ args = {:choices => {:a => :example_single}, :selection => 0}
53
+ expect { BinData::Choice.new(args) }.to raise_error(ArgumentError)
54
+ end
55
+
56
+ it "fails when :choices Hash has a nil key" do
57
+ args = {:choices => {nil => :example_single}, :selection => 0}
58
+ expect { BinData::Choice.new(args) }.to raise_error(ArgumentError)
59
+ end
60
+ end
61
+
62
+ shared_examples "Choice initialized with array or hash" do
63
+ it "can select the choice" do
64
+ subject.choice = 3
65
+ subject.should == 30
66
+ end
67
+
68
+ it "shows the current selection" do
69
+ subject.choice = 3
70
+ subject.selection.should == 3
71
+ end
72
+
73
+ it "forwards #snapshot" do
74
+ subject.choice = 3
75
+ subject.snapshot.should == 30
76
+ end
77
+
78
+ it "can change the choice" do
79
+ subject.choice = 3
80
+
81
+ subject.choice = 7
82
+ subject.should == 70
83
+ end
84
+
85
+ it "fails if no choice has been set" do
86
+ expect { subject.to_s }.to raise_error(IndexError)
87
+ end
88
+
89
+ it "will not select an invalid choice" do
90
+ subject.choice = 99
91
+ expect { subject.to_s }.to raise_error(IndexError)
92
+ end
93
+
94
+ it "will not select a nil choice" do
95
+ subject.choice = 1
96
+ expect { subject.to_s }.to raise_error(IndexError)
97
+ end
98
+
99
+ it "handles missing methods correctly" do
100
+ subject.choice = 3
101
+
102
+ subject.should respond_to(:value)
103
+ subject.should_not respond_to(:does_not_exist)
104
+ expect { subject.does_not_exist }.to raise_error(NoMethodError)
105
+ end
106
+
107
+ it "delegates methods to the selected single choice" do
108
+ subject.choice = 5
109
+ subject.num_bytes.should == ExampleSingle.new.num_bytes
110
+ end
111
+ end
112
+
113
+ describe BinData::Choice, "with sparse choices array" do
114
+ include_examples "Choice initialized with array or hash"
115
+
116
+ subject {
117
+ choices = [nil, nil, nil,
118
+ [:example_single, {:value => 30}], nil,
119
+ [:example_single, {:value => 50}], nil,
120
+ [:example_single, {:value => 70}]]
121
+ create_choice(choices)
122
+ }
123
+ end
124
+
125
+ describe BinData::Choice, "with choices hash" do
126
+ include_examples "Choice initialized with array or hash"
127
+
128
+ subject {
129
+ choices = {3 => [:example_single, {:value => 30}],
130
+ 5 => [:example_single, {:value => 50}],
131
+ 7 => [:example_single, {:value => 70}]}
132
+ create_choice(choices)
133
+ }
134
+ end
135
+
136
+ describe BinData::Choice, "with single values" do
137
+ subject {
138
+ choices = {3 => :example_single,
139
+ 5 => :example_single,
140
+ 7 => :example_single}
141
+ create_choice(choices)
142
+ }
143
+
144
+ it "assigns raw values" do
145
+ subject.choice = 3
146
+ subject.assign(254)
147
+ subject.should == 254
148
+ end
149
+
150
+ it "assigns Single values" do
151
+ obj = ExampleSingle.new(11)
152
+
153
+ subject.choice = 3
154
+ subject.assign(obj)
155
+ subject.should == 11
156
+ end
157
+
158
+ it "clears" do
159
+ subject.choice = 3
160
+ subject.assign(254)
161
+
162
+ subject.clear
163
+ subject.should be_zero
164
+ end
165
+
166
+ it "clears all possible choices" do
167
+ subject.choice = 3
168
+ subject.assign(10)
169
+ subject.choice = 5
170
+ subject.assign(11)
171
+
172
+ subject.clear
173
+
174
+ subject.choice = 3
175
+ subject.should be_zero
176
+ end
177
+
178
+ it "is clear on initialisation" do
179
+ subject.choice = 3
180
+
181
+ subject.should be_clear
182
+ end
183
+
184
+ it "is not clear after assignment" do
185
+ subject.choice = 3
186
+ subject.assign(254)
187
+
188
+ subject.should_not be_clear
189
+ end
190
+
191
+ it "does not copy value when changing selection" do
192
+ subject.choice = 3
193
+ subject.assign(254)
194
+
195
+ subject.choice = 7
196
+ subject.should_not == 254
197
+ end
198
+
199
+ it "behaves as value" do
200
+ subject.choice = 3
201
+ subject.assign(5)
202
+
203
+ (subject + 1).should == 6
204
+ (1 + subject).should == 6
205
+ end
206
+ end
207
+
208
+ describe BinData::Choice, "with copy_on_change => true" do
209
+ subject {
210
+ choices = {3 => :example_single,
211
+ 5 => :example_single,
212
+ 7 => :example_single}
213
+ create_choice(choices, :copy_on_change => true)
214
+ }
215
+
216
+ it "copies value when changing selection" do
217
+ subject.choice = 3
218
+ subject.assign(254)
219
+
220
+ subject.choice = 7
221
+ subject.should == 254
222
+ end
223
+ end
224
+
225
+ describe BinData::Choice, "with :default" do
226
+ let(:choices) { { "a" => :int8, :default => :int16be } }
227
+
228
+ it "selects for existing case" do
229
+ subject = BinData::Choice.new(:selection => "a", :choices => choices)
230
+ subject.num_bytes.should == 1
231
+ end
232
+
233
+ it "selects for default case" do
234
+ subject = BinData::Choice.new(:selection => "other", :choices => choices)
235
+ subject.num_bytes.should == 2
236
+ end
237
+ end
238
+
239
+ describe BinData::Choice, "subclassed with default parameters" do
240
+ class DerivedChoice < BinData::Choice
241
+ endian :big
242
+ default_parameter :selection => 'a'
243
+
244
+ uint16 'a'
245
+ uint32 'b'
246
+ uint64 :default
247
+ end
248
+
249
+ it "sets initial selection" do
250
+ subject = DerivedChoice.new
251
+ subject.num_bytes.should == 2
252
+ end
253
+
254
+ it "overides default parameter" do
255
+ subject = DerivedChoice.new(:selection => 'b')
256
+ subject.num_bytes.should == 4
257
+ end
258
+
259
+ it "selects default selection" do
260
+ subject = DerivedChoice.new(:selection => 'z')
261
+ subject.num_bytes.should == 8
262
+ end
263
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/count_bytes_remaining'
5
+
6
+ describe BinData::CountBytesRemaining do
7
+ it { should == 0 }
8
+ its(:num_bytes) { should be_zero }
9
+
10
+ it "counts till end of stream" do
11
+ data = "abcdefghij"
12
+ subject.read(data).should == 10
13
+ end
14
+
15
+ it "does not read any data" do
16
+ io = StringIO.new "abcdefghij"
17
+ subject.read(io)
18
+
19
+ io.pos.should == 0
20
+ end
21
+
22
+ it "does not write any data" do
23
+ subject.to_binary_s.should == ""
24
+ end
25
+
26
+ it "allows setting value for completeness" do
27
+ subject.assign("123")
28
+ subject.should == "123"
29
+ subject.to_binary_s.should == ""
30
+ end
31
+
32
+ it "accepts BinData::BasePrimitive parameters" do
33
+ count = BinData::CountBytesRemaining.new(:check_value => 2)
34
+ expect {
35
+ count.read("xyz")
36
+ }.to raise_error(BinData::ValidityError)
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "example"))
5
+ require 'bindata'
6
+
7
+ describe BinData::Base, "when defining" do
8
+ it "fails if #initialize is overridden" do
9
+ class BaseWithInitialize < BinData::Base
10
+ def initialize(params = {}, parent = nil)
11
+ super
12
+ end
13
+ end
14
+
15
+ expect {
16
+ BaseWithInitialize.new
17
+ }.to raise_error
18
+ end
19
+
20
+ it "handles if #initialize is naively renamed to #initialize_instance" do
21
+ class BaseWithInitializeInstance < BinData::Base
22
+ def initialize_instance(params = {}, parent = nil)
23
+ super
24
+ end
25
+ end
26
+
27
+ expect {
28
+ BaseWithInitializeInstance.new
29
+ }.to raise_error
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ require 'bindata/base_primitive'
2
+
3
+ class ExampleSingle < BinData::BasePrimitive
4
+ def self.io_with_value(val)
5
+ BinData::IO.new([val].pack("V"))
6
+ end
7
+
8
+ private
9
+
10
+ def value_to_binary_string(val)
11
+ [val].pack("V")
12
+ end
13
+
14
+ def read_and_return_value(io)
15
+ io.readbytes(4).unpack("V").at(0)
16
+ end
17
+
18
+ def sensible_default
19
+ 0
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/float'
5
+
6
+ describe "A FloatLe" do
7
+ subject { BinData::FloatLe.new(Math::PI) }
8
+
9
+ its(:num_bytes) { should == 4 }
10
+ its(:to_binary_s) { should == [Math::PI].pack('e') }
11
+ its(:value_read_from_written) { should be_within(0.000001).of(Math::PI) }
12
+ end
13
+
14
+ describe "A FloatBe" do
15
+ subject { BinData::FloatBe.new(Math::PI) }
16
+
17
+ its(:num_bytes) { should == 4 }
18
+ its(:to_binary_s) { should == [Math::PI].pack('g') }
19
+ its(:value_read_from_written) { should be_within(0.000001).of(Math::PI) }
20
+ end
21
+
22
+ describe "A DoubleLe" do
23
+ subject { BinData::DoubleLe.new(Math::PI) }
24
+
25
+ its(:num_bytes) { should == 8 }
26
+ its(:to_binary_s) { should == [Math::PI].pack('E') }
27
+ its(:value_read_from_written) { should be_within(0.0000000000000001).of(Math::PI) }
28
+ end
29
+
30
+
31
+ describe "A DoubleBe" do
32
+ subject { BinData::DoubleBe.new(Math::PI) }
33
+
34
+ its(:num_bytes) { should == 8 }
35
+ its(:to_binary_s) { should == [Math::PI].pack('G') }
36
+ its(:value_read_from_written) { should be_within(0.0000000000000001).of(Math::PI) }
37
+ end
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata'
5
+
6
+ shared_examples "All Integers" do
7
+
8
+ it "have correct num_bytes" do
9
+ all_classes do |int_class|
10
+ int_class.new.num_bytes.should == @nbytes
11
+ end
12
+ end
13
+
14
+ it "have a sensible value of zero" do
15
+ all_classes do |int_class|
16
+ int_class.new.should be_zero
17
+ end
18
+ end
19
+
20
+ it "avoid underflow" do
21
+ all_classes do |int_class|
22
+ subject = int_class.new
23
+ subject.assign(min_value - 1)
24
+
25
+ subject.should == min_value
26
+ end
27
+ end
28
+
29
+ it "avoid overflow" do
30
+ all_classes do |int_class|
31
+ subject = int_class.new
32
+ subject.assign(max_value + 1)
33
+
34
+ subject.should == max_value
35
+ end
36
+ end
37
+
38
+ it "assign values" do
39
+ all_classes do |int_class|
40
+ subject = int_class.new
41
+ test_int = gen_test_int
42
+ subject.assign(test_int)
43
+
44
+ subject.should == test_int
45
+ end
46
+ end
47
+
48
+ it "assign values from other int objects" do
49
+ all_classes do |int_class|
50
+ src = int_class.new
51
+ src.assign(gen_test_int)
52
+
53
+ subject = int_class.new
54
+ subject.assign(src)
55
+ subject.should == src
56
+ end
57
+ end
58
+
59
+ it "symmetrically read and write a +ve number" do
60
+ all_classes do |int_class|
61
+ subject = int_class.new
62
+ subject.assign(gen_test_int)
63
+
64
+ subject.value_read_from_written.should == subject
65
+ end
66
+ end
67
+
68
+ it "symmetrically read and write a -ve number" do
69
+ all_classes do |int_class|
70
+ if @signed
71
+ subject = int_class.new
72
+ subject.assign(-gen_test_int)
73
+
74
+ subject.value_read_from_written.should == subject
75
+ end
76
+ end
77
+ end
78
+
79
+ it "convert a +ve number to string" do
80
+ all_classes do |int_class|
81
+ val = gen_test_int
82
+
83
+ subject = int_class.new
84
+ subject.assign(val)
85
+
86
+ subject.to_binary_s.should == int_to_binary_str(val)
87
+ end
88
+ end
89
+
90
+ it "convert a -ve number to string" do
91
+ all_classes do |int_class|
92
+ if @signed
93
+ val = -gen_test_int
94
+
95
+ subject = int_class.new
96
+ subject.assign(val)
97
+
98
+ subject.to_binary_s.should == int_to_binary_str(val)
99
+ end
100
+ end
101
+ end
102
+
103
+ def all_classes(&block)
104
+ @ints.each_pair do |int_class, nbytes|
105
+ @nbytes = nbytes
106
+ yield int_class
107
+ end
108
+ end
109
+
110
+ def min_value
111
+ if @signed
112
+ -max_value - 1
113
+ else
114
+ 0
115
+ end
116
+ end
117
+
118
+ def max_value
119
+ if @signed
120
+ (1 << (@nbytes * 8 - 1)) - 1
121
+ else
122
+ (1 << (@nbytes * 8)) - 1
123
+ end
124
+ end
125
+
126
+ def gen_test_int
127
+ # resulting int is guaranteed to be +ve for signed or unsigned integers
128
+ (0 ... @nbytes).inject(0) { |val, i| (val << 8) | ((val + 0x11) % 0x100) }
129
+ end
130
+
131
+ def int_to_binary_str(val)
132
+ str = binary("")
133
+ v = val & ((1 << (@nbytes * 8)) - 1)
134
+ @nbytes.times do
135
+ str.concat(v & 0xff)
136
+ v >>= 8
137
+ end
138
+ (@endian == :little) ? str : str.reverse
139
+ end
140
+
141
+ def create_mapping_of_class_to_nbits(endian, signed)
142
+ base = signed ? "Int" : "Uint"
143
+ signed_sym = signed ? :signed : :unsigned
144
+ endian_str = (endian == :little) ? "le" : "be"
145
+
146
+ result = {}
147
+ result[BinData.const_get("#{base}8")] = 1
148
+ (1 .. 20).each do |nbytes|
149
+ nbits = nbytes * 8
150
+ class_name = "#{base}#{nbits}#{endian_str}"
151
+ result[BinData.const_get(class_name)] = nbytes
152
+ end
153
+
154
+ result
155
+ end
156
+ end
157
+
158
+ describe "All signed big endian integers" do
159
+ include_examples "All Integers"
160
+
161
+ before(:all) do
162
+ @endian = :big
163
+ @signed = true
164
+ @ints = create_mapping_of_class_to_nbits(@endian, @signed)
165
+ end
166
+ end
167
+
168
+ describe "All unsigned big endian integers" do
169
+ include_examples "All Integers"
170
+
171
+ before(:all) do
172
+ @endian = :big
173
+ @signed = false
174
+ @ints = create_mapping_of_class_to_nbits(@endian, @signed)
175
+ end
176
+ end
177
+
178
+ describe "All signed little endian integers" do
179
+ include_examples "All Integers"
180
+
181
+ before(:all) do
182
+ @endian = :little
183
+ @signed = true
184
+ @ints = create_mapping_of_class_to_nbits(@endian, @signed)
185
+ end
186
+ end
187
+
188
+ describe "All unsigned little endian integers" do
189
+ include_examples "All Integers"
190
+
191
+ before(:all) do
192
+ @endian = :little
193
+ @signed = false
194
+ @ints = create_mapping_of_class_to_nbits(@endian, @signed)
195
+ end
196
+ end
197
+
198
+ describe "Custom defined integers" do
199
+ it "fail unless bits are a multiple of 8" do
200
+ expect {
201
+ BinData::Uint7le
202
+ }.to raise_error
203
+
204
+ expect {
205
+ BinData::Uint7be
206
+ }.to raise_error
207
+
208
+ expect {
209
+ BinData::Int7le
210
+ }.to raise_error
211
+
212
+ expect {
213
+ BinData::Int7be
214
+ }.to raise_error
215
+ end
216
+ end