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.

Files changed (61) hide show
  1. data/ChangeLog.rdoc +7 -0
  2. data/NEWS.rdoc +11 -0
  3. data/Rakefile +6 -1
  4. data/bindata.gemspec +2 -1
  5. data/doc/manual.md +17 -9
  6. data/examples/gzip.rb +2 -2
  7. data/examples/list.rb +2 -2
  8. data/lib/bindata/alignment.rb +4 -9
  9. data/lib/bindata/array.rb +57 -51
  10. data/lib/bindata/base.rb +13 -110
  11. data/lib/bindata/base_primitive.rb +130 -75
  12. data/lib/bindata/bits.rb +5 -7
  13. data/lib/bindata/choice.rb +24 -32
  14. data/lib/bindata/dsl.rb +1 -6
  15. data/lib/bindata/framework.rb +81 -0
  16. data/lib/bindata/int.rb +5 -7
  17. data/lib/bindata/name.rb +28 -0
  18. data/lib/bindata/offset.rb +42 -53
  19. data/lib/bindata/params.rb +33 -38
  20. data/lib/bindata/struct.rb +2 -6
  21. data/lib/bindata/trace.rb +16 -16
  22. data/lib/bindata/version.rb +1 -1
  23. data/lib/bindata/virtual.rb +3 -3
  24. data/{spec/alignment_spec.rb → test/alignment_test.rb} +17 -16
  25. data/test/array_test.rb +371 -0
  26. data/test/base_primitive_test.rb +312 -0
  27. data/test/base_test.rb +183 -0
  28. data/{spec/bits_spec.rb → test/bits_test.rb} +59 -59
  29. data/test/choice_test.rb +260 -0
  30. data/{spec/spec_common.rb → test/common.rb} +33 -18
  31. data/test/count_bytes_remaining_test.rb +41 -0
  32. data/{spec/deprecated_spec.rb → test/deprecated_test.rb} +5 -7
  33. data/test/float_test.rb +72 -0
  34. data/{spec/int_spec.rb → test/int_test.rb} +34 -43
  35. data/{spec/io_spec.rb → test/io_test.rb} +70 -71
  36. data/{spec/lazy_spec.rb → test/lazy_test.rb} +38 -39
  37. data/test/offset_test.rb +93 -0
  38. data/test/params_test.rb +144 -0
  39. data/{spec/primitive_spec.rb → test/primitive_test.rb} +42 -54
  40. data/{spec/record_spec.rb → test/record_test.rb} +133 -154
  41. data/test/registry_test.rb +104 -0
  42. data/test/rest_test.rb +29 -0
  43. data/test/skip_test.rb +28 -0
  44. data/{spec/string_spec.rb → test/string_test.rb} +96 -97
  45. data/test/stringz_test.rb +127 -0
  46. data/{spec/struct_spec.rb → test/struct_test.rb} +119 -120
  47. data/{spec/system_spec.rb → test/system_test.rb} +66 -106
  48. metadata +39 -38
  49. data/lib/a.rb +0 -24
  50. data/spec/array_spec.rb +0 -331
  51. data/spec/base_primitive_spec.rb +0 -238
  52. data/spec/base_spec.rb +0 -376
  53. data/spec/choice_spec.rb +0 -263
  54. data/spec/count_bytes_remaining_spec.rb +0 -38
  55. data/spec/example.rb +0 -21
  56. data/spec/float_spec.rb +0 -37
  57. data/spec/registry_spec.rb +0 -108
  58. data/spec/rest_spec.rb +0 -26
  59. data/spec/skip_spec.rb +0 -27
  60. data/spec/stringz_spec.rb +0 -118
  61. 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>] 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>:check_value</tt>] Raise an error unless the value read in meets
42
- # this criteria. The variable +value+ is made
43
- # 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.
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
- class << self
56
- alias_method :do_read_without_check_value, :do_read
57
- alias_method :do_read, :do_read_with_check_value
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
- def initialize_instance
73
- @value = nil
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 clear #:nodoc:
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
- unless has_parameter?(:value)
88
- raw_val = val.respond_to?(:snapshot) ? val.snapshot : val
89
- @value = begin
90
- raw_val.dup
91
- rescue TypeError
92
- # can't dup Fixnums
93
- raw_val
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
- def _value_with_value #:nodoc:
186
- reading? ? @value : eval_parameter(:value)
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
- def _value_with_initial_value #:nodoc:
190
- @value != nil ? @value : eval_parameter(:initial_value)
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
- class << self
68
- alias_method :const_missing_without_bits, :const_missing
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
- const_missing_without_bits(name)
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
@@ -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
- class << self
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
- # A convenience method that returns the current selection.
119
+ # Returns the current selection.
123
120
  def selection
124
- eval_parameter(:selection)
125
- end
126
-
127
- def clear #:nodoc:
128
- initialize_instance
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
- orig_respond_to?(symbol, include_private)
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
- selection = eval_parameter(:selection)
176
- if selection.nil?
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(selection, obj)
199
- prev = get_previous_choice(selection)
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(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 do |name|
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