bindata 0.9.2 → 0.9.3
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.
- data/ChangeLog +9 -0
- data/TODO +18 -1
- data/lib/bindata.rb +1 -1
- data/lib/bindata/array.rb +52 -33
- data/lib/bindata/base.rb +61 -111
- data/lib/bindata/choice.rb +77 -46
- data/lib/bindata/int.rb +48 -13
- data/lib/bindata/io.rb +107 -64
- data/lib/bindata/lazy.rb +67 -79
- data/lib/bindata/multi_value.rb +39 -5
- data/lib/bindata/params.rb +36 -0
- data/lib/bindata/registry.rb +4 -4
- data/lib/bindata/sanitize.rb +74 -58
- data/lib/bindata/single.rb +4 -4
- data/lib/bindata/single_value.rb +15 -13
- data/lib/bindata/string.rb +10 -11
- data/lib/bindata/stringz.rb +8 -8
- data/lib/bindata/struct.rb +58 -141
- data/spec/array_spec.rb +37 -2
- data/spec/base_spec.rb +23 -25
- data/spec/bits_spec.rb +0 -0
- data/spec/choice_spec.rb +11 -5
- data/spec/float_spec.rb +0 -0
- data/spec/int_spec.rb +41 -27
- data/spec/io_spec.rb +0 -0
- data/spec/lazy_spec.rb +107 -74
- data/spec/multi_value_spec.rb +47 -2
- data/spec/registry_spec.rb +0 -0
- data/spec/rest_spec.rb +0 -0
- data/spec/sanitize_spec.rb +10 -11
- data/spec/single_spec.rb +0 -0
- data/spec/single_value_spec.rb +0 -0
- data/spec/spec_common.rb +0 -0
- data/spec/string_spec.rb +0 -0
- data/spec/stringz_spec.rb +0 -0
- data/spec/struct_spec.rb +3 -3
- metadata +68 -60
data/lib/bindata/single.rb
CHANGED
@@ -47,11 +47,11 @@ module BinData
|
|
47
47
|
# the value just read in.
|
48
48
|
class Single < BinData::Base
|
49
49
|
# These are the parameters used by this class.
|
50
|
-
|
51
|
-
|
50
|
+
bindata_optional_parameters :initial_value, :value, :check_value
|
51
|
+
bindata_mutually_exclusive_parameters :initial_value, :value
|
52
52
|
|
53
|
-
def initialize(params = {},
|
54
|
-
super(params,
|
53
|
+
def initialize(params = {}, parent = nil)
|
54
|
+
super(params, parent)
|
55
55
|
clear
|
56
56
|
end
|
57
57
|
|
data/lib/bindata/single_value.rb
CHANGED
@@ -67,6 +67,11 @@ module BinData
|
|
67
67
|
register(subclass.name, subclass)
|
68
68
|
end
|
69
69
|
|
70
|
+
# Can this data object self reference itself?
|
71
|
+
def recursive?
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
70
75
|
# Returns or sets the endianess of numerics used in this stucture.
|
71
76
|
# Endianess is applied to the fields of this structure.
|
72
77
|
# Valid values are :little and :big.
|
@@ -123,28 +128,25 @@ module BinData
|
|
123
128
|
@fields.push([type, name, params])
|
124
129
|
end
|
125
130
|
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
hash = {}
|
132
|
-
hash[:fields] = self.fields
|
133
|
-
hash[:endian] = self.endian unless self.endian.nil?
|
131
|
+
# Ensures that +params+ is of the form expected by #initialize.
|
132
|
+
def sanitize_parameters!(sanitizer, params)
|
133
|
+
struct_params = {}
|
134
|
+
struct_params[:fields] = self.fields
|
135
|
+
struct_params[:endian] = self.endian unless self.endian.nil?
|
134
136
|
|
135
|
-
params[:struct_params] =
|
137
|
+
params[:struct_params] = struct_params
|
136
138
|
|
137
139
|
super(sanitizer, params)
|
138
140
|
end
|
139
141
|
end
|
140
142
|
|
141
143
|
# These are the parameters used by this class.
|
142
|
-
|
144
|
+
bindata_mandatory_parameter :struct_params
|
143
145
|
|
144
|
-
def initialize(params = {},
|
145
|
-
super(params,
|
146
|
+
def initialize(params = {}, parent = nil)
|
147
|
+
super(params, parent)
|
146
148
|
|
147
|
-
@struct = BinData::Struct.new(
|
149
|
+
@struct = BinData::Struct.new(no_eval_param(:struct_params), self)
|
148
150
|
end
|
149
151
|
|
150
152
|
# Forward method calls to the internal struct.
|
data/lib/bindata/string.rb
CHANGED
@@ -51,18 +51,15 @@ module BinData
|
|
51
51
|
register(self.name, self)
|
52
52
|
|
53
53
|
# These are the parameters used by this class.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
54
|
+
bindata_optional_parameters :read_length, :length, :trim_value
|
55
|
+
bindata_default_parameters :pad_char => "\0"
|
56
|
+
bindata_mutually_exclusive_parameters :read_length, :length
|
57
|
+
bindata_mutually_exclusive_parameters :length, :value
|
58
58
|
|
59
59
|
class << self
|
60
60
|
|
61
|
-
#
|
62
|
-
|
63
|
-
def sanitize_parameters(sanitizer, params, *args)
|
64
|
-
params = params.dup
|
65
|
-
|
61
|
+
# Ensures that +params+ is of the form expected by #initialize.
|
62
|
+
def sanitize_parameters!(sanitizer, params)
|
66
63
|
# warn about deprecated param - remove before releasing 1.0
|
67
64
|
if params[:initial_length]
|
68
65
|
warn ":initial_length is deprecated. Replacing with :read_length"
|
@@ -79,7 +76,7 @@ module BinData
|
|
79
76
|
params[:pad_char] = ch
|
80
77
|
end
|
81
78
|
|
82
|
-
super(sanitizer, params
|
79
|
+
super(sanitizer, params)
|
83
80
|
end
|
84
81
|
end
|
85
82
|
|
@@ -87,7 +84,9 @@ module BinData
|
|
87
84
|
# trimmed as required.
|
88
85
|
def value
|
89
86
|
v = val_to_str(_value)
|
90
|
-
|
87
|
+
if no_eval_param(:trim_value) == true
|
88
|
+
v.sub!(/#{eval_param(:pad_char)}*$/, "")
|
89
|
+
end
|
91
90
|
v
|
92
91
|
end
|
93
92
|
|
data/lib/bindata/stringz.rb
CHANGED
@@ -31,7 +31,7 @@ module BinData
|
|
31
31
|
register(self.name, self)
|
32
32
|
|
33
33
|
# These are the parameters used by this class.
|
34
|
-
|
34
|
+
bindata_optional_parameters :max_length
|
35
35
|
|
36
36
|
# Overrides value to return the value of this data excluding the trailing
|
37
37
|
# zero byte.
|
@@ -75,24 +75,24 @@ module BinData
|
|
75
75
|
# will not be longer than +max_length+.
|
76
76
|
def zero_terminate(str, max_length = nil)
|
77
77
|
# str must not be empty
|
78
|
-
|
78
|
+
result = (str == "") ? "\0" : str
|
79
79
|
|
80
80
|
# remove anything after the first \0
|
81
|
-
|
81
|
+
result = result.sub(/([^\0]*\0).*/, '\1')
|
82
82
|
|
83
83
|
# trim string to be no longer than max_length including zero byte
|
84
84
|
if max_length
|
85
85
|
max_length = 1 if max_length < 1
|
86
|
-
|
87
|
-
if
|
88
|
-
|
86
|
+
result = result[0, max_length]
|
87
|
+
if result.length == max_length and result[-1, 1] != "\0"
|
88
|
+
result[-1, 1] = "\0"
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
92
|
# ensure last byte in the string is a zero byte
|
93
|
-
|
93
|
+
result << "\0" if result[-1, 1] != "\0"
|
94
94
|
|
95
|
-
|
95
|
+
result
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
data/lib/bindata/struct.rb
CHANGED
@@ -44,7 +44,8 @@ module BinData
|
|
44
44
|
%w{alias and begin break case class def defined do else elsif
|
45
45
|
end ensure false for if in module next nil not or redo
|
46
46
|
rescue retry return self super then true undef unless until
|
47
|
-
when while yield
|
47
|
+
when while yield} +
|
48
|
+
%w{array element index offset value} ).uniq
|
48
49
|
|
49
50
|
# Register this class
|
50
51
|
register(self.name, self)
|
@@ -57,101 +58,20 @@ module BinData
|
|
57
58
|
end
|
58
59
|
|
59
60
|
class << self
|
60
|
-
#### DEPRECATION HACK to
|
61
|
+
#### DEPRECATION HACK to warn about inheriting from BinData::Struct
|
61
62
|
#
|
62
63
|
def inherited(subclass) #:nodoc:
|
63
64
|
if subclass != MultiValue
|
64
65
|
# warn about deprecated method - remove before releasing 1.0
|
65
|
-
|
66
|
-
|
67
|
-
register(subclass.name, subclass)
|
66
|
+
fail "error: inheriting from BinData::Struct has been deprecated. Inherit from BinData::MultiValue instead."
|
68
67
|
end
|
69
68
|
end
|
70
|
-
def endian(endian = nil)
|
71
|
-
@endian ||= nil
|
72
|
-
if [:little, :big].include?(endian)
|
73
|
-
@endian = endian
|
74
|
-
elsif endian != nil
|
75
|
-
raise ArgumentError, "unknown value for endian '#{endian}'"
|
76
|
-
end
|
77
|
-
@endian
|
78
|
-
end
|
79
|
-
def hide(*args)
|
80
|
-
# note that fields are stored in an instance variable not a class var
|
81
|
-
@hide ||= []
|
82
|
-
args.each do |name|
|
83
|
-
@hide << name.to_s
|
84
|
-
end
|
85
|
-
@hide
|
86
|
-
end
|
87
|
-
def fields
|
88
|
-
@fields || []
|
89
|
-
end
|
90
|
-
def method_missing(symbol, *args)
|
91
|
-
name, params = args
|
92
|
-
|
93
|
-
type = symbol
|
94
|
-
name = name.to_s
|
95
|
-
params ||= {}
|
96
|
-
|
97
|
-
# note that fields are stored in an instance variable not a class var
|
98
|
-
@fields ||= []
|
99
|
-
|
100
|
-
# check that type is known
|
101
|
-
unless Sanitizer.type_exists?(type, endian)
|
102
|
-
raise TypeError, "unknown type '#{type}' for #{self}", caller
|
103
|
-
end
|
104
|
-
|
105
|
-
# check for duplicate names
|
106
|
-
@fields.each do |t, n, p|
|
107
|
-
if n == name
|
108
|
-
raise SyntaxError, "duplicate field '#{name}' in #{self}", caller
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# check that name doesn't shadow an existing method
|
113
|
-
if self.instance_methods.include?(name)
|
114
|
-
raise NameError.new("", name),
|
115
|
-
"field '#{name}' shadows an existing method", caller
|
116
|
-
end
|
117
|
-
|
118
|
-
# check that name isn't reserved
|
119
|
-
if self::RESERVED.include?(name)
|
120
|
-
raise NameError.new("", name),
|
121
|
-
"field '#{name}' is a reserved name", caller
|
122
|
-
end
|
123
|
-
|
124
|
-
# remember this field. These fields will be recalled upon creating
|
125
|
-
# an instance of this class
|
126
|
-
@fields.push([type, name, params])
|
127
|
-
end
|
128
|
-
def deprecated_hack(params)
|
129
|
-
params = params.dup
|
130
|
-
|
131
|
-
# possibly override endian
|
132
|
-
endian = params[:endian] || self.endian
|
133
|
-
params[:endian] = endian unless endian.nil?
|
134
|
-
|
135
|
-
params[:fields] = params[:fields] || self.fields
|
136
|
-
params[:hide] = params[:hide] || self.hide
|
137
|
-
|
138
|
-
params
|
139
|
-
end
|
140
69
|
#
|
141
70
|
#### DEPRECATION HACK to allow inheriting from BinData::Struct
|
142
71
|
|
143
72
|
|
144
|
-
#
|
145
|
-
|
146
|
-
def sanitize_parameters(sanitizer, params)
|
147
|
-
#### DEPRECATION HACK to allow inheriting from BinData::Struct
|
148
|
-
#
|
149
|
-
params = deprecated_hack(params)
|
150
|
-
#
|
151
|
-
#### DEPRECATION HACK to allow inheriting from BinData::Struct
|
152
|
-
|
153
|
-
params = params.dup
|
154
|
-
|
73
|
+
# Ensures that +params+ is of the form expected by #initialize.
|
74
|
+
def sanitize_parameters!(sanitizer, params)
|
155
75
|
# possibly override endian
|
156
76
|
endian = params[:endian]
|
157
77
|
if endian != nil
|
@@ -167,68 +87,65 @@ module BinData
|
|
167
87
|
# ensure names of fields are strings and that params is sanitized
|
168
88
|
all_fields = params[:fields].collect do |ftype, fname, fparams|
|
169
89
|
fname = fname.to_s
|
170
|
-
klass
|
171
|
-
|
90
|
+
klass = sanitizer.lookup_klass(ftype)
|
91
|
+
sanitized_fparams = sanitizer.sanitize_params(klass, fparams)
|
92
|
+
[klass, fname, sanitized_fparams]
|
172
93
|
end
|
173
94
|
params[:fields] = all_fields
|
174
95
|
end
|
175
96
|
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
all_field_names = params[:fields].collect { |k,n,p| n }
|
181
|
-
hide = hidden & all_field_names
|
182
|
-
end
|
183
|
-
params[:hide] = hide
|
184
|
-
end
|
185
|
-
|
186
|
-
# obtain SanitizedParameters
|
187
|
-
params = super(sanitizer, params)
|
97
|
+
# now params are sanitized, check that parameter names are okay
|
98
|
+
field_names = []
|
99
|
+
instance_methods = self.instance_methods
|
100
|
+
reserved_names = RESERVED
|
188
101
|
|
189
|
-
|
102
|
+
params[:fields].each do |fklass, fname, fparams|
|
190
103
|
|
191
|
-
|
192
|
-
|
193
|
-
|
104
|
+
# check that name doesn't shadow an existing method
|
105
|
+
if instance_methods.include?(fname)
|
106
|
+
raise NameError.new("Rename field '#{fname}' in #{self}, " +
|
107
|
+
"as it shadows an existing method.", fname)
|
108
|
+
end
|
194
109
|
|
195
|
-
|
110
|
+
# check that name isn't reserved
|
111
|
+
if reserved_names.include?(fname)
|
112
|
+
raise NameError.new("Rename field '#{fname}' in #{self}, " +
|
113
|
+
"as it is a reserved name.", fname)
|
114
|
+
end
|
196
115
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
116
|
+
# check for multiple definitions
|
117
|
+
if field_names.include?(fname)
|
118
|
+
raise NameError.new("field '#{fname}' in #{self}, " +
|
119
|
+
"is defined multiple times.", fname)
|
120
|
+
end
|
202
121
|
|
203
|
-
|
204
|
-
if reserved_names.include?(fname)
|
205
|
-
raise NameError.new("Rename field '#{fname}' in #{self}, " +
|
206
|
-
"as it is a reserved name.", fname)
|
122
|
+
field_names << fname
|
207
123
|
end
|
208
124
|
|
209
|
-
#
|
210
|
-
|
211
|
-
|
212
|
-
|
125
|
+
# collect all hidden names that correspond to a field name
|
126
|
+
hide = []
|
127
|
+
if params.has_key?(:hide)
|
128
|
+
hidden = (params[:hide] || []).collect { |h| h.to_s }
|
129
|
+
all_field_names = params[:fields].collect { |k,n,p| n }
|
130
|
+
hide = hidden & all_field_names
|
213
131
|
end
|
214
|
-
|
215
|
-
field_names << fname
|
132
|
+
params[:hide] = hide
|
216
133
|
end
|
217
134
|
|
218
|
-
params
|
135
|
+
super(sanitizer, params)
|
219
136
|
end
|
220
137
|
end
|
221
138
|
|
222
139
|
# These are the parameters used by this class.
|
223
|
-
|
224
|
-
|
140
|
+
bindata_mandatory_parameter :fields
|
141
|
+
bindata_optional_parameters :endian, :hide
|
225
142
|
|
226
143
|
# Creates a new Struct.
|
227
|
-
def initialize(params = {},
|
228
|
-
super(params,
|
144
|
+
def initialize(params = {}, parent = nil)
|
145
|
+
super(params, parent)
|
229
146
|
|
230
147
|
# extract field names but don't instantiate the fields
|
231
|
-
@field_names =
|
148
|
+
@field_names = no_eval_param(:fields).collect { |k, n, p| n }
|
232
149
|
@field_objs = []
|
233
150
|
end
|
234
151
|
|
@@ -269,7 +186,7 @@ module BinData
|
|
269
186
|
def field_names(include_hidden = false)
|
270
187
|
# collect field names
|
271
188
|
names = []
|
272
|
-
hidden =
|
189
|
+
hidden = no_eval_param(:hide)
|
273
190
|
@field_names.each do |name|
|
274
191
|
if include_hidden or not hidden.include?(name)
|
275
192
|
names << name
|
@@ -283,17 +200,6 @@ module BinData
|
|
283
200
|
@field_objs.each { |f| f.done_read unless f.nil? }
|
284
201
|
end
|
285
202
|
|
286
|
-
# Returns the data object that stores values for +name+.
|
287
|
-
def find_obj_for_name(name)
|
288
|
-
idx = @field_names.index(name)
|
289
|
-
if idx
|
290
|
-
instantiate_obj(idx)
|
291
|
-
@field_objs[idx]
|
292
|
-
else
|
293
|
-
nil
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
203
|
def offset_of(field)
|
298
204
|
idx = @field_names.index(field.to_s)
|
299
205
|
if idx
|
@@ -344,16 +250,27 @@ module BinData
|
|
344
250
|
#---------------
|
345
251
|
private
|
346
252
|
|
253
|
+
# Returns the data object that stores values for +name+.
|
254
|
+
def find_obj_for_name(name)
|
255
|
+
idx = @field_names.index(name)
|
256
|
+
if idx
|
257
|
+
instantiate_obj(idx)
|
258
|
+
@field_objs[idx].obj
|
259
|
+
else
|
260
|
+
nil
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
347
264
|
# Instantiates all fields.
|
348
265
|
def instantiate_all
|
349
|
-
|
266
|
+
@field_names.each_with_index { |name, i| instantiate_obj(i) }
|
350
267
|
end
|
351
268
|
|
352
269
|
# Instantiates the field object at position +idx+.
|
353
270
|
def instantiate_obj(idx)
|
354
271
|
if @field_objs[idx].nil?
|
355
|
-
fklass, fname, fparams =
|
356
|
-
@field_objs[idx] = fklass.new(fparams,
|
272
|
+
fklass, fname, fparams = no_eval_param(:fields)[idx]
|
273
|
+
@field_objs[idx] = fklass.new(fparams, self)
|
357
274
|
end
|
358
275
|
end
|
359
276
|
|
data/spec/array_spec.rb
CHANGED
@@ -34,7 +34,7 @@ describe BinData::Array, "with no elements" do
|
|
34
34
|
@data.should_not be_single_value
|
35
35
|
end
|
36
36
|
|
37
|
-
it "should return
|
37
|
+
it "should return zero length" do
|
38
38
|
@data.length.should be_zero
|
39
39
|
end
|
40
40
|
|
@@ -127,7 +127,7 @@ describe BinData::Array, "with several elements" do
|
|
127
127
|
@data.should_not be_empty
|
128
128
|
end
|
129
129
|
|
130
|
-
it "should return a nicely formatted array
|
130
|
+
it "should return a nicely formatted array for inspect" do
|
131
131
|
@data.inspect.should == "[1, 2, 3, 4, 5]"
|
132
132
|
end
|
133
133
|
|
@@ -264,6 +264,22 @@ describe BinData::Array, "with :read_until containing +array+ and +index+" do
|
|
264
264
|
end
|
265
265
|
end
|
266
266
|
|
267
|
+
describe BinData::Array, "with :read_until => :eof" do
|
268
|
+
it "should read records until eof" do
|
269
|
+
obj = BinData::Array.new(:type => :int8, :read_until => :eof)
|
270
|
+
data = "\x01\x02\x03"
|
271
|
+
obj.read(data)
|
272
|
+
obj.snapshot.should == [1, 2, 3]
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should read records until eof, ignoring partial records" do
|
276
|
+
obj = BinData::Array.new(:type => :int16be, :read_until => :eof)
|
277
|
+
data = "\x00\x01\x00\x02\x03"
|
278
|
+
obj.read(data)
|
279
|
+
obj.snapshot.should == [1, 2]
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
267
283
|
describe BinData::Array, "of bits" do
|
268
284
|
before(:each) do
|
269
285
|
@data = BinData::Array.new(:type => :bit1, :initial_length => 15)
|
@@ -299,3 +315,22 @@ describe BinData::Array, "of bits" do
|
|
299
315
|
end
|
300
316
|
end
|
301
317
|
|
318
|
+
describe BinData::Array, "nested within an Array" do
|
319
|
+
before(:each) do
|
320
|
+
nested_array_params = { :type => [:int8, { :initial_value => :index }],
|
321
|
+
:initial_length => lambda { index + 1 } }
|
322
|
+
@data = BinData::Array.new(:type => [:array, nested_array_params],
|
323
|
+
:initial_length => 3)
|
324
|
+
end
|
325
|
+
|
326
|
+
it "should use correct index" do
|
327
|
+
@data.snapshot.should == [ [0], [0, 1], [0, 1, 2] ]
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should maintain structure when reading" do
|
331
|
+
str = "\x04\x05\x06\x07\x08\x09"
|
332
|
+
@data.read(str)
|
333
|
+
@data.snapshot.should == [ [4], [5, 6], [7, 8, 9] ]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|