bindata 0.10.0 → 0.11.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.

@@ -9,24 +9,20 @@ module BinData
9
9
  end
10
10
 
11
11
  def register(name, class_to_register)
12
- formatted_name = underscore_name(name)
12
+ formatted_name = lookup_key(name)
13
13
  warn_if_name_is_already_registered(formatted_name, class_to_register)
14
14
 
15
15
  @registry[formatted_name] = class_to_register
16
16
  end
17
17
 
18
18
  def lookup(name, endian = nil)
19
- key = underscore_name(name.to_s)
19
+ key = lookup_key(name, endian)
20
20
 
21
- result = @registry[key]
22
- if result.nil?
23
- result = @registry[merge_key_and_endian(key, endian)]
24
- end
25
- result
21
+ @registry[key] || lookup_int(key)
26
22
  end
27
23
 
28
24
  def is_registered?(name, endian = nil)
29
- lookup(name, endian) != nil
25
+ @registry.has_key?(lookup_key(name, endian))
30
26
  end
31
27
 
32
28
  # Convert camelCase +name+ to underscore style.
@@ -41,18 +37,41 @@ module BinData
41
37
  #---------------
42
38
  private
43
39
 
44
- def merge_key_and_endian(key, endian)
45
- result = key
40
+ def lookup_key(name, endian = nil)
41
+ name = underscore_name(name)
42
+
43
+ result = name
46
44
  if endian != nil
47
- if /^u?int\d+$/ =~ key
48
- result = key + ((endian == :little) ? "le" : "be")
49
- elsif /^(float|double)$/ =~ key
50
- result = key + ((endian == :little) ? "_le" : "_be")
45
+ if /^u?int\d+$/ =~ name
46
+ result = name + ((endian == :little) ? "le" : "be")
47
+ elsif /^(float|double)$/ =~ name
48
+ result = name + ((endian == :little) ? "_le" : "_be")
51
49
  end
52
50
  end
53
51
  result
54
52
  end
55
53
 
54
+ def lookup_int(key)
55
+ if /^(u?)int(\d+)(le|be)$/ =~ key
56
+ signed = $1 == "u" ? :unsigned : :signed
57
+ nbits = $2.to_i
58
+ endian = $3 == "le" ? :little : :big
59
+ if nbits > 0 and (nbits % 8) == 0
60
+ if BinData.const_defined?(:Integer)
61
+ BinData::Integer.define_class(nbits, endian, signed)
62
+ end
63
+ end
64
+ elsif /^bit(\d+)(le)?$/ =~ key
65
+ nbits = $1.to_i
66
+ endian = $2 == "le" ? :little : :big
67
+ if BinData.const_defined?(:BitField)
68
+ BinData::BitField.define_class(nbits, endian)
69
+ end
70
+ end
71
+
72
+ @registry[key]
73
+ end
74
+
56
75
  def warn_if_name_is_already_registered(name, class_to_register)
57
76
  if $VERBOSE and @registry.has_key?(name)
58
77
  prev_class = @registry[name]
@@ -64,11 +83,4 @@ module BinData
64
83
 
65
84
  # A singleton registry of all registered classes.
66
85
  RegisteredClasses = Registry.new
67
-
68
- class Registry
69
- def Registry.instance
70
- warn "'Registry.instance' is deprecated. Replacing with 'RegisteredClasses'"
71
- RegisteredClasses
72
- end
73
- end
74
86
  end
@@ -1,30 +1,112 @@
1
1
  require 'bindata/registry'
2
- require 'forwardable'
3
2
 
4
3
  module BinData
5
4
 
6
- # A BinData object accepts arbitrary parameters. This class only contains
7
- # parameters that have been sanitized.
5
+ # A BinData object accepts arbitrary parameters. This class sanitizes
6
+ # those parameters so they can be used by the BinData object.
8
7
  class SanitizedParameters
9
- extend Forwardable
10
8
 
11
- def initialize(the_class, params)
9
+ def initialize(params, the_class)
10
+ @all_sanitized = false
11
+ @the_class = the_class
12
+
12
13
  @parameters = {}
14
+ params.each { |param, value| @parameters[param.to_sym] = value }
15
+
16
+ ensure_no_nil_values
17
+ end
18
+
19
+ def length
20
+ @parameters.size
21
+ end
22
+ alias_method :size, :length
13
23
 
14
- params.each do |k,v|
15
- k = k.to_sym
16
- if v.nil?
17
- raise ArgumentError, "parameter :#{k} has nil value in #{the_class}"
24
+ def [](param)
25
+ @parameters[param]
26
+ end
27
+
28
+ def []=(param, value)
29
+ @parameters[param] = value unless @all_sanitized
30
+ end
31
+
32
+ def has_parameter?(param)
33
+ @parameters.has_key?(param)
34
+ end
35
+
36
+ def needs_sanitizing?(param)
37
+ has_parameter?(param) and not self[param].is_a?(SanitizedParameter)
38
+ end
39
+
40
+ def all_sanitized?
41
+ @all_sanitized
42
+ end
43
+
44
+ def sanitize!(sanitizer)
45
+ unless @all_sanitized
46
+ merge_default_parameters!
47
+
48
+ @the_class.sanitize_parameters!(self, sanitizer)
49
+
50
+ ensure_mandatory_parameters_exist
51
+ ensure_mutual_exclusion_of_parameters
52
+
53
+ @all_sanitized = true
54
+ end
55
+ end
56
+
57
+ def move_unknown_parameters_to(dest)
58
+ unless @all_sanitized
59
+ unused_keys = @parameters.keys - @the_class.accepted_parameters.all
60
+ unused_keys.each do |param|
61
+ dest[param] = @parameters.delete(param)
62
+ end
63
+ end
64
+ end
65
+
66
+ def delete(param)
67
+ @parameters.delete(param)
68
+ end
69
+
70
+ #---------------
71
+ private
72
+
73
+ def ensure_no_nil_values
74
+ @parameters.each do |param, value|
75
+ if value.nil?
76
+ raise ArgumentError,
77
+ "parameter '#{param}' has nil value in #{@the_class}"
18
78
  end
79
+ end
80
+ end
19
81
 
20
- @parameters[k] = v
82
+ def merge_default_parameters!
83
+ @the_class.default_parameters.each do |param, value|
84
+ self[param] ||= value
21
85
  end
22
86
  end
23
87
 
24
- attr_reader :parameters
88
+ def ensure_mandatory_parameters_exist
89
+ @the_class.mandatory_parameters.each do |param|
90
+ unless has_parameter?(param)
91
+ raise ArgumentError,
92
+ "parameter '#{param}' must be specified in #{@the_class}"
93
+ end
94
+ end
95
+ end
96
+
97
+ def ensure_mutual_exclusion_of_parameters
98
+ return if length < 2
99
+
100
+ @the_class.mutually_exclusive_parameters.each do |param1, param2|
101
+ if has_parameter?(param1) and has_parameter?(param2)
102
+ raise ArgumentError, "params '#{param1}' and '#{param2}' " +
103
+ "are mutually exclusive in #{@the_class}"
104
+ end
105
+ end
106
+ end
25
107
 
26
- def_delegators :@parameters, :[], :has_key?, :include?, :keys
27
108
  end
109
+ #----------------------------------------------------------------------------
28
110
 
29
111
  # The Sanitizer sanitizes the parameters that are passed when creating a
30
112
  # BinData object. Sanitizing consists of checking for mandatory, optional
@@ -35,26 +117,47 @@ module BinData
35
117
  class << self
36
118
  # Sanitize +params+ for +the_class+.
37
119
  # Returns sanitized parameters.
38
- def sanitize(the_class, params)
39
- if SanitizedParameters === params
120
+ def sanitize(params, the_class)
121
+ if params.is_a?(SanitizedParameters) and params.all_sanitized?
40
122
  params
41
123
  else
42
124
  sanitizer = self.new
43
- sanitizer.sanitized_params(the_class, params)
125
+ sanitizer.create_sanitized_params(params, the_class)
44
126
  end
45
127
  end
46
128
  end
47
129
 
48
130
  def initialize
49
131
  @endian = nil
50
- @seen = []
51
132
  end
52
133
 
53
- # Executes the given block with +endian+ set as the current endian.
134
+ def create_sanitized_params(params, the_class)
135
+ sanitized_params = as_sanitized_params(params, the_class)
136
+ sanitized_params.sanitize!(self)
137
+
138
+ sanitized_params
139
+ end
140
+
141
+ def create_sanitized_endian(endian)
142
+ SanitizedEndian.new(endian)
143
+ end
144
+
145
+ def create_sanitized_choices(choices)
146
+ SanitizedChoices.new(self, choices)
147
+ end
148
+
149
+ def create_sanitized_fields(endian = nil)
150
+ SanitizedFields.new(self, endian)
151
+ end
152
+
153
+ def create_sanitized_object_prototype(obj_type, obj_params, endian = nil)
154
+ SanitizedPrototype.new(self, obj_type, obj_params, endian)
155
+ end
156
+
54
157
  def with_endian(endian, &block)
55
158
  if endian != nil
56
159
  saved_endian = @endian
57
- @endian = endian
160
+ @endian = endian.is_a?(SanitizedEndian) ? endian.endian : endian
58
161
  yield
59
162
  @endian = saved_endian
60
163
  else
@@ -70,53 +173,96 @@ module BinData
70
173
  registered_class
71
174
  end
72
175
 
73
- def sanitized_params(the_class, params)
74
- new_params = params.nil? ? {} : params.dup
176
+ #---------------
177
+ private
75
178
 
76
- if can_sanitize_parameters?(the_class)
77
- get_sanitized_params(the_class, new_params)
179
+ def as_sanitized_params(params, the_class)
180
+ if SanitizedParameters === params
181
+ params
78
182
  else
79
- store_current_endian!(the_class, new_params)
80
- new_params
183
+ SanitizedParameters.new(params || {}, the_class)
81
184
  end
82
185
  end
186
+ end
187
+ #----------------------------------------------------------------------------
83
188
 
84
- #---------------
85
- private
189
+ class SanitizedParameter; end
86
190
 
87
- def can_sanitize_parameters?(the_class)
88
- not need_to_delay_sanitizing?(the_class)
191
+ class SanitizedPrototype < SanitizedParameter
192
+ def initialize(sanitizer, obj_type, obj_params, endian = nil)
193
+ sanitizer.with_endian(endian) do
194
+ @obj_class = sanitizer.lookup_class(obj_type)
195
+ @obj_params = sanitizer.create_sanitized_params(obj_params, @obj_class)
196
+ end
197
+ end
198
+
199
+ def instantiate(parent = nil)
200
+ @obj_class.new(@obj_params, parent)
89
201
  end
202
+ end
203
+ #----------------------------------------------------------------------------
204
+
205
+ class SanitizedField < SanitizedParameter
206
+ def initialize(sanitizer, name, field_type, field_params)
207
+ @name = name.to_s
208
+ @prototype = sanitizer.create_sanitized_object_prototype(field_type, field_params)
209
+ end
210
+ attr_reader :name
211
+
212
+ def instantiate(parent = nil)
213
+ @prototype.instantiate(parent)
214
+ end
215
+ end
216
+ #----------------------------------------------------------------------------
90
217
 
91
- def need_to_delay_sanitizing?(the_class)
92
- the_class.recursive? and @seen.include?(the_class)
218
+ class SanitizedFields < SanitizedParameter
219
+ def initialize(sanitizer, endian)
220
+ @sanitizer = sanitizer
221
+ @endian = endian
222
+ @fields = []
93
223
  end
94
224
 
95
- def get_sanitized_params(the_class, params)
96
- result = nil
97
- with_class_to_sanitize(the_class) do
98
- the_class.sanitize_parameters!(self, params)
99
- result = SanitizedParameters.new(the_class, params)
225
+ def add_field(type, name, params)
226
+ @sanitizer.with_endian(@endian) do
227
+ @fields << SanitizedField.new(@sanitizer, name, type, params)
100
228
  end
101
- result
102
229
  end
103
230
 
104
- def with_class_to_sanitize(the_class, &block)
105
- @seen.push(the_class)
106
- yield
107
- @seen.pop
231
+ def [](idx)
232
+ @fields[idx]
108
233
  end
109
234
 
110
- def store_current_endian!(the_class, params)
111
- if can_store_endian?(the_class, params)
112
- params[:endian] = @endian
235
+ def field_names
236
+ @fields.collect { |field| field.name }
237
+ end
238
+ end
239
+ #----------------------------------------------------------------------------
240
+
241
+ class SanitizedChoices < SanitizedParameter
242
+ def initialize(sanitizer, choices)
243
+ @choices = {}
244
+ choices.each_pair do |key, val|
245
+ type, param = val
246
+ prototype = sanitizer.create_sanitized_object_prototype(type, param)
247
+ @choices[key] = prototype
113
248
  end
114
249
  end
115
250
 
116
- def can_store_endian?(the_class, params)
117
- (@endian != nil and
118
- the_class.accepted_internal_parameters.include?(:endian) and
119
- not params.has_key?(:endian))
251
+ def [](key)
252
+ @choices[key]
120
253
  end
121
254
  end
255
+ #----------------------------------------------------------------------------
256
+
257
+ class SanitizedEndian < SanitizedParameter
258
+ def initialize(endian)
259
+ unless [:little, :big].include?(endian)
260
+ raise ArgumentError, "unknown value for endian '#{endian}'"
261
+ end
262
+
263
+ @endian = endian
264
+ end
265
+
266
+ attr_reader :endian
267
+ end
122
268
  end
@@ -56,25 +56,13 @@ module BinData
56
56
 
57
57
  class << self
58
58
 
59
- def deprecate!(params, old_key, new_key)
60
- if params.has_key?(old_key)
61
- warn ":#{old_key} is deprecated. Replacing with :#{new_key}"
62
- params[new_key] = params.delete(old_key)
63
- end
64
- end
65
-
66
- def sanitize_parameters!(sanitizer, params)
67
- # warn about deprecated param - remove before releasing 1.0
68
- deprecate!(params, :trim_value, :trim_padding)
69
-
59
+ def sanitize_parameters!(params, sanitizer)
70
60
  warn_replacement_parameter(params, :initial_length, :read_length)
71
61
 
72
- if params.has_key?(:pad_char)
62
+ if params.has_parameter?(:pad_char)
73
63
  ch = params[:pad_char]
74
64
  params[:pad_char] = sanitized_pad_char(ch)
75
65
  end
76
-
77
- super(sanitizer, params)
78
66
  end
79
67
 
80
68
  #-------------
@@ -1,5 +1,4 @@
1
1
  require 'bindata/base'
2
- require 'bindata/sanitize'
3
2
 
4
3
  module BinData
5
4
  # A Struct is an ordered collection of named data objects.
@@ -69,66 +68,47 @@ module BinData
69
68
  end
70
69
 
71
70
  class << self
72
- #### DEPRECATION HACK to warn about inheriting from BinData::Struct
73
- #
74
- def inherited(subclass) #:nodoc:
75
- if subclass != Record
76
- # warn about deprecated method - remove before releasing 1.0
77
- fail "error: inheriting from BinData::Struct has been deprecated. Inherit from BinData::Record instead."
78
- end
79
- end
80
- #
81
- #### DEPRECATION HACK to allow inheriting from BinData::Struct
82
-
83
-
84
- def sanitize_parameters!(sanitizer, params)
85
- ensure_valid_endian(params)
86
-
87
- if params.has_key?(:fields)
88
- sfields = sanitized_fields(sanitizer, params[:fields], params[:endian])
89
- field_names = sanitized_field_names(sfields)
90
- hfield_names = hidden_field_names(params[:hide])
91
-
92
- ensure_field_names_are_valid(field_names)
93
71
 
94
- params[:fields] = sfields
95
- params[:hide] = (hfield_names & field_names)
96
- end
97
-
98
- super(sanitizer, params)
72
+ def sanitize_parameters!(params, sanitizer)
73
+ sanitize_endian(params, sanitizer)
74
+ sanitize_fields(params, sanitizer)
75
+ sanitize_hide(params, sanitizer)
99
76
  end
100
77
 
101
78
  #-------------
102
79
  private
103
80
 
104
- def ensure_valid_endian(params)
105
- if params.has_key?(:endian)
106
- endian = params[:endian]
107
- unless [:little, :big].include?(endian)
108
- raise ArgumentError, "unknown value for endian '#{endian}'"
109
- end
81
+ def sanitize_endian(params, sanitizer)
82
+ if params.needs_sanitizing?(:endian)
83
+ params[:endian] = sanitizer.create_sanitized_endian(params[:endian])
110
84
  end
111
85
  end
112
86
 
113
- def sanitized_fields(sanitizer, fields, endian)
114
- result = nil
115
- sanitizer.with_endian(endian) do
116
- result = fields.collect do |ftype, fname, fparams|
117
- sanitized_field(sanitizer, ftype, fname, fparams)
87
+ def sanitize_fields(params, sanitizer)
88
+ if params.needs_sanitizing?(:fields)
89
+ fields = params[:fields]
90
+
91
+ params[:fields] = sanitizer.create_sanitized_fields(params[:endian])
92
+ fields.each do |ftype, fname, fparams|
93
+ params[:fields].add_field(ftype, fname, fparams)
118
94
  end
95
+
96
+ field_names = sanitized_field_names(params[:fields])
97
+ ensure_field_names_are_valid(field_names)
119
98
  end
120
- result
121
99
  end
122
100
 
123
- def sanitized_field(sanitizer, ftype, fname, fparams)
124
- fname = fname.to_s
125
- fclass = sanitizer.lookup_class(ftype)
126
- sanitized_fparams = sanitizer.sanitized_params(fclass, fparams)
127
- [fclass, fname, sanitized_fparams]
101
+ def sanitize_hide(params, sanitizer)
102
+ if params.needs_sanitizing?(:hide) and params.has_parameter?(:fields)
103
+ field_names = sanitized_field_names(params[:fields])
104
+ hfield_names = hidden_field_names(params[:hide])
105
+
106
+ params[:hide] = (hfield_names & field_names)
107
+ end
128
108
  end
129
109
 
130
110
  def sanitized_field_names(sanitized_fields)
131
- sanitized_fields.collect { |fclass, fname, fparams| fname }
111
+ sanitized_fields.field_names
132
112
  end
133
113
 
134
114
  def hidden_field_names(hidden)
@@ -162,32 +142,16 @@ module BinData
162
142
  def initialize(params = {}, parent = nil)
163
143
  super(params, parent)
164
144
 
165
- @field_names = get_parameter(:fields).collect { |c, n, p| n }
145
+ @field_names = get_parameter(:fields).field_names
166
146
  @field_objs = []
167
147
  end
168
148
 
169
- # Clears the field represented by +name+. If no +name+
170
- # is given, clears all fields in the struct.
171
- def clear(name = nil)
172
- if name.nil?
173
- @field_objs.each { |f| f.clear unless f.nil? }
174
- else
175
- warn "'obj.clear(name)' is deprecated. Replacing with 'obj.name.clear'"
176
- obj = find_obj_for_name(name)
177
- obj.clear unless obj.nil?
178
- end
149
+ def clear
150
+ @field_objs.each { |f| f.clear unless f.nil? }
179
151
  end
180
152
 
181
- # Returns if the field represented by +name+ is clear?. If no +name+
182
- # is given, returns whether all fields are clear.
183
- def clear?(name = nil)
184
- if name.nil?
185
- @field_objs.inject(true) { |all_clear, f| all_clear and (f.nil? or f.clear?) }
186
- else
187
- warn "'obj.clear?(name)' is deprecated. Replacing with 'obj.name.clear?'"
188
- obj = find_obj_for_name(name)
189
- obj.nil? ? true : obj.clear?
190
- end
153
+ def clear?
154
+ @field_objs.inject(true) { |all_clear, f| all_clear and (f.nil? or f.clear?) }
191
155
  end
192
156
 
193
157
  # Returns a list of the names of all fields accessible through this
@@ -197,7 +161,7 @@ module BinData
197
161
  if include_hidden
198
162
  @field_names.dup
199
163
  else
200
- hidden = get_parameter(:hide)
164
+ hidden = get_parameter(:hide) || []
201
165
  @field_names - hidden
202
166
  end
203
167
  end
@@ -222,10 +186,6 @@ module BinData
222
186
  end
223
187
 
224
188
  def offset_of(child)
225
- if child.class == ::String
226
- fail "error: 'offset_of(\"fieldname\")' is deprecated. Use 'fieldname.offset' instead"
227
- end
228
-
229
189
  instantiate_all_objs
230
190
  sum = sum_num_bytes_below_index(find_index_of(child))
231
191
  child_offset = (::Integer === child.do_num_bytes) ? sum.ceil : sum.floor
@@ -268,8 +228,8 @@ module BinData
268
228
 
269
229
  def instantiate_obj_at(index)
270
230
  if @field_objs[index].nil?
271
- fclass, fname, fparams = get_parameter(:fields)[index]
272
- @field_objs[index] = fclass.new(fparams, self)
231
+ field = get_parameter(:fields)[index]
232
+ @field_objs[index] = field.instantiate(self)
273
233
  end
274
234
  end
275
235
 
@@ -287,15 +247,9 @@ module BinData
287
247
  @field_objs.each { |f| f.do_write(io) if include_obj(f) }
288
248
  end
289
249
 
290
- def _do_num_bytes(name)
291
- if name.nil?
292
- instantiate_all_objs
293
- sum_num_bytes_for_all_fields.ceil
294
- else
295
- warn "'obj.num_bytes(name)' is deprecated. Replacing with 'obj.name.num_bytes'"
296
- obj = find_obj_for_name(name)
297
- obj.nil? ? 0 : obj.do_num_bytes
298
- end
250
+ def _do_num_bytes(deprecated)
251
+ instantiate_all_objs
252
+ sum_num_bytes_for_all_fields.ceil
299
253
  end
300
254
 
301
255
  def _assign(val)