bindata 0.9.3 → 0.10.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.
- data/ChangeLog +18 -0
- data/NEWS +59 -0
- data/README +22 -23
- data/TODO +18 -12
- data/examples/gzip.rb +4 -4
- data/lib/bindata.rb +4 -3
- data/lib/bindata/array.rb +202 -132
- data/lib/bindata/base.rb +147 -166
- data/lib/bindata/{single.rb → base_primitive.rb} +82 -56
- data/lib/bindata/bits.rb +31 -770
- data/lib/bindata/choice.rb +157 -82
- data/lib/bindata/float.rb +25 -27
- data/lib/bindata/int.rb +144 -177
- data/lib/bindata/io.rb +59 -49
- data/lib/bindata/lazy.rb +80 -50
- data/lib/bindata/params.rb +134 -26
- data/lib/bindata/{single_value.rb → primitive.rb} +71 -64
- data/lib/bindata/{multi_value.rb → record.rb} +52 -70
- data/lib/bindata/registry.rb +49 -17
- data/lib/bindata/rest.rb +6 -10
- data/lib/bindata/sanitize.rb +55 -70
- data/lib/bindata/string.rb +60 -42
- data/lib/bindata/stringz.rb +34 -35
- data/lib/bindata/struct.rb +197 -152
- data/lib/bindata/trace.rb +35 -0
- data/spec/array_spec.rb +128 -112
- data/spec/{single_spec.rb → base_primitive_spec.rb} +102 -61
- data/spec/base_spec.rb +190 -185
- data/spec/bits_spec.rb +126 -98
- data/spec/choice_spec.rb +89 -98
- data/spec/example.rb +19 -0
- data/spec/float_spec.rb +28 -44
- data/spec/int_spec.rb +217 -127
- data/spec/io_spec.rb +41 -24
- data/spec/lazy_spec.rb +95 -49
- data/spec/primitive_spec.rb +191 -0
- data/spec/{multi_value_spec.rb → record_spec.rb} +124 -89
- data/spec/registry_spec.rb +53 -12
- data/spec/rest_spec.rb +2 -3
- data/spec/sanitize_spec.rb +47 -73
- data/spec/spec_common.rb +13 -1
- data/spec/string_spec.rb +34 -23
- data/spec/stringz_spec.rb +10 -18
- data/spec/struct_spec.rb +91 -63
- data/spec/system_spec.rb +291 -0
- metadata +12 -8
- data/spec/single_value_spec.rb +0 -131
data/lib/bindata/choice.rb
CHANGED
@@ -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.
|
33
|
+
# a.to_binary_s #=> "\001\000"
|
33
34
|
# mychoice.replace 'little'
|
34
35
|
# a.selection #=> 'little'
|
35
|
-
# a.
|
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>::
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
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
|
-
|
59
|
-
|
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
|
-
|
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
|
-
@
|
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
|
-
|
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::
|
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
|
-
|
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 ||
|
159
|
+
super || current_choice.respond_to?(symbol, include_private)
|
151
160
|
end
|
152
161
|
|
153
162
|
def method_missing(symbol, *args, &block)
|
154
|
-
if
|
155
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
169
|
-
|
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 =
|
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
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
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
|
-
|
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/
|
1
|
+
require 'bindata/base_primitive'
|
2
2
|
|
3
3
|
module BinData
|
4
|
-
#
|
5
|
-
# The float is defined by
|
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
|
8
|
-
def self.create_float_methods(
|
9
|
-
read = create_read_code(
|
10
|
-
to_s = create_to_s_code(
|
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(
|
13
|
+
define_methods(float_class, nbytes, read, to_s)
|
13
14
|
end
|
14
15
|
|
15
|
-
def self.create_read_code(
|
16
|
-
if
|
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(
|
28
|
-
if
|
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(
|
38
|
-
|
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
|
51
|
+
def value_to_binary_string(val)
|
54
52
|
#{to_s}
|
55
53
|
end
|
56
54
|
|
57
|
-
def
|
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::
|
64
|
+
class FloatLe < BinData::BasePrimitive
|
67
65
|
register(self.name, self)
|
68
|
-
|
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::
|
70
|
+
class FloatBe < BinData::BasePrimitive
|
73
71
|
register(self.name, self)
|
74
|
-
|
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::
|
76
|
+
class DoubleLe < BinData::BasePrimitive
|
79
77
|
register(self.name, self)
|
80
|
-
|
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::
|
82
|
+
class DoubleBe < BinData::BasePrimitive
|
85
83
|
register(self.name, self)
|
86
|
-
|
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/
|
1
|
+
require 'bindata/base_primitive'
|
2
2
|
|
3
3
|
module BinData
|
4
|
-
#
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
27
|
+
def create_uint_methods(int_class, nbits, endian)
|
28
|
+
min = 0
|
29
|
+
max = (1 << nbits) - 1
|
16
30
|
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
35
|
+
define_methods(int_class, nbits / 8, clamp, read, to_s)
|
36
|
+
end
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
46
|
+
int2uint = create_int2uint_code(nbits)
|
47
|
+
uint2int = create_uint2int_code(nbits)
|
33
48
|
|
34
|
-
|
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
|
-
|
58
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
88
|
-
|
89
|
-
end
|
60
|
+
def create_uint2int_code(nbits)
|
61
|
+
mask = (1 << (nbits - 1)) - 1
|
90
62
|
|
91
|
-
|
92
|
-
|
63
|
+
"val = ((val & #{1 << (nbits - 1)}).zero?) ? " +
|
64
|
+
"val & #{mask} : -(((~val) & #{mask}) + 1)"
|
65
|
+
end
|
93
66
|
|
94
|
-
|
95
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
144
|
-
|
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
|
-
|
150
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
162
|
-
|
167
|
+
|
168
|
+
# Unsigned 1 byte integer.
|
169
|
+
class Uint8 < BinData::BasePrimitive
|
163
170
|
register(self.name, self)
|
164
|
-
Integer.create_uint_methods(self,
|
171
|
+
Integer.create_uint_methods(self, 8, :little)
|
165
172
|
end
|
166
173
|
|
167
174
|
# Signed 1 byte integer.
|
168
|
-
class Int8 < BinData::
|
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
|
-
#
|
174
|
-
|
175
|
-
|
176
|
-
Integer.
|
177
|
-
|
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
|