bindata 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bindata might be problematic. Click here for more details.

Files changed (47) hide show
  1. data/ChangeLog +18 -0
  2. data/NEWS +59 -0
  3. data/README +22 -23
  4. data/TODO +18 -12
  5. data/examples/gzip.rb +4 -4
  6. data/lib/bindata.rb +4 -3
  7. data/lib/bindata/array.rb +202 -132
  8. data/lib/bindata/base.rb +147 -166
  9. data/lib/bindata/{single.rb → base_primitive.rb} +82 -56
  10. data/lib/bindata/bits.rb +31 -770
  11. data/lib/bindata/choice.rb +157 -82
  12. data/lib/bindata/float.rb +25 -27
  13. data/lib/bindata/int.rb +144 -177
  14. data/lib/bindata/io.rb +59 -49
  15. data/lib/bindata/lazy.rb +80 -50
  16. data/lib/bindata/params.rb +134 -26
  17. data/lib/bindata/{single_value.rb → primitive.rb} +71 -64
  18. data/lib/bindata/{multi_value.rb → record.rb} +52 -70
  19. data/lib/bindata/registry.rb +49 -17
  20. data/lib/bindata/rest.rb +6 -10
  21. data/lib/bindata/sanitize.rb +55 -70
  22. data/lib/bindata/string.rb +60 -42
  23. data/lib/bindata/stringz.rb +34 -35
  24. data/lib/bindata/struct.rb +197 -152
  25. data/lib/bindata/trace.rb +35 -0
  26. data/spec/array_spec.rb +128 -112
  27. data/spec/{single_spec.rb → base_primitive_spec.rb} +102 -61
  28. data/spec/base_spec.rb +190 -185
  29. data/spec/bits_spec.rb +126 -98
  30. data/spec/choice_spec.rb +89 -98
  31. data/spec/example.rb +19 -0
  32. data/spec/float_spec.rb +28 -44
  33. data/spec/int_spec.rb +217 -127
  34. data/spec/io_spec.rb +41 -24
  35. data/spec/lazy_spec.rb +95 -49
  36. data/spec/primitive_spec.rb +191 -0
  37. data/spec/{multi_value_spec.rb → record_spec.rb} +124 -89
  38. data/spec/registry_spec.rb +53 -12
  39. data/spec/rest_spec.rb +2 -3
  40. data/spec/sanitize_spec.rb +47 -73
  41. data/spec/spec_common.rb +13 -1
  42. data/spec/string_spec.rb +34 -23
  43. data/spec/stringz_spec.rb +10 -18
  44. data/spec/struct_spec.rb +91 -63
  45. data/spec/system_spec.rb +291 -0
  46. metadata +12 -8
  47. data/spec/single_value_spec.rb +0 -131
data/spec/example.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'bindata/base_primitive'
2
+
3
+ class ExampleSingle < BinData::BasePrimitive
4
+ register(self.name, self)
5
+
6
+ private
7
+
8
+ def value_to_binary_string(val)
9
+ [val].pack("V")
10
+ end
11
+
12
+ def read_and_return_value(io)
13
+ io.readbytes(4).unpack("V").at(0)
14
+ end
15
+
16
+ def sensible_default
17
+ 0
18
+ end
19
+ end
data/spec/float_spec.rb CHANGED
@@ -7,24 +7,18 @@ describe "A FloatLe" do
7
7
  before(:each) do
8
8
  @obj = BinData::FloatLe.new
9
9
  @obj.value = Math::PI
10
+ end
10
11
 
11
- @io = StringIO.new
12
+ it "should be 4 bytes in size" do
13
+ @obj.num_bytes.should == 4
12
14
  end
13
15
 
14
16
  it "should write the expected value" do
15
- @obj.write(@io)
16
- @io.rewind
17
-
18
- @io.read.should == [Math::PI].pack('e')
17
+ written_value(@obj).should == [Math::PI].pack('e')
19
18
  end
20
19
 
21
20
  it "should read the same value as written" do
22
- @obj.write(@io)
23
- @io.rewind
24
-
25
- # check that we read in the same data that was written
26
- @obj.read(@io)
27
- @obj.value.should be_close(Math::PI, 0.000001)
21
+ value_read_from_written(@obj).should be_close(Math::PI, 0.000001)
28
22
  end
29
23
  end
30
24
 
@@ -32,24 +26,18 @@ describe "A FloatBe" do
32
26
  before(:each) do
33
27
  @obj = BinData::FloatBe.new
34
28
  @obj.value = Math::PI
29
+ end
35
30
 
36
- @io = StringIO.new
31
+ it "should be 4 bytes in size" do
32
+ @obj.num_bytes.should == 4
37
33
  end
38
34
 
39
35
  it "should write the expected value" do
40
- @obj.write(@io)
41
- @io.rewind
42
-
43
- @io.read.should == [Math::PI].pack('g')
36
+ written_value(@obj).should == [Math::PI].pack('g')
44
37
  end
45
38
 
46
39
  it "should read the same value as written" do
47
- @obj.write(@io)
48
- @io.rewind
49
-
50
- # check that we read in the same data that was written
51
- @obj.read(@io)
52
- @obj.value.should be_close(Math::PI, 0.000001)
40
+ value_read_from_written(@obj).should be_close(Math::PI, 0.000001)
53
41
  end
54
42
  end
55
43
 
@@ -57,24 +45,18 @@ describe "A DoubleLe" do
57
45
  before(:each) do
58
46
  @obj = BinData::DoubleLe.new
59
47
  @obj.value = Math::PI
48
+ end
60
49
 
61
- @io = StringIO.new
50
+ it "should be 8 bytes in size" do
51
+ @obj.num_bytes.should == 8
62
52
  end
63
53
 
64
54
  it "should write the expected value" do
65
- @obj.write(@io)
66
- @io.rewind
67
-
68
- @io.read.should == [Math::PI].pack('E')
55
+ written_value(@obj).should == [Math::PI].pack('E')
69
56
  end
70
57
 
71
58
  it "should read the same value as written" do
72
- @obj.write(@io)
73
- @io.rewind
74
-
75
- # check that we read in the same data that was written
76
- @obj.read(@io)
77
- @obj.value.should be_close(Math::PI, 0.000000000000000001)
59
+ value_read_from_written(@obj).should be_close(Math::PI, 0.0000000000000001)
78
60
  end
79
61
  end
80
62
 
@@ -83,23 +65,25 @@ describe "A DoubleBe" do
83
65
  before(:each) do
84
66
  @obj = BinData::DoubleBe.new
85
67
  @obj.value = Math::PI
68
+ end
86
69
 
87
- @io = StringIO.new
70
+ it "should be 8 bytes in size" do
71
+ @obj.num_bytes.should == 8
88
72
  end
89
73
 
90
74
  it "should write the expected value" do
91
- @obj.write(@io)
92
- @io.rewind
93
-
94
- @io.read.should == [Math::PI].pack('G')
75
+ written_value(@obj).should == [Math::PI].pack('G')
95
76
  end
96
77
 
97
78
  it "should read the same value as written" do
98
- @obj.write(@io)
99
- @io.rewind
100
-
101
- # check that we read in the same data that was written
102
- @obj.read(@io)
103
- @obj.value.should be_close(Math::PI, 0.000000000000000001)
79
+ value_read_from_written(@obj).should be_close(Math::PI, 0.0000000000000001)
104
80
  end
105
81
  end
82
+
83
+ def written_value(obj)
84
+ obj.to_binary_s
85
+ end
86
+
87
+ def value_read_from_written(obj)
88
+ obj.class.read(obj.to_binary_s)
89
+ end
data/spec/int_spec.rb CHANGED
@@ -1,163 +1,253 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
4
- require 'bindata/int'
4
+ require 'bindata'
5
+
6
+ share_examples_for "All Integers" do
7
+
8
+ it "should have correct num_bytes" do
9
+ all_classes do |int_class|
10
+ int_class.new.num_bytes.should == @nbytes
11
+ end
12
+ end
5
13
 
6
- describe "All signed integers" do
7
14
  it "should have a sensible value of zero" do
8
- [BinData::Int8,
9
- BinData::Int16le,
10
- BinData::Int16be,
11
- BinData::Int32le,
12
- BinData::Int32be,
13
- BinData::Int64le,
14
- BinData::Int64be,
15
- BinData::Int128le,
16
- BinData::Int128be].each do |klass|
17
- klass.new.value.should be_zero
15
+ all_classes do |int_class|
16
+ int_class.new.value.should be_zero
18
17
  end
19
18
  end
20
19
 
21
- it "should pass these tests" do
22
- [
23
- [ 1, true, BinData::Int8],
24
- [ 2, false, BinData::Int16le],
25
- [ 2, true, BinData::Int16be],
26
- [ 4, false, BinData::Int32le],
27
- [ 4, true, BinData::Int32be],
28
- [ 8, false, BinData::Int64le],
29
- [ 8, true, BinData::Int64be],
30
- [16, false, BinData::Int128le],
31
- [16, true, BinData::Int128be],
32
- ].each do |nbytes, big_endian, klass|
33
- gen_int_test_data(nbytes, big_endian).each do |val, clamped_val, str|
34
- test_read_write(klass, val, clamped_val, str)
35
- end
20
+ it "should avoid underflow" do
21
+ all_classes do |int_class|
22
+ obj = int_class.new
23
+ obj.value = min_value - 1
24
+
25
+ obj.value.should == min_value
36
26
  end
37
27
  end
38
- end
39
28
 
40
- describe "All unsigned integers" do
41
- it "should have a sensible value of zero" do
42
- [BinData::Uint8,
43
- BinData::Uint16le,
44
- BinData::Uint16be,
45
- BinData::Uint32le,
46
- BinData::Uint32be,
47
- BinData::Uint64le,
48
- BinData::Uint64be,
49
- BinData::Uint128le,
50
- BinData::Uint128be].each do |klass|
51
- klass.new.value.should be_zero
29
+ it "should avoid overflow" do
30
+ all_classes do |int_class|
31
+ obj = int_class.new
32
+ obj.value = max_value + 1
33
+
34
+ obj.value.should == max_value
35
+ end
36
+ end
37
+
38
+ it "should assign values" do
39
+ all_classes do |int_class|
40
+ obj = int_class.new
41
+ test_int = gen_test_int
42
+ obj.assign(test_int)
43
+ obj.value.should == test_int
52
44
  end
53
45
  end
54
46
 
55
- it "should pass these tests" do
56
- [
57
- [ 1, true, BinData::Uint8],
58
- [ 2, false, BinData::Uint16le],
59
- [ 2, true, BinData::Uint16be],
60
- [ 4, false, BinData::Uint32le],
61
- [ 4, true, BinData::Uint32be],
62
- [ 8, false, BinData::Uint64le],
63
- [ 8, true, BinData::Uint64be],
64
- [16, false, BinData::Uint128le],
65
- [16, true, BinData::Uint128be],
66
- ].each do |nbytes, big_endian, klass|
67
- gen_uint_test_data(nbytes, big_endian).each do |val, clamped_val, str|
68
- test_read_write(klass, val, clamped_val, str)
47
+ it "should assign values from other int objects" do
48
+ all_classes do |int_class|
49
+ src = int_class.new
50
+ src.assign(gen_test_int)
51
+
52
+ obj = int_class.new
53
+ obj.assign(src)
54
+ obj.value.should == src.value
55
+ end
56
+ end
57
+
58
+ it "should symmetrically read and write a +ve number" do
59
+ all_classes do |int_class|
60
+ obj = int_class.new
61
+ obj.value = gen_test_int
62
+
63
+ str = obj.to_binary_s
64
+ int_class.read(str).should == obj.value
65
+ end
66
+ end
67
+
68
+ it "should symmetrically read and write a -ve number" do
69
+ all_classes do |int_class|
70
+ if @signed
71
+ obj = int_class.new
72
+ obj.value = -gen_test_int
73
+
74
+ str = obj.to_binary_s
75
+ int_class.read(str).should == obj.value
69
76
  end
70
77
  end
71
78
  end
72
- end
73
79
 
74
- # run read / write tests for the given values
75
- def test_read_write(klass, val, clamped_val, str)
76
- # set the data and ensure clamping occurs
77
- data = klass.new
78
- data.value = val
79
- data.value.should == clamped_val
80
-
81
- # write the data
82
- io = StringIO.new
83
- data.write(io)
84
-
85
- # check that we write the expected byte pattern
86
- io.rewind
87
- io.read.should == str
88
-
89
- # check that we read in the same data that was written
90
- io.rewind
91
- data = klass.new
92
- data.read(io)
93
- data.value.should == clamped_val
94
- end
80
+ it "should convert a +ve number to string" do
81
+ all_classes do |int_class|
82
+ val = gen_test_int
95
83
 
96
- # return test data for testing unsigned ints
97
- def gen_uint_test_data(nbytes, big_endian)
98
- raise "nbytes too large" if nbytes > 16
99
- tests = []
84
+ obj = int_class.new
85
+ obj.value = val
100
86
 
101
- max_value = (1 << (nbytes * 8)) - 1
102
- min_value = 0
87
+ obj.to_binary_s.should == int_to_binary_str(val)
88
+ end
89
+ end
103
90
 
104
- # test the minimum value
105
- v = min_value
106
- s = "\x00" * nbytes
107
- tests.push [v, v, big_endian ? s : s.reverse]
91
+ it "should convert a -ve number to string" do
92
+ all_classes do |int_class|
93
+ if @signed
94
+ val = -gen_test_int
108
95
 
109
- # values below minimum should be clamped to minimum
110
- tests.push [v-1, v, big_endian ? s : s.reverse]
96
+ obj = int_class.new
97
+ obj.value = val
111
98
 
112
- # test a value within range
113
- v = 0x123456789abcdef0123456789abcdef0 >> ((16-nbytes) * 8)
114
- s = "\x12\x34\x56\x78\x9a\xbc\xde\xf0\x12\x34\x56\x78\x9a\xbc\xde\xf0".slice(0, nbytes)
115
- tests.push [v, v, big_endian ? s : s.reverse]
99
+ obj.to_binary_s.should == int_to_binary_str(val)
100
+ end
101
+ end
102
+ end
103
+
104
+ def all_classes(&block)
105
+ @ints.each_pair do |int_class, nbytes|
106
+ @nbytes = nbytes
107
+ yield int_class
108
+ end
109
+ end
116
110
 
117
- # test the maximum value
118
- v = max_value
119
- s = "\xff" * nbytes
120
- tests.push [v, v, big_endian ? s : s.reverse]
111
+ def min_value
112
+ if @signed
113
+ -max_value - 1
114
+ else
115
+ 0
116
+ end
117
+ end
121
118
 
122
- # values above maximum should be clamped to maximum
123
- tests.push [v+1, v, big_endian ? s : s.reverse]
119
+ def max_value
120
+ if @signed
121
+ (1 << (@nbytes * 8 - 1)) - 1
122
+ else
123
+ (1 << (@nbytes * 8)) - 1
124
+ end
125
+ end
124
126
 
125
- tests
126
- end
127
+ def gen_test_int
128
+ # resulting int is guaranteed to be +ve for signed or unsigned integers
129
+ (0 ... @nbytes).inject(0) { |val, i| (val << 8) | ((val + 0x11) % 0x100) }
130
+ end
127
131
 
128
- # return test data for testing signed ints
129
- def gen_int_test_data(nbytes, big_endian)
130
- raise "nbytes too large" if nbytes > 16
131
- tests = []
132
+ def int_to_binary_str(val)
133
+ str = ""
134
+ v = val & ((1 << (@nbytes * 8)) - 1)
135
+ @nbytes.times do
136
+ str.concat(v & 0xff)
137
+ v >>= 8
138
+ end
139
+ (@endian == :little) ? str : str.reverse
140
+ end
141
+ end
132
142
 
133
- max_value = (1 << (nbytes * 8 - 1)) - 1
134
- min_value = -max_value - 1
143
+ describe "All signed big endian integers" do
144
+ it_should_behave_like "All Integers"
145
+
146
+ before(:all) do
147
+ BinData::Integer.define_class(24, :big, :signed)
148
+ BinData::Integer.define_class(48, :big, :signed)
149
+ BinData::Integer.define_class(96, :big, :signed)
150
+ @endian = :big
151
+ @signed = true
152
+ @ints = {
153
+ BinData::Int8 => 1,
154
+ BinData::Int8be => 1,
155
+ BinData::Int16be => 2,
156
+ BinData::Int24be => 3,
157
+ BinData::Int32be => 4,
158
+ BinData::Int48be => 6,
159
+ BinData::Int64be => 8,
160
+ BinData::Int96be => 12,
161
+ BinData::Int128be => 16,
162
+ }
163
+ end
164
+ end
135
165
 
136
- # test the minimum value
137
- v = min_value
138
- s = "\x80" + "\x00" * (nbytes - 1)
139
- tests.push [v, v, big_endian ? s : s.reverse]
166
+ describe "All unsigned big endian integers" do
167
+ it_should_behave_like "All Integers"
168
+
169
+ before(:all) do
170
+ BinData::Integer.define_class(24, :big, :unsigned)
171
+ BinData::Integer.define_class(48, :big, :unsigned)
172
+ BinData::Integer.define_class(96, :big, :unsigned)
173
+ @endian = :big
174
+ @signed = false
175
+ @ints = {
176
+ BinData::Uint8 => 1,
177
+ BinData::Uint8be => 1,
178
+ BinData::Uint16be => 2,
179
+ BinData::Uint24be => 3,
180
+ BinData::Uint32be => 4,
181
+ BinData::Uint48be => 6,
182
+ BinData::Uint64be => 8,
183
+ BinData::Uint96be => 12,
184
+ BinData::Uint128be => 16,
185
+ }
186
+ end
187
+ end
140
188
 
141
- # values below minimum should be clamped to minimum
142
- tests.push [v-1, v, big_endian ? s : s.reverse]
189
+ describe "All signed little endian integers" do
190
+ it_should_behave_like "All Integers"
191
+
192
+ before(:all) do
193
+ BinData::Integer.define_class(24, :little, :signed)
194
+ BinData::Integer.define_class(48, :little, :signed)
195
+ BinData::Integer.define_class(96, :little, :signed)
196
+ @endian = :little
197
+ @signed = true
198
+ @ints = {
199
+ BinData::Int8 => 1,
200
+ BinData::Int8le => 1,
201
+ BinData::Int16le => 2,
202
+ BinData::Int24le => 3,
203
+ BinData::Int32le => 4,
204
+ BinData::Int48le => 6,
205
+ BinData::Int64le => 8,
206
+ BinData::Int96le => 12,
207
+ BinData::Int128le => 16,
208
+ }
209
+ end
210
+ end
143
211
 
144
- # test a -ve value within range
145
- v = -1
146
- s = "\xff" * nbytes
147
- tests.push [v, v, big_endian ? s : s.reverse]
212
+ describe "All unsigned little endian integers" do
213
+ it_should_behave_like "All Integers"
214
+
215
+ before(:all) do
216
+ BinData::Integer.define_class(24, :little, :unsigned)
217
+ BinData::Integer.define_class(48, :little, :unsigned)
218
+ BinData::Integer.define_class(96, :little, :unsigned)
219
+ @endian = :little
220
+ @signed = false
221
+ @ints = {
222
+ BinData::Uint8 => 1,
223
+ BinData::Uint8le => 1,
224
+ BinData::Uint16le => 2,
225
+ BinData::Uint24le => 3,
226
+ BinData::Uint32le => 4,
227
+ BinData::Uint48le => 6,
228
+ BinData::Uint64le => 8,
229
+ BinData::Uint96le => 12,
230
+ BinData::Uint128le => 16,
231
+ }
232
+ end
233
+ end
148
234
 
149
- # test a +ve value within range
150
- v = 0x123456789abcdef0123456789abcdef0 >> ((16-nbytes) * 8)
151
- s = "\x12\x34\x56\x78\x9a\xbc\xde\xf0\x12\x34\x56\x78\x9a\xbc\xde\xf0".slice(0, nbytes)
152
- tests.push [v, v, big_endian ? s : s.reverse]
235
+ describe "Custom defined integers" do
236
+ it "should fail unless bits are a multiple of 8" do
237
+ lambda {
238
+ BinData::Integer.define_class(7, :little, :unsigned)
239
+ }.should raise_error
153
240
 
154
- # test the maximum value
155
- v = max_value
156
- s = "\x7f" + "\xff" * (nbytes - 1)
157
- tests.push [v, v, big_endian ? s : s.reverse]
241
+ lambda {
242
+ BinData::Integer.define_class(7, :big, :unsigned)
243
+ }.should raise_error
158
244
 
159
- # values above maximum should be clamped to maximum
160
- tests.push [v+1, v, big_endian ? s : s.reverse]
245
+ lambda {
246
+ BinData::Integer.define_class(7, :little, :signed)
247
+ }.should raise_error
161
248
 
162
- tests
249
+ lambda {
250
+ BinData::Integer.define_class(7, :big, :signed)
251
+ }.should raise_error
252
+ end
163
253
  end