bindata 0.9.3 → 0.10.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 (47) hide show
  1. data/ChangeLog +18 -0
  2. data/NEWS +59 -0
  3. data/README +22 -23
  4. data/TODO +18 -12
  5. data/examples/gzip.rb +4 -4
  6. data/lib/bindata.rb +4 -3
  7. data/lib/bindata/array.rb +202 -132
  8. data/lib/bindata/base.rb +147 -166
  9. data/lib/bindata/{single.rb → base_primitive.rb} +82 -56
  10. data/lib/bindata/bits.rb +31 -770
  11. data/lib/bindata/choice.rb +157 -82
  12. data/lib/bindata/float.rb +25 -27
  13. data/lib/bindata/int.rb +144 -177
  14. data/lib/bindata/io.rb +59 -49
  15. data/lib/bindata/lazy.rb +80 -50
  16. data/lib/bindata/params.rb +134 -26
  17. data/lib/bindata/{single_value.rb → primitive.rb} +71 -64
  18. data/lib/bindata/{multi_value.rb → record.rb} +52 -70
  19. data/lib/bindata/registry.rb +49 -17
  20. data/lib/bindata/rest.rb +6 -10
  21. data/lib/bindata/sanitize.rb +55 -70
  22. data/lib/bindata/string.rb +60 -42
  23. data/lib/bindata/stringz.rb +34 -35
  24. data/lib/bindata/struct.rb +197 -152
  25. data/lib/bindata/trace.rb +35 -0
  26. data/spec/array_spec.rb +128 -112
  27. data/spec/{single_spec.rb → base_primitive_spec.rb} +102 -61
  28. data/spec/base_spec.rb +190 -185
  29. data/spec/bits_spec.rb +126 -98
  30. data/spec/choice_spec.rb +89 -98
  31. data/spec/example.rb +19 -0
  32. data/spec/float_spec.rb +28 -44
  33. data/spec/int_spec.rb +217 -127
  34. data/spec/io_spec.rb +41 -24
  35. data/spec/lazy_spec.rb +95 -49
  36. data/spec/primitive_spec.rb +191 -0
  37. data/spec/{multi_value_spec.rb → record_spec.rb} +124 -89
  38. data/spec/registry_spec.rb +53 -12
  39. data/spec/rest_spec.rb +2 -3
  40. data/spec/sanitize_spec.rb +47 -73
  41. data/spec/spec_common.rb +13 -1
  42. data/spec/string_spec.rb +34 -23
  43. data/spec/stringz_spec.rb +10 -18
  44. data/spec/struct_spec.rb +91 -63
  45. data/spec/system_spec.rb +291 -0
  46. metadata +12 -8
  47. data/spec/single_value_spec.rb +0 -131
@@ -1,6 +1,7 @@
1
1
  require 'forwardable'
2
2
  require 'bindata/base'
3
3
  require 'bindata/sanitize'
4
+ require 'bindata/trace'
4
5
 
5
6
  module BinData
6
7
  # A Choice is a collection of data objects of which only one is active
@@ -29,10 +30,10 @@ module BinData
29
30
  # a = BinData::Choice.new(:choices => choices,
30
31
  # :selection => lambda { mychoice })
31
32
  # a.value = 256
32
- # a.to_s #=> "\001\000"
33
+ # a.to_binary_s #=> "\001\000"
33
34
  # mychoice.replace 'little'
34
35
  # a.selection #=> 'little'
35
- # a.to_s #=> "\000\001"
36
+ # a.to_binary_s #=> "\000\001"
36
37
  #
37
38
  #
38
39
  # == Parameters
@@ -40,73 +41,89 @@ module BinData
40
41
  # Parameters may be provided at initialisation to control the behaviour of
41
42
  # an object. These params are:
42
43
  #
43
- # <tt>:choices</tt>:: Either an array or a hash specifying the possible
44
- # data objects. The format of the array/hash.values is
45
- # a list of symbols representing the data object type.
46
- # If a choice is to have params passed to it, then it
47
- # should be provided as [type_symbol, hash_params].
48
- # An implementation gotcha is that the hash may not
49
- # contain symbols as keys.
50
- # <tt>:selection</tt>:: An index/key into the :choices array/hash which
51
- # specifies the currently active choice.
44
+ # <tt>:choices</tt>:: Either an array or a hash specifying the possible
45
+ # data objects. The format of the
46
+ # array/hash.values is a list of symbols
47
+ # representing the data object type. If a choice
48
+ # is to have params passed to it, then it should
49
+ # be provided as [type_symbol, hash_params]. An
50
+ # implementation constraint is that the hash may
51
+ # not contain symbols as keys.
52
+ # <tt>:selection</tt>:: An index/key into the :choices array/hash which
53
+ # specifies the currently active choice.
54
+ # <tt>:copy_on_change</tt>:: If set to true, copy the value of the previous
55
+ # selection to the current selection whenever the
56
+ # selection changes. Default is false.
52
57
  class Choice < BinData::Base
53
58
  extend Forwardable
54
59
 
55
- # Register this class
56
60
  register(self.name, self)
57
61
 
58
- # These are the parameters used by this class.
59
- bindata_mandatory_parameters :choices, :selection
62
+ mandatory_parameters :choices, :selection
63
+ optional_parameter :copy_on_change
60
64
 
61
65
  class << self
62
66
 
63
- # Ensures that +params+ is of the form expected by #initialize.
64
67
  def sanitize_parameters!(sanitizer, params)
65
68
  if params.has_key?(:choices)
66
- choices = params[:choices]
67
-
68
- # convert array to hash keyed by index
69
- if ::Array === choices
70
- tmp = {}
71
- choices.each_with_index do |el, i|
72
- tmp[i] = el unless el.nil?
73
- end
74
- choices = tmp
75
- end
76
-
77
- # ensure valid hash keys
78
- if choices.has_key?(nil)
79
- raise ArgumentError, ":choices hash may not have nil key"
80
- end
81
- if choices.keys.detect { |k| Symbol === k }
82
- raise ArgumentError, ":choices hash may not have symbols for keys"
83
- end
84
-
85
- # sanitize each choice
86
- new_choices = {}
87
- choices.each_pair do |key, val|
88
- type, param = val
89
- klass = sanitizer.lookup_klass(type)
90
- sanitized_params = sanitizer.sanitize_params(klass, param)
91
- new_choices[key] = [klass, sanitized_params]
92
- end
93
- params[:choices] = new_choices
69
+ choices = choices_as_hash(params[:choices])
70
+ ensure_valid_keys(choices)
71
+ params[:choices] = sanitized_choices(sanitizer, choices)
94
72
  end
95
73
 
96
74
  super(sanitizer, params)
97
75
  end
76
+
77
+ #-------------
78
+ private
79
+
80
+ def choices_as_hash(choices)
81
+ if choices.respond_to?(:to_ary)
82
+ key_array_by_index(choices.to_ary)
83
+ else
84
+ choices
85
+ end
86
+ end
87
+
88
+ def key_array_by_index(array)
89
+ result = {}
90
+ array.each_with_index do |el, i|
91
+ result[i] = el unless el.nil?
92
+ end
93
+ result
94
+ end
95
+
96
+ def ensure_valid_keys(choices)
97
+ if choices.has_key?(nil)
98
+ raise ArgumentError, ":choices hash may not have nil key"
99
+ end
100
+ if choices.keys.detect { |k| Symbol === k }
101
+ raise ArgumentError, ":choices hash may not have symbols for keys"
102
+ end
103
+ end
104
+
105
+ def sanitized_choices(sanitizer, choices)
106
+ result = {}
107
+ choices.each_pair do |key, val|
108
+ type, param = val
109
+ the_class = sanitizer.lookup_class(type)
110
+ sanitized_params = sanitizer.sanitized_params(the_class, param)
111
+ result[key] = [the_class, sanitized_params]
112
+ end
113
+ result
114
+ end
98
115
  end
99
116
 
100
117
  def initialize(params = {}, parent = nil)
101
118
  super(params, parent)
102
119
 
103
- @choices = {}
104
- @last_key = nil
120
+ @choices = {}
121
+ @last_selection = nil
105
122
  end
106
123
 
107
124
  # A convenience method that returns the current selection.
108
125
  def selection
109
- eval_param(:selection)
126
+ eval_parameter(:selection)
110
127
  end
111
128
 
112
129
  # This method does not exist. This stub only exists to document why.
@@ -116,7 +133,7 @@ module BinData
116
133
  # If you really *must* be able to programmatically adjust the selection
117
134
  # then try something like the following.
118
135
  #
119
- # class ProgrammaticChoice < BinData::MultiValue
136
+ # class ProgrammaticChoice < BinData::Record
120
137
  # choice :data, :choices => :choices, :selection => :selection
121
138
  # attrib_accessor :selection
122
139
  # end
@@ -136,62 +153,120 @@ module BinData
136
153
  raise NoMethodError
137
154
  end
138
155
 
139
- # A choice represents a specific object.
140
- def obj
141
- the_choice
142
- end
143
-
144
- def_delegators :the_choice, :clear, :clear?, :single_value?
145
- def_delegators :the_choice, :done_read, :_snapshot
146
- def_delegators :the_choice, :_do_read, :_do_write, :_do_num_bytes
156
+ def_delegators :current_choice, :clear, :clear?
147
157
 
148
- # Override to include selected data object.
149
158
  def respond_to?(symbol, include_private = false)
150
- super || the_choice.respond_to?(symbol, include_private)
159
+ super || current_choice.respond_to?(symbol, include_private)
151
160
  end
152
161
 
153
162
  def method_missing(symbol, *args, &block)
154
- if the_choice.respond_to?(symbol)
155
- the_choice.__send__(symbol, *args, &block)
163
+ if current_choice.respond_to?(symbol)
164
+ current_choice.__send__(symbol, *args, &block)
156
165
  else
157
166
  super
158
167
  end
159
168
  end
160
169
 
170
+ def debug_name_of(child)
171
+ debug_name
172
+ end
173
+
174
+ def offset_of(child)
175
+ offset
176
+ end
177
+
161
178
  #---------------
162
179
  private
163
180
 
164
- # Returns the selected data object.
165
- def the_choice
166
- key = eval_param(:selection)
181
+ def _do_read(io)
182
+ trace_selection
183
+ current_choice.do_read(io)
184
+ end
185
+
186
+ def trace_selection
187
+ BinData::trace_message do |tracer|
188
+ selection_string = eval_parameter(:selection).inspect
189
+ if selection_string.length > 30
190
+ selection_string = selection_string.slice(0 .. 30) + "..."
191
+ end
192
+
193
+ tracer.trace("#{debug_name}-selection- => #{selection_string}")
194
+ end
195
+ end
196
+
197
+ def _done_read
198
+ current_choice.done_read
199
+ end
200
+
201
+ def _do_write(io)
202
+ current_choice.do_write(io)
203
+ end
204
+
205
+ def _do_num_bytes(what)
206
+ current_choice.do_num_bytes(what)
207
+ end
208
+
209
+ def _assign(val)
210
+ current_choice.assign(val)
211
+ end
212
+
213
+ def _snapshot
214
+ current_choice.snapshot
215
+ end
167
216
 
168
- if key.nil?
169
- raise IndexError, ":selection returned nil value"
217
+ def current_choice
218
+ selection = eval_parameter(:selection)
219
+ if selection.nil?
220
+ raise IndexError, ":selection returned nil for #{debug_name}"
170
221
  end
171
222
 
172
- obj = @choices[key]
223
+ obj = get_or_instantiate_choice(selection)
224
+ copy_previous_value_if_required(selection, obj)
225
+
226
+ obj
227
+ end
228
+
229
+ def get_or_instantiate_choice(selection)
230
+ obj = @choices[selection]
173
231
  if obj.nil?
174
- # instantiate choice object
175
- choice_klass, choice_params = no_eval_param(:choices)[key]
176
- if choice_klass.nil?
177
- raise IndexError, "selection #{key} does not exist in :choices"
178
- end
179
- obj = choice_klass.new(choice_params, self)
180
- @choices[key] = obj
232
+ obj = instantiate_choice(selection)
233
+ @choices[selection] = obj
234
+ end
235
+ obj
236
+ end
237
+
238
+ def instantiate_choice(selection)
239
+ choice_class, choice_params = get_parameter(:choices)[selection]
240
+ if choice_class.nil?
241
+ raise IndexError, "selection '#{selection}' does not exist in :choices for #{debug_name}"
181
242
  end
243
+ choice_class.new(choice_params, self)
244
+ end
182
245
 
183
- # for single_values copy the value when the selected object changes
184
- if key != @last_key
185
- if @last_key != nil
186
- prev = @choices[@last_key]
187
- if prev != nil and prev.single_value? and obj.single_value?
188
- obj.value = prev.value
189
- end
190
- end
191
- @last_key = key
246
+ def copy_previous_value_if_required(selection, obj)
247
+ prev = get_previous_choice(selection)
248
+ if should_copy_value?(prev, obj)
249
+ obj.assign(prev)
192
250
  end
251
+ remember_current_selection(selection)
252
+ end
193
253
 
194
- obj
254
+ def should_copy_value?(prev, cur)
255
+ prev != nil and eval_parameter(:copy_on_change) == true
256
+ end
257
+
258
+ def get_previous_choice(selection)
259
+ if selection != @last_selection and @last_selection != nil
260
+ @choices[@last_selection]
261
+ else
262
+ nil
263
+ end
264
+ end
265
+
266
+ def remember_current_selection(selection)
267
+ if selection != @last_selection
268
+ @last_selection = selection
269
+ end
195
270
  end
196
271
  end
197
272
  end
data/lib/bindata/float.rb CHANGED
@@ -1,19 +1,20 @@
1
- require 'bindata/single'
1
+ require 'bindata/base_primitive'
2
2
 
3
3
  module BinData
4
- # Provides a number of classes that contain a floating point number.
5
- # The float is defined by endian, and precision.
4
+ # Defines a number of classes that contain a floating point number.
5
+ # The float is defined by precision and endian.
6
6
 
7
- module Float #:nodoc: all
8
- def self.create_float_methods(klass, single_precision, endian)
9
- read = create_read_code(single_precision, endian)
10
- to_s = create_to_s_code(single_precision, endian)
7
+ module FloatingPoint #:nodoc: all
8
+ def self.create_float_methods(float_class, precision, endian)
9
+ read = create_read_code(precision, endian)
10
+ to_s = create_to_s_code(precision, endian)
11
+ nbytes = (precision == :single) ? 4 : 8
11
12
 
12
- define_methods(klass, single_precision, read, to_s)
13
+ define_methods(float_class, nbytes, read, to_s)
13
14
  end
14
15
 
15
- def self.create_read_code(single_precision, endian)
16
- if single_precision
16
+ def self.create_read_code(precision, endian)
17
+ if precision == :single
17
18
  unpack = (endian == :little) ? 'e' : 'g'
18
19
  nbytes = 4
19
20
  else # double_precision
@@ -24,8 +25,8 @@ module BinData
24
25
  "io.readbytes(#{nbytes}).unpack('#{unpack}').at(0)"
25
26
  end
26
27
 
27
- def self.create_to_s_code(single_precision, endian)
28
- if single_precision
28
+ def self.create_to_s_code(precision, endian)
29
+ if precision == :single
29
30
  pack = (endian == :little) ? 'e' : 'g'
30
31
  else # double_precision
31
32
  pack = (endian == :little) ? 'E' : 'G'
@@ -34,11 +35,8 @@ module BinData
34
35
  "[val].pack('#{pack}')"
35
36
  end
36
37
 
37
- def self.define_methods(klass, single_precision, read, to_s)
38
- nbytes = single_precision ? 4 : 8
39
-
40
- # define methods in the given class
41
- klass.module_eval <<-END
38
+ def self.define_methods(float_class, nbytes, read, to_s)
39
+ float_class.module_eval <<-END
42
40
  def _do_num_bytes(ignored)
43
41
  #{nbytes}
44
42
  end
@@ -50,11 +48,11 @@ module BinData
50
48
  0.0
51
49
  end
52
50
 
53
- def val_to_str(val)
51
+ def value_to_binary_string(val)
54
52
  #{to_s}
55
53
  end
56
54
 
57
- def read_val(io)
55
+ def read_and_return_value(io)
58
56
  #{read}
59
57
  end
60
58
  END
@@ -63,26 +61,26 @@ module BinData
63
61
 
64
62
 
65
63
  # Single precision floating point number in little endian format
66
- class FloatLe < BinData::Single
64
+ class FloatLe < BinData::BasePrimitive
67
65
  register(self.name, self)
68
- Float.create_float_methods(self, true, :little)
66
+ FloatingPoint.create_float_methods(self, :single, :little)
69
67
  end
70
68
 
71
69
  # Single precision floating point number in big endian format
72
- class FloatBe < BinData::Single
70
+ class FloatBe < BinData::BasePrimitive
73
71
  register(self.name, self)
74
- Float.create_float_methods(self, true, :big)
72
+ FloatingPoint.create_float_methods(self, :single, :big)
75
73
  end
76
74
 
77
75
  # Double precision floating point number in little endian format
78
- class DoubleLe < BinData::Single
76
+ class DoubleLe < BinData::BasePrimitive
79
77
  register(self.name, self)
80
- Float.create_float_methods(self, false, :little)
78
+ FloatingPoint.create_float_methods(self, :double, :little)
81
79
  end
82
80
 
83
81
  # Double precision floating point number in big endian format
84
- class DoubleBe < BinData::Single
82
+ class DoubleBe < BinData::BasePrimitive
85
83
  register(self.name, self)
86
- Float.create_float_methods(self, false, :big)
84
+ FloatingPoint.create_float_methods(self, :double, :big)
87
85
  end
88
86
  end
data/lib/bindata/int.rb CHANGED
@@ -1,220 +1,187 @@
1
- require 'bindata/single'
1
+ require 'bindata/base_primitive'
2
2
 
3
3
  module BinData
4
- # Provides a number of classes that contain an integer. The integer
4
+ # Defines a number of classes that contain an integer. The integer
5
5
  # is defined by endian, signedness and number of bytes.
6
6
 
7
7
  module Integer #:nodoc: all
8
- def self.create_int_methods(klass, nbits, endian)
9
- max = (1 << (nbits - 1)) - 1
10
- min = -(max + 1)
11
- clamp = "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
8
+ class << self
9
+ def define_class(nbits, endian, signed)
10
+ endian_str = (endian == :big) ? "be" : "le"
11
+ if signed == :signed
12
+ name = "Int#{nbits}#{endian_str}"
13
+ creation_method = "create_int_methods"
14
+ else
15
+ name = "Uint#{nbits}#{endian_str}"
16
+ creation_method = "create_uint_methods"
17
+ end
18
+
19
+ BinData.module_eval <<-END
20
+ class #{name} < BinData::BasePrimitive
21
+ register(self.name, self)
22
+ Integer.#{creation_method}(self, #{nbits}, :#{endian.to_s})
23
+ end
24
+ END
25
+ end
12
26
 
13
- int2uint = "val = val & #{(1 << nbits) - 1}"
14
- uint2int = "val = ((val & #{1 << (nbits - 1)}).zero?) ? " +
15
- "val & #{max} : -(((~val) & #{max}) + 1)"
27
+ def create_uint_methods(int_class, nbits, endian)
28
+ min = 0
29
+ max = (1 << nbits) - 1
16
30
 
17
- read = create_read_code(nbits, endian)
18
- to_s = create_to_s_code(nbits, endian)
31
+ clamp = create_clamp_code(min, max)
32
+ read = create_read_code(nbits, endian)
33
+ to_s = create_to_s_code(nbits, endian)
19
34
 
20
- define_methods(klass, nbits / 8, clamp, read, to_s, int2uint, uint2int)
21
- end
35
+ define_methods(int_class, nbits / 8, clamp, read, to_s)
36
+ end
22
37
 
23
- def self.create_uint_methods(klass, nbits, endian)
24
- min = 0
25
- max = (1 << nbits) - 1
26
- clamp = "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
38
+ def create_int_methods(int_class, nbits, endian)
39
+ max = (1 << (nbits - 1)) - 1
40
+ min = -(max + 1)
27
41
 
28
- read = create_read_code(nbits, endian)
29
- to_s = create_to_s_code(nbits, endian)
42
+ clamp = create_clamp_code(min, max)
43
+ read = create_read_code(nbits, endian)
44
+ to_s = create_to_s_code(nbits, endian)
30
45
 
31
- define_methods(klass, nbits / 8, clamp, read, to_s)
32
- end
46
+ int2uint = create_int2uint_code(nbits)
47
+ uint2int = create_uint2int_code(nbits)
33
48
 
34
- def self.create_read_code(nbits, endian)
35
- c16 = (endian == :little) ? 'v' : 'n'
36
- c32 = (endian == :little) ? 'V' : 'N'
37
- idx = (0 ... (nbits / 32)).to_a
38
- idx.reverse! if (endian == :little)
39
-
40
- case nbits
41
- when 8; "io.readbytes(1).unpack('C').at(0)"
42
- when 16; "io.readbytes(2).unpack('#{c16}').at(0)"
43
- when 32; "io.readbytes(4).unpack('#{c32}').at(0)"
44
- when 64; "(a = io.readbytes(8).unpack('#{c32 * 2}'); " +
45
- "(a.at(#{idx[0]}) << 32) + " +
46
- "a.at(#{idx[1]}))"
47
- when 128; "(a = io.readbytes(16).unpack('#{c32 * 4}'); " +
48
- "((a.at(#{idx[0]}) << 96) + " +
49
- "(a.at(#{idx[1]}) << 64) + " +
50
- "(a.at(#{idx[2]}) << 32) + " +
51
- "a.at(#{idx[3]})))"
52
- else
53
- raise "unknown nbits '#{nbits}'"
49
+ define_methods(int_class, nbits / 8, clamp, read, to_s, int2uint, uint2int)
54
50
  end
55
- end
56
51
 
57
- def self.create_to_s_code(nbits, endian)
58
- c16 = (endian == :little) ? 'v' : 'n'
59
- c32 = (endian == :little) ? 'V' : 'N'
60
- vals = (0 ... (nbits / 32)).collect { |i| "(val >> #{32 * i})" }
61
- vals.reverse! if (endian == :little)
62
-
63
- case nbits
64
- when 8; "val.chr"
65
- when 16; "[val].pack('#{c16}')"
66
- when 32; "[val].pack('#{c32}')"
67
- when 64; "[#{vals[1]} & 0xffffffff, " +
68
- "#{vals[0]} & 0xffffffff].pack('#{c32 * 2}')"
69
- when 128; "[#{vals[3]} & 0xffffffff, " +
70
- "#{vals[2]} & 0xffffffff, " +
71
- "#{vals[1]} & 0xffffffff, " +
72
- "#{vals[0]} & 0xffffffff].pack('#{c32 * 4}')"
73
- else
74
- raise "unknown nbits '#{nbits}'"
52
+ def create_clamp_code(min, max)
53
+ "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
75
54
  end
76
- end
77
55
 
78
- def self.define_methods(klass, nbytes, clamp, read, to_s,
79
- int2uint = nil, uint2int = nil)
80
- # define methods in the given class
81
- klass.module_eval <<-END
82
- def value=(val)
83
- #{clamp}
84
- super(val)
85
- end
56
+ def create_int2uint_code(nbits)
57
+ "val = val & #{(1 << nbits) - 1}"
58
+ end
86
59
 
87
- def _do_num_bytes(ignored)
88
- #{nbytes}
89
- end
60
+ def create_uint2int_code(nbits)
61
+ mask = (1 << (nbits - 1)) - 1
90
62
 
91
- #---------------
92
- private
63
+ "val = ((val & #{1 << (nbits - 1)}).zero?) ? " +
64
+ "val & #{mask} : -(((~val) & #{mask}) + 1)"
65
+ end
93
66
 
94
- def sensible_default
95
- 0
67
+ def create_read_code(nbits, endian)
68
+ raise "nbits must be divisible by 8" unless (nbits % 8).zero?
69
+
70
+ # determine "word" size and unpack directive
71
+ if (nbits % 32).zero?
72
+ bytes_per_word = 4
73
+ d = (endian == :big) ? 'N' : 'V'
74
+ elsif (nbits % 16).zero?
75
+ bytes_per_word = 2
76
+ d = (endian == :big) ? 'n' : 'v'
77
+ else
78
+ bytes_per_word = 1
79
+ d = 'C'
96
80
  end
97
81
 
98
- def val_to_str(val)
99
- #{clamp}
100
- #{int2uint unless int2uint.nil?}
101
- #{to_s}
102
- end
82
+ bits_per_word = bytes_per_word * 8
83
+ nwords = nbits / bits_per_word
84
+ nbytes = nbits / 8
103
85
 
104
- def read_val(io)
105
- val = #{read}
106
- #{uint2int unless uint2int.nil?}
107
- end
108
- END
109
- end
110
- end
86
+ idx = (0 ... nwords).to_a
87
+ idx.reverse! if (endian == :big)
111
88
 
89
+ unpack_str = "a = io.readbytes(#{nbytes}).unpack('#{d * nwords}')"
112
90
 
113
- # Unsigned 1 byte integer.
114
- class Uint8 < BinData::Single
115
- register(self.name, self)
116
- Integer.create_uint_methods(self, 8, :little)
117
- end
91
+ parts = (0 ... nwords).collect do |i|
92
+ i.zero? ? "a.at(#{idx[i]})" :
93
+ "(a.at(#{idx[i]}) << #{bits_per_word * i})"
94
+ end
95
+ assemble_str = parts.join(" + ")
118
96
 
119
- # Unsigned 2 byte little endian integer.
120
- class Uint16le < BinData::Single
121
- register(self.name, self)
122
- Integer.create_uint_methods(self, 16, :little)
123
- end
97
+ "(#{unpack_str}; #{assemble_str})"
98
+ end
124
99
 
125
- # Unsigned 2 byte big endian integer.
126
- class Uint16be < BinData::Single
127
- register(self.name, self)
128
- Integer.create_uint_methods(self, 16, :big)
129
- end
100
+ def create_to_s_code(nbits, endian)
101
+ raise "nbits must be divisible by 8" unless (nbits % 8).zero?
102
+
103
+ # special case 8bit integers for speed
104
+ return "val.chr" if nbits == 8
105
+
106
+ # determine "word" size and pack directive
107
+ if (nbits % 32).zero?
108
+ bytes_per_word = 4
109
+ d = (endian == :big) ? 'N' : 'V'
110
+ elsif (nbits % 16).zero?
111
+ bytes_per_word = 2
112
+ d = (endian == :big) ? 'n' : 'v'
113
+ else
114
+ bytes_per_word = 1
115
+ d = 'C'
116
+ end
130
117
 
131
- # Unsigned 4 byte little endian integer.
132
- class Uint32le < BinData::Single
133
- register(self.name, self)
134
- Integer.create_uint_methods(self, 32, :little)
135
- end
118
+ bits_per_word = bytes_per_word * 8
119
+ nwords = nbits / bits_per_word
120
+ mask = (1 << bits_per_word) - 1
136
121
 
137
- # Unsigned 4 byte big endian integer.
138
- class Uint32be < BinData::Single
139
- register(self.name, self)
140
- Integer.create_uint_methods(self, 32, :big)
141
- end
122
+ vals = (0 ... nwords).collect do |i|
123
+ i.zero? ? "val" : "(val >> #{bits_per_word * i})"
124
+ end
125
+ vals.reverse! if (endian == :big)
142
126
 
143
- # Unsigned 8 byte little endian integer.
144
- class Uint64le < BinData::Single
145
- register(self.name, self)
146
- Integer.create_uint_methods(self, 64, :little)
147
- end
127
+ parts = (0 ... nwords).collect { |i| "#{vals[i]} & #{mask}" }
128
+ array_str = "[" + parts.join(", ") + "]"
148
129
 
149
- # Unsigned 8 byte big endian integer.
150
- class Uint64be < BinData::Single
151
- register(self.name, self)
152
- Integer.create_uint_methods(self, 64, :big)
153
- end
130
+ "#{array_str}.pack('#{d * nwords}')"
131
+ end
154
132
 
155
- # Unsigned 16 byte little endian integer.
156
- class Uint128le < BinData::Single
157
- register(self.name, self)
158
- Integer.create_uint_methods(self, 128, :little)
133
+ def define_methods(int_class, nbytes, clamp, read, to_s,
134
+ int2uint = nil, uint2int = nil)
135
+ int_class.module_eval <<-END
136
+ #---------------
137
+ private
138
+
139
+ def _assign(val)
140
+ #{clamp}
141
+ super(val)
142
+ end
143
+
144
+ def _do_num_bytes(ignored)
145
+ #{nbytes}
146
+ end
147
+
148
+ def sensible_default
149
+ 0
150
+ end
151
+
152
+ def value_to_binary_string(val)
153
+ #{clamp}
154
+ #{int2uint unless int2uint.nil?}
155
+ #{to_s}
156
+ end
157
+
158
+ def read_and_return_value(io)
159
+ val = #{read}
160
+ #{uint2int unless uint2int.nil?}
161
+ end
162
+ END
163
+ end
164
+ end
159
165
  end
160
166
 
161
- # Unsigned 16 byte big endian integer.
162
- class Uint128be < BinData::Single
167
+
168
+ # Unsigned 1 byte integer.
169
+ class Uint8 < BinData::BasePrimitive
163
170
  register(self.name, self)
164
- Integer.create_uint_methods(self, 128, :big)
171
+ Integer.create_uint_methods(self, 8, :little)
165
172
  end
166
173
 
167
174
  # Signed 1 byte integer.
168
- class Int8 < BinData::Single
175
+ class Int8 < BinData::BasePrimitive
169
176
  register(self.name, self)
170
177
  Integer.create_int_methods(self, 8, :little)
171
178
  end
172
179
 
173
- # Signed 2 byte little endian integer.
174
- class Int16le < BinData::Single
175
- register(self.name, self)
176
- Integer.create_int_methods(self, 16, :little)
177
- end
178
-
179
- # Signed 2 byte big endian integer.
180
- class Int16be < BinData::Single
181
- register(self.name, self)
182
- Integer.create_int_methods(self, 16, :big)
183
- end
184
-
185
- # Signed 4 byte little endian integer.
186
- class Int32le < BinData::Single
187
- register(self.name, self)
188
- Integer.create_int_methods(self, 32, :little)
189
- end
190
-
191
- # Signed 4 byte big endian integer.
192
- class Int32be < BinData::Single
193
- register(self.name, self)
194
- Integer.create_int_methods(self, 32, :big)
195
- end
196
-
197
- # Signed 8 byte little endian integer.
198
- class Int64le < BinData::Single
199
- register(self.name, self)
200
- Integer.create_int_methods(self, 64, :little)
201
- end
202
-
203
- # Signed 8 byte big endian integer.
204
- class Int64be < BinData::Single
205
- register(self.name, self)
206
- Integer.create_int_methods(self, 64, :big)
207
- end
208
-
209
- # Signed 16 byte little endian integer.
210
- class Int128le < BinData::Single
211
- register(self.name, self)
212
- Integer.create_int_methods(self, 128, :little)
213
- end
214
-
215
- # Signed 16 byte big endian integer.
216
- class Int128be < BinData::Single
217
- register(self.name, self)
218
- Integer.create_int_methods(self, 128, :big)
180
+ # Create commonly used integers
181
+ [8, 16, 32, 64, 128].each do |nbits|
182
+ Integer.define_class(nbits, :little, :unsigned)
183
+ Integer.define_class(nbits, :little, :signed)
184
+ Integer.define_class(nbits, :big, :unsigned)
185
+ Integer.define_class(nbits, :big, :signed)
219
186
  end
220
187
  end