bindata 1.5.1 → 1.6.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.
- data/ChangeLog.rdoc +7 -0
- data/NEWS.rdoc +11 -0
- data/Rakefile +6 -1
- data/bindata.gemspec +2 -1
- data/doc/manual.md +17 -9
- data/examples/gzip.rb +2 -2
- data/examples/list.rb +2 -2
- data/lib/bindata/alignment.rb +4 -9
- data/lib/bindata/array.rb +57 -51
- data/lib/bindata/base.rb +13 -110
- data/lib/bindata/base_primitive.rb +130 -75
- data/lib/bindata/bits.rb +5 -7
- data/lib/bindata/choice.rb +24 -32
- data/lib/bindata/dsl.rb +1 -6
- data/lib/bindata/framework.rb +81 -0
- data/lib/bindata/int.rb +5 -7
- data/lib/bindata/name.rb +28 -0
- data/lib/bindata/offset.rb +42 -53
- data/lib/bindata/params.rb +33 -38
- data/lib/bindata/struct.rb +2 -6
- data/lib/bindata/trace.rb +16 -16
- data/lib/bindata/version.rb +1 -1
- data/lib/bindata/virtual.rb +3 -3
- data/{spec/alignment_spec.rb → test/alignment_test.rb} +17 -16
- data/test/array_test.rb +371 -0
- data/test/base_primitive_test.rb +312 -0
- data/test/base_test.rb +183 -0
- data/{spec/bits_spec.rb → test/bits_test.rb} +59 -59
- data/test/choice_test.rb +260 -0
- data/{spec/spec_common.rb → test/common.rb} +33 -18
- data/test/count_bytes_remaining_test.rb +41 -0
- data/{spec/deprecated_spec.rb → test/deprecated_test.rb} +5 -7
- data/test/float_test.rb +72 -0
- data/{spec/int_spec.rb → test/int_test.rb} +34 -43
- data/{spec/io_spec.rb → test/io_test.rb} +70 -71
- data/{spec/lazy_spec.rb → test/lazy_test.rb} +38 -39
- data/test/offset_test.rb +93 -0
- data/test/params_test.rb +144 -0
- data/{spec/primitive_spec.rb → test/primitive_test.rb} +42 -54
- data/{spec/record_spec.rb → test/record_test.rb} +133 -154
- data/test/registry_test.rb +104 -0
- data/test/rest_test.rb +29 -0
- data/test/skip_test.rb +28 -0
- data/{spec/string_spec.rb → test/string_test.rb} +96 -97
- data/test/stringz_test.rb +127 -0
- data/{spec/struct_spec.rb → test/struct_test.rb} +119 -120
- data/{spec/system_spec.rb → test/system_test.rb} +66 -106
- metadata +39 -38
- data/lib/a.rb +0 -24
- data/spec/array_spec.rb +0 -331
- data/spec/base_primitive_spec.rb +0 -238
- data/spec/base_spec.rb +0 -376
- data/spec/choice_spec.rb +0 -263
- data/spec/count_bytes_remaining_spec.rb +0 -38
- data/spec/example.rb +0 -21
- data/spec/float_spec.rb +0 -37
- data/spec/registry_spec.rb +0 -108
- data/spec/rest_spec.rb +0 -26
- data/spec/skip_spec.rb +0 -27
- data/spec/stringz_spec.rb +0 -118
- data/tasks/rspec.rake +0 -17
@@ -31,49 +31,46 @@ module BinData
|
|
31
31
|
# Parameters may be provided at initialisation to control the behaviour of
|
32
32
|
# an object. These params include those for BinData::Base as well as:
|
33
33
|
#
|
34
|
-
# [<tt>:initial_value</tt>]
|
35
|
-
#
|
36
|
-
# [<tt>:value</tt>]
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# [<tt>:
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
34
|
+
# [<tt>:initial_value</tt>] This is the initial value to use before one is
|
35
|
+
# either #read or explicitly set with #value=.
|
36
|
+
# [<tt>:value</tt>] The object will always have this value.
|
37
|
+
# Calls to #value= are ignored when
|
38
|
+
# using this param. While reading, #value
|
39
|
+
# will return the value of the data read from the
|
40
|
+
# IO, not the result of the <tt>:value</tt> param.
|
41
|
+
# [<tt>:assert</tt>] Raise an error unless the value read or assigned
|
42
|
+
# meets this criteria. The variable +value+ is
|
43
|
+
# made available to any lambda assigned to this
|
44
|
+
# parameter. A boolean return indicates success
|
45
|
+
# or failure. Any other return is compared to
|
46
|
+
# the value just read in.
|
47
|
+
# [<tt>:asserted_value</tt>] Equavalent to <tt>:assert</tt> and <tt>:value</tt>.
|
48
|
+
#
|
47
49
|
class BasePrimitive < BinData::Base
|
48
50
|
unregister_self
|
49
51
|
|
50
|
-
optional_parameters :initial_value, :value, :check_value
|
52
|
+
optional_parameters :initial_value, :value, :check_value, :assert, :asserted_value
|
51
53
|
mutually_exclusive_parameters :initial_value, :value
|
54
|
+
mutually_exclusive_parameters :asserted_value, :value, :assert
|
55
|
+
mutually_exclusive_parameters :check_value, :assert
|
56
|
+
mutually_exclusive_parameters :check_value, :asserted_value
|
52
57
|
|
53
58
|
def initialize_shared_instance
|
54
|
-
if has_parameter?(:check_value)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
60
|
-
if has_parameter?(:value)
|
61
|
-
class << self
|
62
|
-
alias_method :_value, :_value_with_value
|
63
|
-
end
|
64
|
-
end
|
65
|
-
if has_parameter?(:initial_value)
|
66
|
-
class << self
|
67
|
-
alias_method :_value, :_value_with_initial_value
|
68
|
-
end
|
59
|
+
if has_parameter?(:check_value) and has_parameter?(:value)
|
60
|
+
warn ":check_value has been deprecated. Consider using :asserted_value instead of :check_value and :value in #{self.class}."
|
61
|
+
elsif has_parameter?(:check_value)
|
62
|
+
warn ":check_value has been deprecated. Use :assert instead in #{self.class}."
|
69
63
|
end
|
70
|
-
end
|
71
64
|
|
72
|
-
|
73
|
-
|
65
|
+
extend InitialValuePlugin if has_parameter?(:initial_value)
|
66
|
+
extend ValuePlugin if has_parameter?(:value)
|
67
|
+
extend CheckValuePlugin if has_parameter?(:check_value)
|
68
|
+
extend AssertPlugin if has_parameter?(:assert)
|
69
|
+
extend AssertedValuePlugin if has_parameter?(:asserted_value)
|
70
|
+
super
|
74
71
|
end
|
75
72
|
|
76
|
-
def
|
73
|
+
def initialize_instance
|
77
74
|
@value = nil
|
78
75
|
end
|
79
76
|
|
@@ -84,15 +81,13 @@ module BinData
|
|
84
81
|
def assign(val)
|
85
82
|
raise ArgumentError, "can't set a nil value for #{debug_name}" if val.nil?
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
84
|
+
raw_val = val.respond_to?(:snapshot) ? val.snapshot : val
|
85
|
+
@value = begin
|
86
|
+
raw_val.dup
|
87
|
+
rescue TypeError
|
88
|
+
# can't dup Fixnums
|
89
|
+
raw_val
|
90
|
+
end
|
96
91
|
end
|
97
92
|
|
98
93
|
def snapshot
|
@@ -133,12 +128,6 @@ module BinData
|
|
133
128
|
|
134
129
|
def do_read(io) #:nodoc:
|
135
130
|
@value = read_and_return_value(io)
|
136
|
-
hook_after_do_read
|
137
|
-
end
|
138
|
-
|
139
|
-
def do_read_with_check_value(io) #:nodoc:
|
140
|
-
do_read_without_check_value(io)
|
141
|
-
check_value(snapshot)
|
142
131
|
end
|
143
132
|
|
144
133
|
def do_write(io) #:nodoc:
|
@@ -152,42 +141,108 @@ module BinData
|
|
152
141
|
#---------------
|
153
142
|
private
|
154
143
|
|
155
|
-
def hook_after_do_read; end
|
156
|
-
|
157
|
-
def check_value(current_value)
|
158
|
-
expected = eval_parameter(:check_value, :value => current_value)
|
159
|
-
if not expected
|
160
|
-
raise ValidityError,
|
161
|
-
"value '#{current_value}' not as expected for #{debug_name}"
|
162
|
-
elsif current_value != expected and expected != true
|
163
|
-
raise ValidityError,
|
164
|
-
"value is '#{current_value}' but " +
|
165
|
-
"expected '#{expected}' for #{debug_name}"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
144
|
# The unmodified value of this data object. Note that #snapshot calls this
|
170
145
|
# method. This indirection is so that #snapshot can be overridden in
|
171
146
|
# subclasses to modify the presentation value.
|
172
|
-
#
|
173
|
-
# Table of possible preconditions and expected outcome
|
174
|
-
# 1. :value and !reading? -> :value
|
175
|
-
# 2. :value and reading? -> @value
|
176
|
-
# 3. :initial_value and clear? -> :initial_value
|
177
|
-
# 4. :initial_value and !clear? -> @value
|
178
|
-
# 5. clear? -> sensible_default
|
179
|
-
# 6. !clear? -> @value
|
180
|
-
|
181
147
|
def _value
|
182
148
|
@value != nil ? @value : sensible_default()
|
183
149
|
end
|
184
150
|
|
185
|
-
|
186
|
-
|
151
|
+
# Logic for the :value parameter
|
152
|
+
module ValuePlugin
|
153
|
+
def assign(val)
|
154
|
+
# Ignored
|
155
|
+
end
|
156
|
+
|
157
|
+
def _value
|
158
|
+
reading? ? @value : eval_parameter(:value)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Logic for the :initial_value parameter
|
163
|
+
module InitialValuePlugin
|
164
|
+
def _value
|
165
|
+
@value != nil ? @value : eval_parameter(:initial_value)
|
166
|
+
end
|
187
167
|
end
|
188
168
|
|
189
|
-
|
190
|
-
|
169
|
+
# Logic for the :check_value parameter
|
170
|
+
module CheckValuePlugin
|
171
|
+
def do_read(io) #:nodoc:
|
172
|
+
super(io)
|
173
|
+
check_value(snapshot)
|
174
|
+
end
|
175
|
+
|
176
|
+
def check_value(current_value)
|
177
|
+
expected = eval_parameter(:check_value, :value => current_value)
|
178
|
+
if not expected
|
179
|
+
raise ValidityError,
|
180
|
+
"value '#{current_value}' not as expected for #{debug_name}"
|
181
|
+
elsif current_value != expected and expected != true
|
182
|
+
raise ValidityError,
|
183
|
+
"value is '#{current_value}' but " +
|
184
|
+
"expected '#{expected}' for #{debug_name}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Logic for the :assert parameter
|
190
|
+
module AssertPlugin
|
191
|
+
def assign(val)
|
192
|
+
super(val)
|
193
|
+
assert!
|
194
|
+
end
|
195
|
+
|
196
|
+
def do_read(io) #:nodoc:
|
197
|
+
super(io)
|
198
|
+
assert!
|
199
|
+
end
|
200
|
+
|
201
|
+
def assert!
|
202
|
+
current_value = snapshot
|
203
|
+
expected = eval_parameter(:assert, :value => current_value)
|
204
|
+
if not expected
|
205
|
+
raise ValidityError,
|
206
|
+
"value '#{current_value}' not as expected for #{debug_name}"
|
207
|
+
elsif current_value != expected and expected != true
|
208
|
+
raise ValidityError,
|
209
|
+
"value is '#{current_value}' but " +
|
210
|
+
"expected '#{expected}' for #{debug_name}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Logic for the :asserted_value parameter
|
216
|
+
module AssertedValuePlugin
|
217
|
+
def assign(val)
|
218
|
+
assert_value(val)
|
219
|
+
super(val)
|
220
|
+
end
|
221
|
+
|
222
|
+
def _value
|
223
|
+
reading? ? @value : eval_parameter(:asserted_value)
|
224
|
+
end
|
225
|
+
|
226
|
+
def do_read(io) #:nodoc:
|
227
|
+
super(io)
|
228
|
+
assert!
|
229
|
+
end
|
230
|
+
|
231
|
+
def assert!
|
232
|
+
assert_value(snapshot)
|
233
|
+
end
|
234
|
+
|
235
|
+
def assert_value(current_value)
|
236
|
+
expected = eval_parameter(:asserted_value, :value => current_value)
|
237
|
+
if not expected
|
238
|
+
raise ValidityError,
|
239
|
+
"value '#{current_value}' not as expected for #{debug_name}"
|
240
|
+
elsif current_value != expected and expected != true
|
241
|
+
raise ValidityError,
|
242
|
+
"value is '#{current_value}' but " +
|
243
|
+
"expected '#{expected}' for #{debug_name}"
|
244
|
+
end
|
245
|
+
end
|
191
246
|
end
|
192
247
|
|
193
248
|
###########################################################################
|
data/lib/bindata/bits.rb
CHANGED
@@ -64,24 +64,22 @@ module BinData
|
|
64
64
|
end
|
65
65
|
|
66
66
|
# Create classes on demand
|
67
|
-
|
68
|
-
|
69
|
-
def const_missing_with_bits(name)
|
70
|
-
name = name.to_s
|
67
|
+
module BitFieldFactory
|
68
|
+
def const_missing(name)
|
71
69
|
mappings = {
|
72
70
|
/^Bit(\d+)$/ => :big,
|
73
71
|
/^Bit(\d+)le$/ => :little
|
74
72
|
}
|
75
73
|
|
76
74
|
mappings.each_pair do |regex, endian|
|
77
|
-
if regex =~ name
|
75
|
+
if regex =~ name.to_s
|
78
76
|
nbits = $1.to_i
|
79
77
|
return BitField.define_class(nbits, endian)
|
80
78
|
end
|
81
79
|
end
|
82
80
|
|
83
|
-
|
81
|
+
super(name)
|
84
82
|
end
|
85
|
-
alias_method :const_missing, :const_missing_with_bits
|
86
83
|
end
|
84
|
+
BinData.extend BitFieldFactory
|
87
85
|
end
|
data/lib/bindata/choice.rb
CHANGED
@@ -107,11 +107,8 @@ module BinData
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def initialize_shared_instance
|
110
|
-
if eval_parameter(:copy_on_change) == true
|
111
|
-
|
112
|
-
alias_method :hook_after_current_choice, :copy_previous_value
|
113
|
-
end
|
114
|
-
end
|
110
|
+
extend CopyOnChangePlugin if eval_parameter(:copy_on_change) == true
|
111
|
+
super
|
115
112
|
end
|
116
113
|
|
117
114
|
def initialize_instance
|
@@ -119,13 +116,13 @@ module BinData
|
|
119
116
|
@last_selection = nil
|
120
117
|
end
|
121
118
|
|
122
|
-
#
|
119
|
+
# Returns the current selection.
|
123
120
|
def selection
|
124
|
-
eval_parameter(:selection)
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
121
|
+
selection = eval_parameter(:selection)
|
122
|
+
if selection.nil?
|
123
|
+
raise IndexError, ":selection returned nil for #{debug_name}"
|
124
|
+
end
|
125
|
+
selection
|
129
126
|
end
|
130
127
|
|
131
128
|
def clear? #:nodoc:
|
@@ -145,7 +142,7 @@ module BinData
|
|
145
142
|
end
|
146
143
|
|
147
144
|
def safe_respond_to?(symbol, include_private = false) #:nodoc:
|
148
|
-
|
145
|
+
base_respond_to?(symbol, include_private)
|
149
146
|
end
|
150
147
|
|
151
148
|
def method_missing(symbol, *args, &block) #:nodoc:
|
@@ -153,7 +150,6 @@ module BinData
|
|
153
150
|
end
|
154
151
|
|
155
152
|
def do_read(io) #:nodoc:
|
156
|
-
hook_before_do_read
|
157
153
|
current_choice.do_read(io)
|
158
154
|
end
|
159
155
|
|
@@ -168,23 +164,9 @@ module BinData
|
|
168
164
|
#---------------
|
169
165
|
private
|
170
166
|
|
171
|
-
def hook_before_do_read; end
|
172
|
-
def hook_after_current_choice(*args); end
|
173
|
-
|
174
167
|
def current_choice
|
175
|
-
|
176
|
-
|
177
|
-
raise IndexError, ":selection returned nil for #{debug_name}"
|
178
|
-
end
|
179
|
-
|
180
|
-
obj = get_or_instantiate_choice(selection)
|
181
|
-
hook_after_current_choice(selection, obj)
|
182
|
-
|
183
|
-
obj
|
184
|
-
end
|
185
|
-
|
186
|
-
def get_or_instantiate_choice(selection)
|
187
|
-
@choices[selection] ||= instantiate_choice(selection)
|
168
|
+
current_selection = selection
|
169
|
+
@choices[current_selection] ||= instantiate_choice(current_selection)
|
188
170
|
end
|
189
171
|
|
190
172
|
def instantiate_choice(selection)
|
@@ -194,11 +176,21 @@ module BinData
|
|
194
176
|
end
|
195
177
|
prototype.instantiate(nil, self)
|
196
178
|
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Logic for the :copy_on_change parameter
|
182
|
+
module CopyOnChangePlugin
|
183
|
+
def current_choice
|
184
|
+
obj = super
|
185
|
+
copy_previous_value(obj)
|
186
|
+
obj
|
187
|
+
end
|
197
188
|
|
198
|
-
def copy_previous_value(
|
199
|
-
|
189
|
+
def copy_previous_value(obj)
|
190
|
+
current_selection = selection
|
191
|
+
prev = get_previous_choice(current_selection)
|
200
192
|
obj.assign(prev) unless prev.nil?
|
201
|
-
remember_current_selection(
|
193
|
+
remember_current_selection(current_selection)
|
202
194
|
end
|
203
195
|
|
204
196
|
def get_previous_choice(selection)
|
data/lib/bindata/dsl.rb
CHANGED
@@ -53,12 +53,7 @@ module BinData
|
|
53
53
|
|
54
54
|
def hide(*args)
|
55
55
|
if option?(:hidden_fields)
|
56
|
-
hidden = args.collect
|
57
|
-
unless Symbol === name
|
58
|
-
warn "Hidden field '#{name}' should be provided as a symbol. Using strings is deprecated"
|
59
|
-
end
|
60
|
-
name.to_sym
|
61
|
-
end
|
56
|
+
hidden = args.collect { |name| name.to_sym }
|
62
57
|
|
63
58
|
unless defined? @hide
|
64
59
|
@hide = parent_attribute(:hide, []).dup
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module BinData
|
2
|
+
# Error raised when unexpected results occur when reading data from IO.
|
3
|
+
class ValidityError < StandardError ; end
|
4
|
+
|
5
|
+
# All methods provided by the framework are to be implemented or overridden
|
6
|
+
# by subclasses of BinData::Base.
|
7
|
+
module Framework
|
8
|
+
def self.included(base) #:nodoc:
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods #:nodoc:
|
13
|
+
# Performs sanity checks on the given parameters. This method converts
|
14
|
+
# the parameters to the form expected by this data object.
|
15
|
+
def sanitize_parameters!(parameters)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Initializes the state of the object. All instance variables that
|
20
|
+
# are used by the object must be initialized here.
|
21
|
+
def initialize_instance
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initialises state that is shared by objects with the same parameters.
|
25
|
+
#
|
26
|
+
# This should only be used when optimising for performance. Instance
|
27
|
+
# variables set here, and changes to the singleton class will be shared
|
28
|
+
# between all objects that are initialized with the same parameters.
|
29
|
+
# This method is called only once for a particular set of parameters.
|
30
|
+
def initialize_shared_instance
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns true if the object has not been changed since creation.
|
34
|
+
def clear?
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
# Assigns the value of +val+ to this data object. Note that +val+ must
|
39
|
+
# always be deep copied to ensure no aliasing problems can occur.
|
40
|
+
def assign(val)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a snapshot of this data object.
|
45
|
+
def snapshot
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the debug name of +child+. This only needs to be implemented
|
50
|
+
# by objects that contain child objects.
|
51
|
+
def debug_name_of(child) #:nodoc:
|
52
|
+
debug_name
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the offset of +child+. This only needs to be implemented
|
56
|
+
# by objects that contain child objects.
|
57
|
+
def offset_of(child) #:nodoc:
|
58
|
+
0
|
59
|
+
end
|
60
|
+
|
61
|
+
# Reads the data for this data object from +io+.
|
62
|
+
def do_read(io) #:nodoc:
|
63
|
+
raise NotImplementedError
|
64
|
+
end
|
65
|
+
|
66
|
+
# Writes the value for this data to +io+.
|
67
|
+
def do_write(io) #:nodoc:
|
68
|
+
raise NotImplementedError
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the number of bytes it will take to write this data.
|
72
|
+
def do_num_bytes #:nodoc:
|
73
|
+
raise NotImplementedError
|
74
|
+
end
|
75
|
+
|
76
|
+
# Set visibility requirements of methods to implement
|
77
|
+
public :clear?, :assign, :snapshot, :debug_name_of, :offset_of
|
78
|
+
protected :initialize_instance, :initialize_shared_instance
|
79
|
+
protected :do_read, :do_write, :do_num_bytes
|
80
|
+
end
|
81
|
+
end
|