bindata 2.2.0 → 2.3.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.

Potentially problematic release.


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

@@ -48,7 +48,7 @@ module BinData
48
48
  # Returns the index of this data object inside it's nearest container
49
49
  # array.
50
50
  def index
51
- return @overrides[:index] if @overrides and @overrides.has_key?(:index)
51
+ return @overrides[:index] if defined? @overrides and @overrides.has_key?(:index)
52
52
 
53
53
  child = @obj
54
54
  parent = @obj.parent
@@ -1,4 +1,13 @@
1
1
  module BinData
2
+ # WARNING: THIS IS UNSUPPORTED!!
3
+ #
4
+ # This was a (failed) experimental feature that allowed seeking within the
5
+ # input stream. It remains here for backwards compatability for the few
6
+ # people that used it.
7
+ #
8
+ # The official way to skip around the stream is to use BinData::Skip with
9
+ # the `:to_abs_offset` parameter.
10
+ #
2
11
  # == Parameters
3
12
  #
4
13
  # Parameters may be provided at initialisation to control the behaviour of
@@ -77,4 +86,9 @@ module BinData
77
86
  end
78
87
  end
79
88
  end
89
+
90
+ # Add these offset options to Base
91
+ class Base
92
+ include CheckOrAdjustOffsetPlugin
93
+ end
80
94
  end
@@ -69,6 +69,7 @@ module BinData
69
69
  mandatory_parameter :struct_params
70
70
 
71
71
  def initialize_instance
72
+ super
72
73
  @struct = BinData::Struct.new(get_parameter(:struct_params), self)
73
74
  end
74
75
 
@@ -28,7 +28,7 @@ module BinData
28
28
  end
29
29
 
30
30
  def has_parameter?(param)
31
- if @factory
31
+ if defined? @factory
32
32
  @factory.has_parameter?(param)
33
33
  else
34
34
  @obj_params.has_parameter?(param)
@@ -200,7 +200,7 @@ module BinData
200
200
  self[:endian] ||= hints[:endian]
201
201
  end
202
202
 
203
- if hints[:search_prefix]
203
+ if hints[:search_prefix] and not hints[:search_prefix].empty?
204
204
  self[:search_prefix] = Array(self[:search_prefix]).concat(Array(hints[:search_prefix]))
205
205
  end
206
206
 
@@ -217,19 +217,19 @@ module BinData
217
217
 
218
218
  def warn_replacement_parameter(bad_key, suggested_key)
219
219
  if has_parameter?(bad_key)
220
- warn ":#{bad_key} is not used with #{@the_class}. " +
220
+ Kernel.warn ":#{bad_key} is not used with #{@the_class}. " +
221
221
  "You probably want to change this to :#{suggested_key}"
222
222
  end
223
223
  end
224
224
 
225
- def warn_renamed_parameter(old_key, new_key)
226
- val = delete(old_key)
227
- if val
228
- self[new_key] = val
229
- warn ":#{old_key} has been renamed to :#{new_key} in #{@the_class}. " +
230
- "Using :#{old_key} is now deprecated and will be removed in the future"
231
- end
232
- end
225
+ # def warn_renamed_parameter(old_key, new_key)
226
+ # val = delete(old_key)
227
+ # if val
228
+ # self[new_key] = val
229
+ # Kernel.warn ":#{old_key} has been renamed to :#{new_key} in #{@the_class}. " +
230
+ # "Using :#{old_key} is now deprecated and will be removed in the future"
231
+ # end
232
+ # end
233
233
 
234
234
  def must_be_integer(*keys)
235
235
  keys.each do |key|
@@ -4,7 +4,7 @@ module BinData
4
4
  # Skip will skip over bytes from the input stream. If the stream is not
5
5
  # seekable, then the bytes are consumed and discarded.
6
6
  #
7
- # When writing, skip will write <tt>:length</tt> number of zero bytes.
7
+ # When writing, skip will write the appropriate number of zero bytes.
8
8
  #
9
9
  # require 'bindata'
10
10
  #
@@ -21,22 +21,40 @@ module BinData
21
21
  # Skip objects accept all the params that BinData::BasePrimitive
22
22
  # does, as well as the following:
23
23
  #
24
- # <tt>:length</tt>:: The number of bytes to skip.
24
+ # <tt>:length</tt>:: The number of bytes to skip.
25
+ # <tt>:to_abs_offset</tt>:: Skips to the given absolute offset.
25
26
  #
26
27
  class Skip < BinData::BasePrimitive
27
28
 
28
- mandatory_parameter :length
29
+ arg_processor :skip
30
+
31
+ optional_parameters :length, :to_abs_offset
32
+ mutually_exclusive_parameters :length, :to_abs_offset
33
+
34
+ def initialize_shared_instance
35
+ extend SkipLengthPlugin if has_parameter?(:length)
36
+ extend SkipToAbsOffsetPlugin if has_parameter?(:to_abs_offset)
37
+ super
38
+ end
29
39
 
30
40
  #---------------
31
41
  private
32
42
 
33
43
  def value_to_binary_string(val)
34
- len = eval_parameter(:length)
35
- "\000" * len
44
+ len = skip_length
45
+ if len < 0
46
+ raise ValidityError, "#{debug_name} attempted to seek backwards by #{len.abs} bytes"
47
+ end
48
+
49
+ "\000" * skip_length
36
50
  end
37
51
 
38
52
  def read_and_return_value(io)
39
- len = eval_parameter(:length)
53
+ len = skip_length
54
+ if len < 0
55
+ raise ValidityError, "#{debug_name} attempted to seek backwards by #{len.abs} bytes"
56
+ end
57
+
40
58
  io.seekbytes(len)
41
59
  ""
42
60
  end
@@ -45,4 +63,27 @@ module BinData
45
63
  ""
46
64
  end
47
65
  end
66
+
67
+ class SkipArgProcessor < BaseArgProcessor
68
+ def sanitize_parameters!(obj_class, params)
69
+ unless (params.has_parameter?(:length) or params.has_parameter?(:to_abs_offset))
70
+ raise ArgumentError, "#{obj_class} requires either :length or :to_abs_offset"
71
+ end
72
+ params.must_be_integer(:to_abs_offset, :length)
73
+ end
74
+ end
75
+
76
+ # Logic for the :length parameter
77
+ module SkipLengthPlugin
78
+ def skip_length
79
+ eval_parameter(:length)
80
+ end
81
+ end
82
+
83
+ # Logic for the :to_abs_offset parameter
84
+ module SkipToAbsOffsetPlugin
85
+ def skip_length
86
+ eval_parameter(:to_abs_offset) - abs_offset
87
+ end
88
+ end
48
89
  end
@@ -126,7 +126,7 @@ module BinData
126
126
  def offset_of(child) #:nodoc:
127
127
  instantiate_all_objs
128
128
  sum = sum_num_bytes_below_index(find_index_of(child))
129
- child.do_num_bytes.is_a?(Integer) ? sum.ceil : sum.floor
129
+ child.bit_aligned? ? sum.floor : sum.ceil
130
130
  end
131
131
 
132
132
  def do_read(io) #:nodoc:
@@ -1,3 +1,3 @@
1
1
  module BinData
2
- VERSION = "2.2.0"
2
+ VERSION = "2.3.0"
3
3
  end
@@ -28,12 +28,5 @@ module BinData
28
28
  fail "#{caller[0]} remove the call to super in #initialize_instance"
29
29
  end
30
30
  end
31
-
32
- # #offset has been renamed to #abs_offset.
33
- # Eventually #rel_offset will be renamed to #offset.
34
- def offset
35
- warn "#offset is deprecated in #{debug_name}. Use #abs_offset instead"
36
- abs_offset
37
- end
38
31
  end
39
32
  end
@@ -50,6 +50,10 @@ describe BinData::BitAligned do
50
50
  obj.num_bytes.must_equal 3
51
51
  end
52
52
 
53
+ it "has expected abs_offset" do
54
+ obj.str.abs_offset.must_equal 0
55
+ end
56
+
53
57
  it "reads as expected" do
54
58
  obj.read("\x56\x36\x42")
55
59
  obj.snapshot.must_equal({:preamble => 5, :str => "cd", :afterward => 2})
@@ -17,11 +17,30 @@ describe BinData::Array, "when instantiating" do
17
17
  end
18
18
  end
19
19
 
20
+ it "warns about :length" do
21
+ Kernel.must_warn ":length is not used with BinData::Array. You probably want to change this to :initial_length" do
22
+ obj = BinData::Array.new(:type => :uint8, :length => 3)
23
+ obj.read "123"
24
+ end
25
+ end
26
+
27
+ it "warns about :read_length" do
28
+ Kernel.must_warn ":read_length is not used with BinData::Array. You probably want to change this to :initial_length" do
29
+ obj = BinData::Array.new(:type => :uint8, :read_length => 3)
30
+ obj.read "123"
31
+ end
32
+ end
33
+
20
34
  it "fails if a given type is unknown" do
21
35
  args = {:type => :does_not_exist, :initial_length => 3}
22
36
  lambda { BinData::Array.new(args) }.must_raise BinData::UnRegisteredTypeError
23
37
  end
24
38
 
39
+ it "fails if :initial_length is not an integer" do
40
+ args = {:type => :uint8, :initial_length => "3"}
41
+ lambda { BinData::Array.new(args) }.must_raise ArgumentError
42
+ end
43
+
25
44
  it "does not allow both :initial_length and :read_until" do
26
45
  args = {:initial_length => 3, :read_until => lambda { false } }
27
46
  lambda { BinData::Array.new(args) }.must_raise ArgumentError
@@ -194,6 +194,12 @@ describe BinData::BasePrimitive, "asserting value" do
194
194
  let(:io) { ExampleSingle.io_with_value(12) }
195
195
 
196
196
  describe ":assert is non boolean" do
197
+ it "asserts sensible value" do
198
+ data = ExampleSingle.new(:assert => 0)
199
+ data.assert!
200
+ data.value.must_equal 0
201
+ end
202
+
197
203
  it "succeeds when assert is correct" do
198
204
  data = ExampleSingle.new(:assert => 12)
199
205
  data.read(io)
@@ -83,8 +83,8 @@ describe BinData::Base do
83
83
  class BaseStub < BinData::Base
84
84
  # Override to avoid NotImplemented errors
85
85
  def clear?; end
86
- def assign(x); @data = x; end
87
- def snapshot; @data; end
86
+ def assign(x); end
87
+ def snapshot; end
88
88
  def do_read(io) end
89
89
  def do_write(io) end
90
90
  def do_num_bytes; end
@@ -218,4 +218,14 @@ describe "Bits of size 1" do
218
218
  obj.must_equal 0
219
219
  end
220
220
  end
221
+
222
+ it "must not be signed" do
223
+ lambda {
224
+ BinData::Sbit1
225
+ }.must_raise RuntimeError
226
+
227
+ lambda {
228
+ BinData::Sbit1le
229
+ }.must_raise RuntimeError
230
+ end
221
231
  end
@@ -54,6 +54,7 @@ describe BinData::Buffer, "subclassed with a single type" do
54
54
 
55
55
  it "has total num_bytes" do
56
56
  obj = IntBuffer.new
57
+ assert obj.clear?
57
58
  obj.num_bytes.must_equal 5
58
59
  end
59
60
  end
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
4
+
5
+ describe BinData::DelayedIO, "when instantiating" do
6
+ describe "with no mandatory parameters supplied" do
7
+ it "raises an error" do
8
+ args = {}
9
+ lambda { BinData::DelayedIO.new(args) }.must_raise ArgumentError
10
+ end
11
+ end
12
+
13
+ describe "with some but not all mandatory parameters supplied" do
14
+ it "raises an error" do
15
+ args = {:read_abs_offset => 3}
16
+ lambda { BinData::DelayedIO.new(args) }.must_raise ArgumentError
17
+ end
18
+ end
19
+
20
+ it "fails if a given type is unknown" do
21
+ args = {:type => :does_not_exist, :length => 3}
22
+ lambda { BinData::DelayedIO.new(args) }.must_raise BinData::UnRegisteredTypeError
23
+ end
24
+
25
+ it "accepts BinData::Base as :type" do
26
+ obj = BinData::Int8.new(:initial_value => 5)
27
+ array = BinData::DelayedIO.new(:type => obj, :read_abs_offset => 3)
28
+ array.must_equal 5
29
+ end
30
+ end
31
+
32
+ describe BinData::DelayedIO, "subclassed with a single type" do
33
+ class IntDelayedIO < BinData::DelayedIO
34
+ endian :big
35
+ default_parameter :read_abs_offset => 5
36
+
37
+ uint16
38
+ end
39
+
40
+ it "behaves as type" do
41
+ obj = IntDelayedIO.new(3)
42
+ obj.must_equal 3
43
+ end
44
+
45
+ it "does not read" do
46
+ obj = IntDelayedIO.read "\001\002\003\004\005\006\007"
47
+ assert obj.clear?
48
+ end
49
+
50
+ it "does not do_num_bytes" do
51
+ obj = IntDelayedIO.new(3)
52
+ obj.do_num_bytes.must_equal 0
53
+ end
54
+
55
+ it "does num_bytes" do
56
+ obj = IntDelayedIO.new(3)
57
+ obj.num_bytes.must_equal 2
58
+ end
59
+
60
+ it "does not write" do
61
+ io = StringIO.new
62
+ obj = IntDelayedIO.new(3)
63
+ obj.write(io)
64
+ io.value.must_equal ""
65
+ end
66
+
67
+ it "uses read_abs_offset" do
68
+ obj = IntDelayedIO.new(3)
69
+ obj.abs_offset.must_equal 5
70
+ obj.rel_offset.must_equal 5
71
+ end
72
+
73
+ it "uses abs_offset if set" do
74
+ obj = IntDelayedIO.new(3)
75
+ obj.abs_offset = 10
76
+ obj.abs_offset.must_equal 10
77
+ obj.rel_offset.must_equal 10
78
+ end
79
+
80
+ it "must call #read before #read_now!" do
81
+ obj = IntDelayedIO.new(3)
82
+ lambda {
83
+ obj.read_now!
84
+ }.must_raise IOError
85
+ end
86
+
87
+ it "reads explicitly" do
88
+ obj = IntDelayedIO.read "\001\002\003\004\005\006\007"
89
+ obj.read_now!
90
+
91
+ obj.must_equal 0x0607
92
+ end
93
+
94
+ it "must call #write before #write_now!" do
95
+ obj = IntDelayedIO.new(3)
96
+ lambda {
97
+ obj.write_now!
98
+ }.must_raise IOError
99
+ end
100
+
101
+ it "writes explicitly" do
102
+ io = StringIO.new "\001\002\003\004\005\006\007\010\011"
103
+ obj = IntDelayedIO.new(3)
104
+ obj.write(io)
105
+ obj.write_now!
106
+ io.value.must_equal "\001\002\003\004\005\000\003\010\011"
107
+ end
108
+
109
+ it "writes explicitly after setting abs_offset" do
110
+ io = StringIO.new "\001\002\003\004\005\006\007\010\011"
111
+ obj = IntDelayedIO.new(7)
112
+ obj.write(io)
113
+
114
+ obj.abs_offset = 1
115
+ obj.write_now!
116
+ io.value.must_equal "\001\000\007\004\005\006\007\010\011"
117
+ end
118
+ end
119
+
120
+ describe BinData::DelayedIO, "subclassed with multiple types" do
121
+ class StringDelayedIO < BinData::DelayedIO
122
+ endian :big
123
+ default_parameter :read_abs_offset => 5
124
+
125
+ uint16 :len, :value => lambda { str.length }
126
+ string :str, :read_length => :len
127
+ end
128
+
129
+ it "behaves as type" do
130
+ obj = StringDelayedIO.new(:str => "hello")
131
+ obj.snapshot.must_equal({:len => 5, :str => "hello"})
132
+ end
133
+
134
+ it "reads explicitly" do
135
+ obj = StringDelayedIO.read "\001\002\003\004\005\000\003abc\013"
136
+ obj.read_now!
137
+
138
+ obj.snapshot.must_equal({:len => 3, :str => "abc"})
139
+ end
140
+
141
+ it "writes explicitly" do
142
+ io = StringIO.new "\001\002\003\004\005\006\007\010\011\012\013\014\015"
143
+ obj = StringDelayedIO.new(:str => "hello")
144
+ obj.write(io)
145
+ obj.write_now!
146
+ io.value.must_equal "\001\002\003\004\005\000\005hello\015"
147
+ end
148
+ end
149
+
150
+ describe BinData::DelayedIO, "inside a Record" do
151
+ class DelayedIORecord < BinData::Record
152
+ endian :little
153
+
154
+ uint16 :str_length, :value => lambda { str.length }
155
+ delayed_io :str, :read_abs_offset => 4 do
156
+ string :read_length => :str_length
157
+ end
158
+ delayed_io :my_int, :read_abs_offset => 2 do
159
+ uint16 :initial_value => 7
160
+ end
161
+ end
162
+
163
+ it "reads" do
164
+ obj = DelayedIORecord.read "\x05\x00\x03\x0012345"
165
+ obj.num_bytes.must_equal 2
166
+ obj.snapshot.must_equal({:str_length => 0, :str => "", :my_int => 7})
167
+ end
168
+
169
+ it "reads explicitly" do
170
+ obj = DelayedIORecord.read "\x05\x00\x03\x0012345"
171
+ obj.str.read_now!
172
+ obj.my_int.read_now!
173
+ obj.num_bytes.must_equal 2
174
+ obj.snapshot.must_equal({:str_length => 5, :str => "12345", :my_int => 3})
175
+ end
176
+
177
+ it "writes" do
178
+ obj = DelayedIORecord.new(:str => "abc", :my_int => 2)
179
+ io = StringIO.new
180
+ obj.write(io)
181
+ obj.str.write_now!
182
+ obj.my_int.write_now!
183
+ io.value.must_equal "\x03\x00\x02\x00abc"
184
+ end
185
+ end
186
+
187
+ describe BinData::DelayedIO, "with auto_call" do
188
+ class AutoCallDelayedIORecord < BinData::Record
189
+ auto_call_delayed_io
190
+ uint8 :a
191
+ delayed_io :b, :read_abs_offset => 1 do
192
+ uint8
193
+ end
194
+ end
195
+
196
+ it "class reads" do
197
+ obj = AutoCallDelayedIORecord.read "\x01\x02"
198
+ obj.snapshot.must_equal({:a => 1, :b => 2})
199
+ end
200
+
201
+ it "reads" do
202
+ obj = AutoCallDelayedIORecord.new
203
+ obj.read "\x01\x02"
204
+ obj.snapshot.must_equal({:a => 1, :b => 2})
205
+ end
206
+
207
+ it "writes" do
208
+ obj = AutoCallDelayedIORecord.new(:a => 1, :b => 2)
209
+ io = StringIO.new
210
+ obj.write(io)
211
+ io.value.must_equal "\x01\x02"
212
+ end
213
+
214
+ it "to_binary_s" do
215
+ obj = AutoCallDelayedIORecord.new(:a => 1, :b => 2)
216
+ obj.to_binary_s.must_equal_binary "\x01\x02"
217
+ end
218
+
219
+ it "num_bytes" do
220
+ obj = AutoCallDelayedIORecord.new(:a => 1, :b => 2)
221
+ obj.num_bytes.must_equal 2
222
+ end
223
+ end