bindata 2.1.0 → 2.2.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.

@@ -65,38 +65,44 @@ module BinData
65
65
  end
66
66
 
67
67
  def create_read_code(nbits, endian, signed)
68
- unpack_str = create_read_unpack_code(nbits, endian, signed)
69
- assemble_str = create_read_assemble_code(nbits, endian, signed)
68
+ read_str = create_raw_read_code(nbits, endian, signed)
70
69
 
71
- read_str = "(#{unpack_str} ; #{assemble_str})"
72
-
73
- if need_conversion_code?(nbits, signed)
70
+ if need_signed_conversion_code?(nbits, signed)
74
71
  "val = #{read_str} ; #{create_uint2int_code(nbits)}"
75
72
  else
76
73
  read_str
77
74
  end
78
75
  end
79
76
 
77
+ def create_raw_read_code(nbits, endian, signed)
78
+ # special case 8bit integers for speed
79
+ if nbits == 8
80
+ "io.readbytes(1).ord"
81
+ else
82
+ unpack_str = create_read_unpack_code(nbits, endian, signed)
83
+ assemble_str = create_read_assemble_code(nbits, endian, signed)
84
+
85
+ "(#{unpack_str} ; #{assemble_str})"
86
+ end
87
+ end
88
+
80
89
  def create_read_unpack_code(nbits, endian, signed)
81
- nbytes = nbits / 8
90
+ nbytes = nbits / 8
91
+ pack_directive = pack_directive(nbits, endian, signed)
82
92
 
83
- "ints = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits, endian, signed)}')"
93
+ "ints = io.readbytes(#{nbytes}).unpack('#{pack_directive}')"
84
94
  end
85
95
 
86
96
  def create_read_assemble_code(nbits, endian, signed)
87
- bits_per_word = bytes_per_word(nbits) * 8
88
- nwords = nbits / bits_per_word
97
+ nwords = nbits / bits_per_word(nbits)
89
98
 
90
99
  idx = (0 ... nwords).to_a
91
100
  idx.reverse! if (endian == :big)
92
101
 
93
102
  parts = (0 ... nwords).collect do |i|
94
- if i.zero?
95
- "ints.at(#{idx[i]})"
96
- else
97
- "(ints.at(#{idx[i]}) << #{bits_per_word * i})"
98
- end
103
+ "(ints.at(#{idx[i]}) << #{bits_per_word(nbits) * i})"
99
104
  end
105
+ parts[0].sub!(/ << 0\b/, "") # Remove " << 0" for optimisation
100
106
 
101
107
  assemble_str = parts.join(" + ")
102
108
  end
@@ -105,25 +111,29 @@ module BinData
105
111
  # special case 8bit integers for speed
106
112
  return "(val & 0xff).chr" if nbits == 8
107
113
 
108
- bits_per_word = bytes_per_word(nbits) * 8
109
- nwords = nbits / bits_per_word
110
- mask = (1 << bits_per_word) - 1
111
-
112
- vals = (0 ... nwords).collect do |i|
113
- i.zero? ? "val" : "val >> #{bits_per_word * i}"
114
- end
115
- vals.reverse! if (endian == :big)
114
+ pack_directive = pack_directive(nbits, endian, signed)
115
+ words = val_as_packed_words(nbits, endian, signed)
116
+ pack_str = "[#{words}].pack('#{pack_directive}')"
116
117
 
117
- array_str = "[" + vals.collect { |val| "#{val} & #{mask}" }.join(", ") + "]" # TODO: "& mask" is needed to work around jruby bug
118
- pack_str = "#{array_str}.pack('#{pack_directive(nbits, endian, signed)}')"
119
-
120
- if need_conversion_code?(nbits, signed)
118
+ if need_signed_conversion_code?(nbits, signed)
121
119
  "#{create_int2uint_code(nbits)} ; #{pack_str}"
122
120
  else
123
121
  pack_str
124
122
  end
125
123
  end
126
124
 
125
+ def val_as_packed_words(nbits, endian, signed)
126
+ nwords = nbits / bits_per_word(nbits)
127
+ mask = (1 << bits_per_word(nbits)) - 1
128
+
129
+ vals = (0 ... nwords).collect { |i| "val >> #{bits_per_word(nbits) * i}" }
130
+ vals[0].sub!(/ >> 0\b/, "") # Remove " >> 0" for optimisation
131
+ vals.reverse! if (endian == :big)
132
+
133
+ vals = vals.collect { |val| "#{val} & #{mask}" } # TODO: "& mask" is needed to work around jruby bug. Remove this line when fixed.
134
+ vals.join(",")
135
+ end
136
+
127
137
  def create_int2uint_code(nbits)
128
138
  "val &= #{(1 << nbits) - 1}"
129
139
  end
@@ -132,40 +142,30 @@ module BinData
132
142
  "(val >= #{1 << (nbits - 1)}) ? val - #{1 << nbits} : val"
133
143
  end
134
144
 
135
- def bytes_per_word(nbits)
136
- (nbits % 64).zero? ? 8 :
137
- (nbits % 32).zero? ? 4 :
138
- (nbits % 16).zero? ? 2 :
139
- 1
145
+ def bits_per_word(nbits)
146
+ (nbits % 64).zero? ? 64 :
147
+ (nbits % 32).zero? ? 32 :
148
+ (nbits % 16).zero? ? 16 :
149
+ 8
140
150
  end
141
151
 
142
152
  def pack_directive(nbits, endian, signed)
143
- bits_per_word = bytes_per_word(nbits) * 8
144
- nwords = nbits / bits_per_word
145
-
146
- if (nbits % 64).zero?
147
- d = (endian == :big) ? 'Q>' : 'Q<'
148
- elsif (nbits % 32).zero?
149
- d = (endian == :big) ? 'L>' : 'L<'
150
- elsif (nbits % 16).zero?
151
- d = (endian == :big) ? 'S>' : 'S<'
152
- else
153
- d = 'C'
154
- end
153
+ nwords = nbits / bits_per_word(nbits)
154
+
155
+ directives = { 8 => "C", 16 => "S", 32 => "L", 64 => "Q" }
156
+
157
+ d = directives[bits_per_word(nbits)]
158
+ d << ((endian == :big) ? ">" : "<") unless d == "C"
155
159
 
156
- if pack_directive_signed?(nbits, signed)
160
+ if signed == :signed and directives.has_key?(nbits)
157
161
  (d * nwords).downcase
158
162
  else
159
163
  d * nwords
160
164
  end
161
165
  end
162
166
 
163
- def need_conversion_code?(nbits, signed)
164
- signed == :signed and not pack_directive_signed?(nbits, signed)
165
- end
166
-
167
- def pack_directive_signed?(nbits, signed)
168
- signed == :signed and [64, 32, 16, 8].include?(nbits)
167
+ def need_signed_conversion_code?(nbits, signed)
168
+ signed == :signed and not [64, 32, 16].include?(nbits)
169
169
  end
170
170
  end
171
171
  end
@@ -5,8 +5,16 @@ module BinData
5
5
  # This registry contains a register of name -> class mappings.
6
6
  #
7
7
  # Numerics (integers and floating point numbers) have an endian property as
8
- # part of their name (e.g. int32be, float_le). The lookup can either be
9
- # on the full name, or on the shortened name plus endian (e.g. "int32", :big)
8
+ # part of their name (e.g. int32be, float_le).
9
+ #
10
+ # Classes can be looked up based on their full name or an abbreviated +name+
11
+ # with +hints+.
12
+ #
13
+ # There are two hints supported, :endian and :search_prefix.
14
+ #
15
+ # #lookup("int32", { endian: :big }) will return Int32Be.
16
+ #
17
+ # #lookup("my_type", { search_prefix: :ns }) will return NsMyType.
10
18
  #
11
19
  # Names are stored in under_score_style, not camelCase.
12
20
  class Registry
@@ -28,21 +36,11 @@ module BinData
28
36
  @registry.delete(underscore_name(name))
29
37
  end
30
38
 
31
- def lookup(name, endian = nil)
32
- key = normalize_name(name, endian)
39
+ def lookup(name, hints = {})
40
+ key = normalize_name(name, hints)
33
41
  @registry[key] || raise(UnRegisteredTypeError, name.to_s)
34
42
  end
35
43
 
36
- def normalize_name(name, endian = nil)
37
- name = underscore_name(name)
38
- return name if is_registered?(name)
39
-
40
- name = name_with_endian(name, endian)
41
- return name if is_registered?(name)
42
-
43
- name
44
- end
45
-
46
44
  # Convert CamelCase +name+ to underscore style.
47
45
  def underscore_name(name)
48
46
  name.to_s.sub(/.*::/, "").
@@ -55,6 +53,32 @@ module BinData
55
53
  #---------------
56
54
  private
57
55
 
56
+ def normalize_name(name, hints)
57
+ name = underscore_name(name)
58
+
59
+ if not is_registered?(name)
60
+ search_prefix = [""].concat(Array(hints[:search_prefix]))
61
+ search_prefix.each do |prefix|
62
+ n = name_with_endian(name_with_prefix(name, prefix), hints[:endian])
63
+ if is_registered?(n)
64
+ name = n
65
+ break
66
+ end
67
+ end
68
+ end
69
+
70
+ name
71
+ end
72
+
73
+ def name_with_prefix(name, prefix)
74
+ prefix = prefix.to_s.chomp("_")
75
+ if prefix == ""
76
+ name
77
+ else
78
+ "#{prefix}_#{name}"
79
+ end
80
+ end
81
+
58
82
  def name_with_endian(name, endian)
59
83
  return name if endian.nil?
60
84
 
@@ -6,21 +6,24 @@ module BinData
6
6
  class SanitizedParameter; end
7
7
 
8
8
  class SanitizedPrototype < SanitizedParameter
9
- def initialize(obj_type, obj_params, endian)
10
- endian = endian.endian if endian.respond_to? :endian
9
+ def initialize(obj_type, obj_params, hints)
10
+ raw_hints = hints.dup
11
+ if raw_hints[:endian].respond_to?(:endian)
12
+ raw_hints[:endian] = raw_hints[:endian].endian
13
+ end
11
14
  obj_params ||= {}
12
15
 
13
16
  if BinData::Base === obj_type
14
17
  obj_class = obj_type
15
18
  else
16
- obj_class = RegisteredClasses.lookup(obj_type, endian)
19
+ obj_class = RegisteredClasses.lookup(obj_type, raw_hints)
17
20
  end
18
21
 
19
22
  if BinData::Base === obj_class
20
23
  @factory = obj_class
21
24
  else
22
25
  @obj_class = obj_class
23
- @obj_params = SanitizedParameters.new(obj_params, @obj_class, endian)
26
+ @obj_params = SanitizedParameters.new(obj_params, @obj_class, hints)
24
27
  end
25
28
  end
26
29
 
@@ -41,9 +44,9 @@ module BinData
41
44
  #----------------------------------------------------------------------------
42
45
 
43
46
  class SanitizedField < SanitizedParameter
44
- def initialize(name, field_type, field_params, endian)
47
+ def initialize(name, field_type, field_params, hints)
45
48
  @name = name
46
- @prototype = SanitizedPrototype.new(field_type, field_params, endian)
49
+ @prototype = SanitizedPrototype.new(field_type, field_params, hints)
47
50
  end
48
51
 
49
52
  attr_reader :prototype
@@ -69,16 +72,16 @@ module BinData
69
72
  class SanitizedFields < SanitizedParameter
70
73
  include Enumerable
71
74
 
72
- def initialize(endian)
75
+ def initialize(hints)
73
76
  @fields = []
74
- @endian = endian
77
+ @hints = hints
75
78
  end
76
79
  attr_reader :fields
77
80
 
78
81
  def add_field(type, name, params)
79
82
  name = nil if name == ""
80
83
 
81
- @fields << SanitizedField.new(name, type, params, @endian)
84
+ @fields << SanitizedField.new(name, type, params, @hints)
82
85
  end
83
86
 
84
87
  def [](idx)
@@ -124,14 +127,14 @@ module BinData
124
127
  #----------------------------------------------------------------------------
125
128
 
126
129
  class SanitizedChoices < SanitizedParameter
127
- def initialize(choices, endian)
130
+ def initialize(choices, hints)
128
131
  @choices = {}
129
132
  choices.each_pair do |key, val|
130
133
  if SanitizedParameter === val
131
134
  prototype = val
132
135
  else
133
136
  type, param = val
134
- prototype = SanitizedPrototype.new(type, param, endian)
137
+ prototype = SanitizedPrototype.new(type, param, hints)
135
138
  end
136
139
 
137
140
  if key == :default
@@ -183,16 +186,23 @@ module BinData
183
186
  if SanitizedParameters === parameters
184
187
  parameters
185
188
  else
186
- SanitizedParameters.new(parameters, the_class, nil)
189
+ SanitizedParameters.new(parameters, the_class, {})
187
190
  end
188
191
  end
189
192
  end
190
193
 
191
- def initialize(parameters, the_class, endian)
194
+ def initialize(parameters, the_class, hints)
192
195
  parameters.each_pair { |key, value| self[key.to_sym] = value }
193
196
 
194
197
  @the_class = the_class
195
- @endian = endian
198
+
199
+ if hints[:endian]
200
+ self[:endian] ||= hints[:endian]
201
+ end
202
+
203
+ if hints[:search_prefix]
204
+ self[:search_prefix] = Array(self[:search_prefix]).concat(Array(hints[:search_prefix]))
205
+ end
196
206
 
197
207
  sanitize!
198
208
  end
@@ -235,10 +245,12 @@ module BinData
235
245
  end
236
246
  end
237
247
 
238
- def endian
239
- @endian || self[:endian]
248
+ def hints
249
+ {
250
+ :endian => self[:endian],
251
+ :search_prefix => self[:search_prefix],
252
+ }
240
253
  end
241
- attr_writer :endian
242
254
 
243
255
  def create_sanitized_endian(endian)
244
256
  if endian == :big
@@ -253,19 +265,19 @@ module BinData
253
265
  end
254
266
 
255
267
  def create_sanitized_params(params, the_class)
256
- SanitizedParameters.new(params, the_class, self.endian)
268
+ SanitizedParameters.new(params, the_class, hints)
257
269
  end
258
270
 
259
271
  def create_sanitized_choices(choices)
260
- SanitizedChoices.new(choices, self.endian)
272
+ SanitizedChoices.new(choices, hints)
261
273
  end
262
274
 
263
275
  def create_sanitized_fields
264
- SanitizedFields.new(self.endian)
276
+ SanitizedFields.new(hints)
265
277
  end
266
278
 
267
279
  def create_sanitized_object_prototype(obj_type, obj_params)
268
- SanitizedPrototype.new(obj_type, obj_params, self.endian)
280
+ SanitizedPrototype.new(obj_type, obj_params, hints)
269
281
  end
270
282
 
271
283
  #---------------
@@ -56,6 +56,14 @@ module BinData
56
56
  mutually_exclusive_parameters :read_length, :length
57
57
  mutually_exclusive_parameters :length, :value
58
58
 
59
+ def initialize_shared_instance
60
+ if (has_parameter?(:value) || has_parameter?(:asserted_value)) &&
61
+ ! has_parameter?(:read_length)
62
+ extend WarnNoReadLengthPlugin
63
+ end
64
+ super
65
+ end
66
+
59
67
  def assign(val)
60
68
  super(binary_string(val))
61
69
  end
@@ -141,4 +149,12 @@ module BinData
141
149
  pad_byte
142
150
  end
143
151
  end
152
+
153
+ # Warns when reading if :value && no :read_length
154
+ module WarnNoReadLengthPlugin
155
+ def read_and_return_value(io)
156
+ warn "#{debug_name} does not have a :read_length parameter - returning empty string"
157
+ ""
158
+ end
159
+ end
144
160
  end
@@ -42,6 +42,9 @@ module BinData
42
42
  # <tt>:endian</tt>:: Either :little or :big. This specifies the default
43
43
  # endian of any numerics in this struct, or in any
44
44
  # nested data objects.
45
+ # <tt>:search_prefix</tt>:: Allows abbreviated type names. If a type is
46
+ # unrecognised, then each prefix is applied until
47
+ # a match is found.
45
48
  #
46
49
  # == Field Parameters
47
50
  #
@@ -56,7 +59,7 @@ module BinData
56
59
  arg_processor :struct
57
60
 
58
61
  mandatory_parameter :fields
59
- optional_parameters :endian, :hide
62
+ optional_parameters :endian, :search_prefix, :hide
60
63
 
61
64
  # These reserved words may not be used as field names
62
65
  RESERVED = Hash[*
@@ -333,6 +336,7 @@ module BinData
333
336
  class StructArgProcessor < BaseArgProcessor
334
337
  def sanitize_parameters!(obj_class, params)
335
338
  sanitize_endian(params)
339
+ sanitize_search_prefix(params)
336
340
  sanitize_fields(obj_class, params)
337
341
  sanitize_hide(params)
338
342
  end
@@ -344,7 +348,18 @@ module BinData
344
348
  if params.needs_sanitizing?(:endian)
345
349
  endian = params.create_sanitized_endian(params[:endian])
346
350
  params[:endian] = endian
347
- params.endian = endian # sync params[:endian] and params.endian
351
+ end
352
+ end
353
+
354
+ def sanitize_search_prefix(params)
355
+ if params.needs_sanitizing?(:search_prefix)
356
+ search_prefix = []
357
+ Array(params[:search_prefix]).each do |prefix|
358
+ prefix = prefix.to_s.chomp("_")
359
+ search_prefix << prefix if prefix != ""
360
+ end
361
+
362
+ params[:search_prefix] = search_prefix
348
363
  end
349
364
  end
350
365
 
@@ -1,3 +1,3 @@
1
1
  module BinData
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end