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/ChangeLog
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
= BinData Changelog
|
2
2
|
|
3
|
+
== Version 0.9.3 (2008-12-03)
|
4
|
+
|
5
|
+
* Arrays can now :read_until => :eof
|
6
|
+
* TCPSocket and UDPSocket can now be used as input streams (patch courtesy
|
7
|
+
of Peter Suschlik).
|
8
|
+
* Added 128 bit integers.
|
9
|
+
* Significant memory usage reduction.
|
10
|
+
* Added custom mandatory and default parameters for user defined MultiValues.
|
11
|
+
|
3
12
|
== Version 0.9.2 (2008-07-18)
|
4
13
|
|
5
14
|
* Added lazy instantiation to allow recursive definitions.
|
data/TODO
CHANGED
@@ -1,3 +1,20 @@
|
|
1
1
|
* Think how offset_of should work.
|
2
2
|
|
3
|
-
|
3
|
+
+ perhaps #offset_of and #abs_offset_of methods?
|
4
|
+
+ needed for struct and array
|
5
|
+
|
6
|
+
* Add ability to determine name of a data object.
|
7
|
+
e.g. obj.a[4].c
|
8
|
+
|
9
|
+
This will be used when throwing exceptions to aid debugging.
|
10
|
+
|
11
|
+
* Using the above names, add tracing capability when reading.
|
12
|
+
|
13
|
+
* Clean up Array to make it as close to ruby Array as possible.
|
14
|
+
|
15
|
+
* Need more examples.
|
16
|
+
|
17
|
+
* Refactor test cases.
|
18
|
+
add more comprehensive integration tests to serve as examples
|
19
|
+
|
20
|
+
* Need better documentation.
|
data/lib/bindata.rb
CHANGED
data/lib/bindata/array.rb
CHANGED
@@ -27,6 +27,10 @@ module BinData
|
|
27
27
|
# obj.read(data)
|
28
28
|
# obj.snapshot #=> [3, 4, 5, 6, 7]
|
29
29
|
#
|
30
|
+
# obj = BinData::Array.new(:type => :int8, :read_until => :eof)
|
31
|
+
# obj.read(data)
|
32
|
+
# obj.snapshot #=> [3, 4, 5, 6, 7, 8, 9]
|
33
|
+
#
|
30
34
|
# == Parameters
|
31
35
|
#
|
32
36
|
# Parameters may be provided at initialisation to control the behaviour of
|
@@ -42,7 +46,9 @@ module BinData
|
|
42
46
|
# read an array until a sentinel value is found.
|
43
47
|
# The variables +index+, +element+ and +array+
|
44
48
|
# are made available to any lambda assigned to
|
45
|
-
# this parameter.
|
49
|
+
# this parameter. If the value of this parameter
|
50
|
+
# is the symbol :eof, then the array will read
|
51
|
+
# as much data from the stream as possible.
|
46
52
|
#
|
47
53
|
# Each data object in an array has the variable +index+ made available
|
48
54
|
# to any lambda evaluated as a parameter of that data object.
|
@@ -53,16 +59,13 @@ module BinData
|
|
53
59
|
register(self.name, self)
|
54
60
|
|
55
61
|
# These are the parameters used by this class.
|
56
|
-
|
57
|
-
|
58
|
-
|
62
|
+
bindata_mandatory_parameter :type
|
63
|
+
bindata_optional_parameters :initial_length, :read_until
|
64
|
+
bindata_mutually_exclusive_parameters :initial_length, :read_until
|
59
65
|
|
60
66
|
class << self
|
61
|
-
#
|
62
|
-
|
63
|
-
def sanitize_parameters(sanitizer, params)
|
64
|
-
params = params.dup
|
65
|
-
|
67
|
+
# Ensures that +params+ is of the form expected by #initialize.
|
68
|
+
def sanitize_parameters!(sanitizer, params)
|
66
69
|
unless params.has_key?(:initial_length) or params.has_key?(:read_until)
|
67
70
|
# ensure one of :initial_length and :read_until exists
|
68
71
|
params[:initial_length] = 0
|
@@ -74,7 +77,9 @@ module BinData
|
|
74
77
|
|
75
78
|
if params.has_key?(:type)
|
76
79
|
type, el_params = params[:type]
|
77
|
-
|
80
|
+
klass = sanitizer.lookup_klass(type)
|
81
|
+
sanitized_params = sanitizer.sanitize_params(klass, el_params)
|
82
|
+
params[:type] = [klass, sanitized_params]
|
78
83
|
end
|
79
84
|
|
80
85
|
super(sanitizer, params)
|
@@ -82,10 +87,10 @@ module BinData
|
|
82
87
|
end
|
83
88
|
|
84
89
|
# Creates a new Array
|
85
|
-
def initialize(params = {},
|
86
|
-
super(params,
|
90
|
+
def initialize(params = {}, parent = nil)
|
91
|
+
super(params, parent)
|
87
92
|
|
88
|
-
klass, el_params =
|
93
|
+
klass, el_params = no_eval_param(:type)
|
89
94
|
|
90
95
|
@element_list = nil
|
91
96
|
@element_klass = klass
|
@@ -139,6 +144,11 @@ module BinData
|
|
139
144
|
self.last
|
140
145
|
end
|
141
146
|
|
147
|
+
def index(obj)
|
148
|
+
# TODO handle single values
|
149
|
+
elements.index(obj)
|
150
|
+
end
|
151
|
+
|
142
152
|
# Pushes the given object(s) on to the end of this array.
|
143
153
|
# This expression returns the array itself, so several appends may
|
144
154
|
# be chained together.
|
@@ -166,7 +176,7 @@ module BinData
|
|
166
176
|
end
|
167
177
|
|
168
178
|
data = elements[*args]
|
169
|
-
if
|
179
|
+
if args.length > 1 or ::Range === args[0]
|
170
180
|
data.collect { |el| (el && el.single_value?) ? el.value : el }
|
171
181
|
else
|
172
182
|
(data && data.single_value?) ? data.value : data
|
@@ -202,13 +212,11 @@ module BinData
|
|
202
212
|
# If the array is empty, the first form returns nil, and the second
|
203
213
|
# form returns an empty array.
|
204
214
|
def first(n = nil)
|
205
|
-
if n.nil?
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
self[0]
|
211
|
-
end
|
215
|
+
if n.nil? and elements.empty?
|
216
|
+
# explicitly return nil as arrays grow automatically
|
217
|
+
nil
|
218
|
+
elsif n.nil?
|
219
|
+
self[0]
|
212
220
|
else
|
213
221
|
self[0, n]
|
214
222
|
end
|
@@ -249,15 +257,28 @@ module BinData
|
|
249
257
|
def _do_read(io)
|
250
258
|
if has_param?(:initial_length)
|
251
259
|
elements.each { |f| f.do_read(io) }
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
260
|
+
elsif has_param?(:read_until)
|
261
|
+
if no_eval_param(:read_until) == :eof
|
262
|
+
@element_list = nil
|
263
|
+
loop do
|
264
|
+
element = append_new_element
|
265
|
+
begin
|
266
|
+
element.do_read(io)
|
267
|
+
rescue
|
268
|
+
@element_list.pop
|
269
|
+
break
|
270
|
+
end
|
271
|
+
end
|
272
|
+
else
|
273
|
+
@element_list = nil
|
274
|
+
loop do
|
275
|
+
element = append_new_element
|
276
|
+
element.do_read(io)
|
277
|
+
variables = { :index => self.length - 1, :element => self.last,
|
278
|
+
:array => self }
|
279
|
+
finished = eval_param(:read_until, variables)
|
280
|
+
break if finished
|
281
|
+
end
|
261
282
|
end
|
262
283
|
end
|
263
284
|
end
|
@@ -304,9 +325,7 @@ module BinData
|
|
304
325
|
# ensure @element_list is initialised
|
305
326
|
elements()
|
306
327
|
|
307
|
-
|
308
|
-
env.add_variable(:index, @element_list.length)
|
309
|
-
element = @element_klass.new(@element_params, env)
|
328
|
+
element = @element_klass.new(@element_params, self)
|
310
329
|
@element_list << element
|
311
330
|
element
|
312
331
|
end
|
data/lib/bindata/base.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'bindata/io'
|
2
2
|
require 'bindata/lazy'
|
3
|
+
require 'bindata/params'
|
3
4
|
require 'bindata/registry'
|
4
5
|
require 'bindata/sanitize'
|
5
6
|
require 'stringio'
|
@@ -32,98 +33,43 @@ module BinData
|
|
32
33
|
# <tt>:check_offset</tt>, except that it will
|
33
34
|
# adjust the IO offset instead of raising an error.
|
34
35
|
class Base
|
36
|
+
|
35
37
|
class << self
|
36
|
-
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
if not args.empty?
|
50
|
-
args.each { |arg| @mandatory_parameters << arg.to_sym }
|
51
|
-
@mandatory_parameters.uniq!
|
52
|
-
end
|
53
|
-
@mandatory_parameters
|
54
|
-
end
|
55
|
-
alias_method :mandatory_parameter, :mandatory_parameters
|
56
|
-
|
57
|
-
# Returns the optional parameters used by this class. Any given args
|
58
|
-
# are appended to the parameters list. The parameters for a class will
|
59
|
-
# include the parameters of its ancestors.
|
60
|
-
def optional_parameters(*args)
|
61
|
-
unless defined? @optional_parameters
|
62
|
-
@optional_parameters = []
|
63
|
-
ancestors[1..-1].each do |parent|
|
64
|
-
if parent.respond_to?(:optional_parameters)
|
65
|
-
pop = parent.optional_parameters
|
66
|
-
@optional_parameters.concat(pop)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
if not args.empty?
|
71
|
-
args.each { |arg| @optional_parameters << arg.to_sym }
|
72
|
-
@optional_parameters.uniq!
|
73
|
-
end
|
74
|
-
@optional_parameters
|
38
|
+
extend Parameters
|
39
|
+
|
40
|
+
# Define methods for:
|
41
|
+
# bindata_mandatory_parameters
|
42
|
+
# bindata_optional_parameters
|
43
|
+
# bindata_default_parameters
|
44
|
+
# bindata_mutually_exclusive_parameters
|
45
|
+
|
46
|
+
define_x_parameters(:bindata_mandatory, []) do |array, args|
|
47
|
+
args.each { |arg| array << arg.to_sym }
|
48
|
+
array.uniq!
|
75
49
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
# include the parameters of its ancestors.
|
81
|
-
def default_parameters(params = {})
|
82
|
-
unless defined? @default_parameters
|
83
|
-
@default_parameters = {}
|
84
|
-
ancestors[1..-1].each do |parent|
|
85
|
-
if parent.respond_to?(:default_parameters)
|
86
|
-
pdp = parent.default_parameters
|
87
|
-
@default_parameters = @default_parameters.merge(pdp)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
if not params.empty?
|
92
|
-
@default_parameters = @default_parameters.merge(params)
|
93
|
-
end
|
94
|
-
@default_parameters
|
50
|
+
|
51
|
+
define_x_parameters(:bindata_optional, []) do |array, args|
|
52
|
+
args.each { |arg| array << arg.to_sym }
|
53
|
+
array.uniq!
|
95
54
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# a class will include the parameters of its ancestors.
|
101
|
-
def mutually_exclusive_parameters(*args)
|
102
|
-
unless defined? @mutually_exclusive_parameters
|
103
|
-
@mutually_exclusive_parameters = []
|
104
|
-
ancestors[1..-1].each do |parent|
|
105
|
-
if parent.respond_to?(:mutually_exclusive_parameters)
|
106
|
-
pmep = parent.mutually_exclusive_parameters
|
107
|
-
@mutually_exclusive_parameters.concat(pmep)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
if not args.empty?
|
112
|
-
@mutually_exclusive_parameters << [args[0].to_sym, args[1].to_sym]
|
113
|
-
end
|
114
|
-
@mutually_exclusive_parameters
|
55
|
+
|
56
|
+
define_x_parameters(:bindata_default, {}) do |hash, args|
|
57
|
+
params = args.length > 0 ? args[0] : {}
|
58
|
+
hash.merge!(params)
|
115
59
|
end
|
116
60
|
|
117
|
-
|
118
|
-
|
119
|
-
(mandatory_parameters + optional_parameters + default_parameters.keys).uniq
|
61
|
+
define_x_parameters(:bindata_mutually_exclusive, []) do |array, args|
|
62
|
+
array << [args[0].to_sym, args[1].to_sym]
|
120
63
|
end
|
121
64
|
|
122
|
-
# Returns a
|
123
|
-
|
124
|
-
|
125
|
-
|
65
|
+
# Returns a list of internal parameters that are accepted by this object
|
66
|
+
def internal_parameters
|
67
|
+
(bindata_mandatory_parameters + bindata_optional_parameters +
|
68
|
+
bindata_default_parameters.keys).uniq
|
69
|
+
end
|
126
70
|
|
71
|
+
# Ensures that +params+ is of the form expected by #initialize.
|
72
|
+
def sanitize_parameters!(sanitizer, params)
|
127
73
|
# replace :readwrite with :onlyif
|
128
74
|
if params.has_key?(:readwrite)
|
129
75
|
warn ":readwrite is deprecated. Replacing with :onlyif"
|
@@ -131,12 +77,12 @@ module BinData
|
|
131
77
|
end
|
132
78
|
|
133
79
|
# add default parameters
|
134
|
-
|
80
|
+
bindata_default_parameters.each do |k,v|
|
135
81
|
params[k] = v unless params.has_key?(k)
|
136
82
|
end
|
137
83
|
|
138
84
|
# ensure mandatory parameters exist
|
139
|
-
|
85
|
+
bindata_mandatory_parameters.each do |prm|
|
140
86
|
if not params.has_key?(prm)
|
141
87
|
raise ArgumentError, "parameter ':#{prm}' must be specified " +
|
142
88
|
"in #{self}"
|
@@ -144,14 +90,17 @@ module BinData
|
|
144
90
|
end
|
145
91
|
|
146
92
|
# ensure mutual exclusion
|
147
|
-
|
93
|
+
bindata_mutually_exclusive_parameters.each do |param1, param2|
|
148
94
|
if params.has_key?(param1) and params.has_key?(param2)
|
149
95
|
raise ArgumentError, "params #{param1} and #{param2} " +
|
150
96
|
"are mutually exclusive"
|
151
97
|
end
|
152
98
|
end
|
99
|
+
end
|
153
100
|
|
154
|
-
|
101
|
+
# Can this data object self reference itself?
|
102
|
+
def recursive?
|
103
|
+
false
|
155
104
|
end
|
156
105
|
|
157
106
|
# Instantiates this class and reads from +io+. For single value objects
|
@@ -171,26 +120,27 @@ module BinData
|
|
171
120
|
end
|
172
121
|
|
173
122
|
# Define the parameters we use in this class.
|
174
|
-
|
175
|
-
|
176
|
-
|
123
|
+
bindata_optional_parameters :check_offset, :adjust_offset
|
124
|
+
bindata_default_parameters :onlyif => true
|
125
|
+
bindata_mutually_exclusive_parameters :check_offset, :adjust_offset
|
177
126
|
|
178
127
|
# Creates a new data object.
|
179
128
|
#
|
180
129
|
# +params+ is a hash containing symbol keys. Some params may
|
181
|
-
# reference callable objects (methods or procs). +
|
182
|
-
#
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
130
|
+
# reference callable objects (methods or procs). +parent+ is the
|
131
|
+
# parent data object (e.g. struct, array, choice) this object resides
|
132
|
+
# under.
|
133
|
+
def initialize(params = {}, parent = nil)
|
134
|
+
@params = Sanitizer.sanitize(self.class, params)
|
135
|
+
@parent = parent
|
136
|
+
end
|
187
137
|
|
188
|
-
|
138
|
+
# The parent data object.
|
139
|
+
attr_accessor :parent
|
189
140
|
|
190
|
-
|
191
|
-
|
192
|
-
@
|
193
|
-
@env.data_object = self
|
141
|
+
# Returns all the custom parameters supplied to this data object.
|
142
|
+
def parameters
|
143
|
+
@params.extra_parameters
|
194
144
|
end
|
195
145
|
|
196
146
|
# Reads data into this data object by calling #do_read then #done_read.
|
@@ -272,33 +222,33 @@ module BinData
|
|
272
222
|
snapshot.inspect
|
273
223
|
end
|
274
224
|
|
225
|
+
# Returns the object this object represents.
|
226
|
+
def obj
|
227
|
+
self
|
228
|
+
end
|
229
|
+
|
275
230
|
#---------------
|
276
231
|
private
|
277
232
|
|
278
|
-
# Creates a new LazyEvalEnv for use by a child data object.
|
279
|
-
def create_env
|
280
|
-
LazyEvalEnv.new(@env)
|
281
|
-
end
|
282
|
-
|
283
233
|
# Returns the value of the evaluated parameter. +key+ references a
|
284
234
|
# parameter from the +params+ hash used when creating the data object.
|
285
235
|
# +values+ contains data that may be accessed when evaluating +key+.
|
286
236
|
# Returns nil if +key+ does not refer to any parameter.
|
287
237
|
def eval_param(key, values = nil)
|
288
|
-
|
238
|
+
LazyEvaluator.eval(no_eval_param(key), self, values)
|
289
239
|
end
|
290
240
|
|
291
241
|
# Returns the parameter from the +params+ hash referenced by +key+.
|
292
242
|
# Use this method if you are sure the parameter is not to be evaluated.
|
293
243
|
# You most likely want #eval_param.
|
294
|
-
def
|
295
|
-
@params[key]
|
244
|
+
def no_eval_param(key)
|
245
|
+
@params.internal_parameters[key]
|
296
246
|
end
|
297
247
|
|
298
248
|
# Returns whether +key+ exists in the +params+ hash used when creating
|
299
249
|
# this data object.
|
300
250
|
def has_param?(key)
|
301
|
-
@params.has_key?(key
|
251
|
+
@params.internal_parameters.has_key?(key)
|
302
252
|
end
|
303
253
|
|
304
254
|
# Checks that the current offset of +io+ is as expected. This should
|