libbin 1.0.5 → 2.0.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.
- checksums.yaml +4 -4
- data/ext/libbin/data_types.c +557 -0
- data/ext/libbin/data_types.h +16 -0
- data/ext/libbin/libbin_c.c +1916 -41
- data/ext/libbin/libbin_c.h +12 -0
- data/ext/libbin/libbin_endian.h +126 -0
- data/lib/libbin/data_types.rb +629 -398
- data/lib/libbin.rb +77 -355
- data/libbin.gemspec +2 -3
- metadata +8 -6
- data/lib/libbin/alignment.rb +0 -14
data/lib/libbin/data_types.rb
CHANGED
@@ -1,489 +1,720 @@
|
|
1
1
|
module LibBin
|
2
2
|
|
3
|
+
# Refinement to Range allowing reducing ranges through the + operator
|
3
4
|
module RangeRefinement
|
4
5
|
refine Range do
|
6
|
+
# Union of two ranges
|
7
|
+
# @return [Range]
|
5
8
|
def +(other)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
+
return other.dup unless min
|
10
|
+
return self.dup unless other.min
|
11
|
+
Range::new(min <= other.min ? min : other.min,
|
12
|
+
max >= other.max ? max : other.max)
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
12
16
|
|
13
|
-
|
17
|
+
# Classs that can be used to get the shape of a {Structure} or of a {Structure::Scalar}.
|
18
|
+
class DataRange
|
14
19
|
using RangeRefinement
|
15
20
|
attr_reader :range
|
16
|
-
attr_reader :members
|
17
|
-
|
18
|
-
def method_missing(m, *arg, &block)
|
19
|
-
return @members[m] if @members && @members[m]
|
20
|
-
super
|
21
|
-
end
|
22
21
|
|
22
|
+
# @overload initialize(min, max)
|
23
|
+
# Create a new shape starting and +min+ and ending at max.
|
24
|
+
# This shape has no members.
|
25
|
+
# @param min [Integer] start of the shape
|
26
|
+
# @param max [Integer] end of the shape
|
27
|
+
# @return [DataShape] a new DataShape
|
28
|
+
# @overload initialize(members)
|
29
|
+
# Creates a new shape by reducing the shape of it's memebers.
|
30
|
+
# @param members [DataShape] the members composing the shape
|
31
|
+
# @return [DataShape] a new DataShape
|
23
32
|
def initialize(*args)
|
24
33
|
if args.length == 2
|
25
34
|
@range = Range::new(args[0], args[1])
|
26
|
-
@members = nil
|
27
35
|
else
|
28
|
-
|
29
|
-
|
36
|
+
if args[0].kind_of?(Hash)
|
37
|
+
@range = args[0].values.compact.collect(&:range).reduce(:+)
|
38
|
+
else
|
39
|
+
@range = args[0].compact.collect(&:range).reduce(:+)
|
40
|
+
end
|
30
41
|
end
|
31
42
|
end
|
32
43
|
|
44
|
+
# Return the beginning of the shape
|
45
|
+
# @return [Integer]
|
33
46
|
def first
|
34
47
|
@range.first
|
35
48
|
end
|
36
49
|
|
50
|
+
# Return the end of the shape
|
51
|
+
# @return [Integer]
|
37
52
|
def last
|
38
53
|
@range.last
|
39
54
|
end
|
40
55
|
|
56
|
+
# Return the size of the shape
|
57
|
+
# @return [Integer]
|
41
58
|
def size
|
42
59
|
@range.size
|
43
60
|
end
|
44
61
|
|
45
62
|
end
|
46
63
|
|
47
|
-
class
|
64
|
+
# Default class used to get the shape of a {Structure} or of a {Structure::Scalar}.
|
65
|
+
# It maintains a memory of it's members
|
66
|
+
class DataShape # < DataRange
|
48
67
|
using RangeRefinement
|
49
|
-
attr_reader :
|
68
|
+
attr_reader :members
|
50
69
|
|
70
|
+
# Add method readers for members
|
71
|
+
def method_missing(m, *arg, &block)
|
72
|
+
return @members[m] if @members && @members[m]
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
# @overload initialize(min, max)
|
77
|
+
# Create a new shape starting and +min+ and ending at max.
|
78
|
+
# This shape has no members.
|
79
|
+
# @param min [Integer] start of the shape
|
80
|
+
# @param max [Integer] end of the shape
|
81
|
+
# @return [DataShape] a new DataShape
|
82
|
+
# @overload initialize(members)
|
83
|
+
# Creates a new shape by reducing the shape of it's memeber.
|
84
|
+
# Members can be accessed through {members}.
|
85
|
+
# @param members [DataShape] the members composing the shape
|
86
|
+
# @return [DataShape] a new DataShape
|
51
87
|
def initialize(*args)
|
52
|
-
if args.length ==
|
53
|
-
@
|
54
|
-
else
|
55
|
-
@range = args[0].values.flatten.compact.collect(&:range).reduce { |memo, obj| memo + obj }
|
88
|
+
if args.length == 1
|
89
|
+
@members = args[0]
|
56
90
|
end
|
91
|
+
super
|
57
92
|
end
|
58
93
|
|
59
|
-
|
60
|
-
|
94
|
+
end
|
95
|
+
|
96
|
+
# @abstract This class is used to describe a binary data structure
|
97
|
+
# composed of different fields.
|
98
|
+
class Structure
|
99
|
+
|
100
|
+
# @!parse
|
101
|
+
# # Field class that is instantiated to describe a structure field.
|
102
|
+
# # Instances are immutable.
|
103
|
+
# class Field
|
104
|
+
# attr_reader :name
|
105
|
+
# attr_reader :type
|
106
|
+
# attr_reader :length
|
107
|
+
# attr_reader :count
|
108
|
+
# attr_reader :offset
|
109
|
+
# attr_reader :condition
|
110
|
+
# attr_reader :align
|
111
|
+
# attr_reader :expect
|
112
|
+
#
|
113
|
+
# # @method relative_offset?
|
114
|
+
# # Returns +true+ if the field offset should be relative to its parent.
|
115
|
+
# # @return [Boolean] field offset is relative
|
116
|
+
#
|
117
|
+
# # @method sequence?
|
118
|
+
# # Returns +true+ if the field is a sequence
|
119
|
+
# # @return [Boolean] field is a sequence
|
120
|
+
# end
|
121
|
+
|
122
|
+
# Define a new field in the structure
|
123
|
+
# @param name [Symbol, String] the name of the field.
|
124
|
+
# @param type [Class, String, Proc] the type of the field, as a Class, or
|
125
|
+
# as a String or Proc that will be evaluated in the context of the
|
126
|
+
# {Structure} instance.
|
127
|
+
# @param length [Integer, String, Proc] if given, consider the field a
|
128
|
+
# vector of the type. The length is either a constant Integer of a
|
129
|
+
# String or Proc that will be evaluated in the context of the
|
130
|
+
# {Structure} instance.
|
131
|
+
# @param count [Integer, String, Proc] if given, consider the field is
|
132
|
+
# repeated count times. The count is either a constant Integer of a
|
133
|
+
# String or Proc that will be evaluated in the context of the
|
134
|
+
# {Structure} instance.
|
135
|
+
# @param offset [integer, String, Proc] if given, the absolute offset in
|
136
|
+
# the file, or the offset from the parent position, where the field can
|
137
|
+
# be found. See relative offset. The offset is either a constant
|
138
|
+
# Integer of a String or Proc that will be evaluated in the context
|
139
|
+
# of the {Structure} instance.
|
140
|
+
# @param sequence [Boolean] if true, +type+, +length+, +offset+, and
|
141
|
+
# +condition+ are evaluated for each repetition.
|
142
|
+
# @param condition [String, Proc] if given, the field, or repetition of the
|
143
|
+
# field can be conditionally present. The condition will be evaluated in
|
144
|
+
# the context of the {Structure} instance.
|
145
|
+
# @param relative_offset [Boolean] consider the +offset+ relative to the
|
146
|
+
# field +parent+.
|
147
|
+
# @param align [Boolean,Integer] if given, align the field. If given as an
|
148
|
+
# Integer it must be a power of 2. Else the field is aligned to the
|
149
|
+
# field's type preferred alignment.
|
150
|
+
# @param expect [Proc, Object] if given, the field value must validate or
|
151
|
+
# an eexception is raised. If a proc the proc resulted must be truthy. If
|
152
|
+
# not, the object will be tested for equality.
|
153
|
+
# @return self
|
154
|
+
def self.field(name, type, length: nil, count: nil, offset: nil,
|
155
|
+
sequence: false, condition: nil, relative_offset: false,
|
156
|
+
align: false, expect: nil)
|
157
|
+
if type.respond_to?(:always_align) && type.always_align
|
158
|
+
al = type.align
|
159
|
+
if align.kind_of?(Integer)
|
160
|
+
align = align >= al ? align : al
|
161
|
+
else
|
162
|
+
align = al
|
163
|
+
end
|
164
|
+
end
|
165
|
+
if align == true
|
166
|
+
# Automatic alignment not supported for dynamic types
|
167
|
+
if type.respond_to?(:align)
|
168
|
+
align = type.align
|
169
|
+
else
|
170
|
+
raise "alignment is unsupported for dynamic types"
|
171
|
+
end
|
172
|
+
else
|
173
|
+
raise "alignement must be a power of 2" if align && (align - 1) & align != 0
|
174
|
+
end
|
175
|
+
@fields.push(Field::new(name, type, length, count, offset, sequence,
|
176
|
+
condition, relative_offset, align, expect))
|
177
|
+
attr_accessor name
|
178
|
+
self
|
61
179
|
end
|
62
180
|
|
63
|
-
|
64
|
-
|
181
|
+
class << self
|
182
|
+
alias register_field field
|
65
183
|
end
|
66
184
|
|
67
|
-
|
68
|
-
|
185
|
+
# @!parse
|
186
|
+
# # @abstract Parent class for scalars
|
187
|
+
# class Scalar
|
188
|
+
# end
|
189
|
+
|
190
|
+
# @!macro attach
|
191
|
+
# @!parse
|
192
|
+
# # $4
|
193
|
+
# class $1 < Scalar
|
194
|
+
# # @method align
|
195
|
+
# # @scope class
|
196
|
+
# # Returns the alignment of {$1}.
|
197
|
+
# # @return [Integer] the byte boundary to align the type to.
|
198
|
+
# # @method shape(value = nil, offset = 0, parent = nil, index = nil, kind = DataShape, length = nil)
|
199
|
+
# # @scope class
|
200
|
+
# # Returns the shape of a field of type {$1}
|
201
|
+
# # @param value [Object] ignored.
|
202
|
+
# # @param offset [Integer] start of the shape.
|
203
|
+
# # @param parent [Structure] ignored.
|
204
|
+
# # @param index [Integer] ignored.
|
205
|
+
# # @param kind [Class] shape class. Will be instantiated through
|
206
|
+
# # new with the +offset+ and <tt>offset + sizeof($3) * length - 1</tt>.
|
207
|
+
# # @param length [Integer] if given the length of the vector. Else
|
208
|
+
# # the length is considered to be 1.
|
209
|
+
# # @return [kind] a new instance of +kind+
|
210
|
+
# # @method size(value = nil, offset = 0, parent = nil, index = nil, length = nil)
|
211
|
+
# # @scope class
|
212
|
+
# # Returns the size of a field of type {$1}.
|
213
|
+
# # @param value [Object] ignored.
|
214
|
+
# # @param offset [Integer] ignored.
|
215
|
+
# # @param parent [Structure] ignored.
|
216
|
+
# # @param index [Integer] ignored.
|
217
|
+
# # @param length [Integer] if given the length of the vector. Else
|
218
|
+
# # the length is considered to be 1.
|
219
|
+
# # @return [Integer] the type <tt>sizeof($3) * length</tt>.
|
220
|
+
# # @method load(input, input_big = LibBin::default_big?, parent = nil, index = nil, length = nil)
|
221
|
+
# # @scope class
|
222
|
+
# # Load a field of type {$1} from +input+, and return it.
|
223
|
+
# # @param input [IO] the stream to load the field from.
|
224
|
+
# # @param input_big [Boolean] the endianness of +input+
|
225
|
+
# # @param parent [Structure] ignored.
|
226
|
+
# # @param index [Integer] ignored.
|
227
|
+
# # @param length [Integer] if given the length of the vector. Else
|
228
|
+
# # the length is considered to be 1.
|
229
|
+
# # @return [Numeric, Array<Numeric>] the Ruby representation of the
|
230
|
+
# # type, or the Array representation of the vector if +length+
|
231
|
+
# # was specified
|
232
|
+
# # @method dump(value, output, output_big = LibBin::default_big?, parent = nil, index = nil, length = nil)
|
233
|
+
# # @scope class
|
234
|
+
# # Dump a field of class {$1} to +output+.
|
235
|
+
# # @param value [Numeric, Array<Numeric>] the Ruby representation
|
236
|
+
# # of the type, or the Array representation of the vector if
|
237
|
+
# # +length+ is specified.
|
238
|
+
# # @param output [IO] the stream to dump the field to.
|
239
|
+
# # @param output_big [Boolean] the endianness of +output+.
|
240
|
+
# # @param parent [Structure] ignored.
|
241
|
+
# # @param index [Integer] ignored.
|
242
|
+
# # @param length [Integer] if given the length of the vector. Else
|
243
|
+
# # the length is considered to be 1.
|
244
|
+
# # @return [nil]
|
245
|
+
# # @method convert(input, output, input_big = LibBin::default_big?, output_big = !input_big, parent = nil, index = nil, length = nil)
|
246
|
+
# # @scope class
|
247
|
+
# # Convert a field of class {$1} by loading it from +input+ and
|
248
|
+
# # dumping it to +output+. Returns the loaded field.
|
249
|
+
# # @param input [IO] the stream to load the field from.
|
250
|
+
# # @param output [IO] the stream to dump the field to.
|
251
|
+
# # @param input_big [Boolean] the endianness of +input+
|
252
|
+
# # @param output_big [Boolean] the endianness of +output+.
|
253
|
+
# # @param parent [Structure] ignored.
|
254
|
+
# # @param index [Integer] ignored.
|
255
|
+
# # @param length [Integer] if given the length of the vector. Else
|
256
|
+
# # the length is considered to be 1.
|
257
|
+
# # @return [Numeric, Array<Numeric>] the Ruby representation of the
|
258
|
+
# # type, or the Array representation of the vector if +length+
|
259
|
+
# # was specified
|
260
|
+
# end
|
261
|
+
# @!method $2 method_signature(name, length: nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false, align: false)
|
262
|
+
# @!scope class
|
263
|
+
# Create a new field of type {$1} and name +name+. See {field} for options.
|
264
|
+
# @return self
|
265
|
+
#
|
266
|
+
def self.define_scalar_constructor(klassname, name, mapped_type, description)
|
267
|
+
eval <<EOF
|
268
|
+
def self.#{name}(name, length: nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false, align: false, expect: nil)
|
269
|
+
if align == true
|
270
|
+
align = #{klassname}.align
|
271
|
+
else
|
272
|
+
raise "alignement must be a power of 2" if align && (align - 1) & align != 0
|
273
|
+
end
|
274
|
+
@fields.push(Field::new(name, #{klassname}, length, count, offset, sequence, condition, relative_offset, align, expect))
|
275
|
+
attr_accessor name
|
276
|
+
self
|
277
|
+
end
|
278
|
+
EOF
|
279
|
+
end
|
280
|
+
private_class_method :define_scalar_constructor
|
281
|
+
|
282
|
+
# @!group Scalars
|
283
|
+
define_scalar_constructor "Int8", :int8, :int8_t, "A signed 8 bit integer"
|
284
|
+
define_scalar_constructor "UInt8", :uint8, :uint8_t, "An unsigned 8 bit integer"
|
285
|
+
define_scalar_constructor "Int16", :int16, :int16_t, "A signed 16 bit integer"
|
286
|
+
define_scalar_constructor "Int16_LE", :int16_le, :int16_t, "A signed little endian 16 bit integer"
|
287
|
+
define_scalar_constructor "Int16_BE", :int16_be, :int16_t, "A signed big endian 16 bit integer"
|
288
|
+
define_scalar_constructor "UInt16", :uint16, :uint16_t, "An unsigned 16 bit integer"
|
289
|
+
define_scalar_constructor "UInt16_LE", :uint16_le, :uint16_t, "An unsigned little endian 16 bit integer"
|
290
|
+
define_scalar_constructor "UInt16_BE", :uint16_be, :uint16_t, "An unsigned big endian 16 bit integer"
|
291
|
+
define_scalar_constructor "Int32", :int32, :int32_t, "A signed little endian 32 bit integer"
|
292
|
+
define_scalar_constructor "Int32_LE", :int32_le, :int32_t, "A signed little endian 32 bit integer"
|
293
|
+
define_scalar_constructor "Int32_BE", :int32_be, :int32_t, "A signed big endian 32 bit integer"
|
294
|
+
define_scalar_constructor "UInt32", :uint32, :uint32_t, "An unsigned 32 bit integer"
|
295
|
+
define_scalar_constructor "UInt32_LE", :uint32_le, :uint32_t, "An unsigned little endian 32 bit integer"
|
296
|
+
define_scalar_constructor "UInt32_BE", :uint32_be, :uint32_t, "An unsigned big endian 32 bit integer"
|
297
|
+
define_scalar_constructor "Int64", :int64, :int64_t, "A signed little endian 64 bit integer"
|
298
|
+
define_scalar_constructor "Int64_LE", :int64_le, :int64_t, "A signed little endian 64 bit integer"
|
299
|
+
define_scalar_constructor "Int64_BE", :int64_be, :int64_t, "A signed big endian 64 bit integer"
|
300
|
+
define_scalar_constructor "UInt64", :uint64, :uint64_t, "An unsigned 64 bit integer"
|
301
|
+
define_scalar_constructor "UInt64_LE", :uint64_le, :uint64_t, "An unsigned little endian 64 bit integer"
|
302
|
+
define_scalar_constructor "UInt64_BE", :uint64_be, :uint64_t, "An unsigned big endian 64 bit integer"
|
303
|
+
define_scalar_constructor "Flt", :float, :float, "A single precision floating point scalar"
|
304
|
+
define_scalar_constructor "Flt_LE", :float_le, :float, "A single precision little endian floating point scalar"
|
305
|
+
define_scalar_constructor "Flt_BE", :float_be, :float, "A single precision big endian floating point scalar"
|
306
|
+
define_scalar_constructor "Double", :double, :double, "A double precision floating point scalar"
|
307
|
+
define_scalar_constructor "Double_LE", :double_le, :double, "A double precision little endian floating point scalar"
|
308
|
+
define_scalar_constructor "Double_BE", :double_be, :double, "A double precision big endian floating point scalar"
|
309
|
+
define_scalar_constructor "Half", :half, :half, "A half precision floating point scalar"
|
310
|
+
define_scalar_constructor "Half_LE", :half_le, :half, "A half precision little endian floating point scalar"
|
311
|
+
define_scalar_constructor "Half_BE", :half_be, :half, "A half precision big endian floating point scalar"
|
312
|
+
define_scalar_constructor "PGHalf", :pghalf, :pghalf, "A half precision floating point scalar as used by PlatinumGames in certain formats"
|
313
|
+
define_scalar_constructor "PGHalf_LE", :pghalf_le, :pghalf, "A half precision little endian floating point scalar as used by PlatinumGames in certain formats"
|
314
|
+
define_scalar_constructor "PGHalf_BE", :pghalf_be, :pghalf, "A half precision big endian floating point scalar as used by PlatinumGames in certain formats"
|
315
|
+
|
316
|
+
# @!parse
|
317
|
+
# # A string type, can be NULL terminated or have an arbitrary length.
|
318
|
+
# class Str < Scalar
|
319
|
+
# end
|
320
|
+
|
321
|
+
# Create a new field of type {Str} and name +name+. See {field} for options.
|
322
|
+
# @return self
|
323
|
+
def self.string(field, length = nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false, align: false, expect: nil)
|
324
|
+
if align == true
|
325
|
+
align = Str.align
|
326
|
+
else
|
327
|
+
raise "alignement must be a power of 2" if align && (align - 1) & align != 0
|
328
|
+
end
|
329
|
+
@fields.push(Field::new(field, Str, length, count, offset, sequence, condition, relative_offset, align, expect))
|
330
|
+
attr_accessor field
|
331
|
+
self
|
69
332
|
end
|
70
333
|
|
71
|
-
|
334
|
+
# @abstract A parent class representing an enumeration that is loading integers as symbols.
|
335
|
+
# Useer should inherit this base class.
|
336
|
+
class Enum
|
337
|
+
class << self
|
338
|
+
#get the enum map
|
339
|
+
attr_reader :map
|
340
|
+
# Get the underlying type
|
341
|
+
attr_accessor :type
|
342
|
+
|
343
|
+
# Specify the size of the underlying type
|
344
|
+
def type_size=(sz)
|
345
|
+
t = eval "Int#{sz}"
|
346
|
+
raise "unsupported enum size #{sz}" unless t
|
347
|
+
@type = t
|
348
|
+
return sz
|
349
|
+
end
|
350
|
+
alias set_type_size type_size=
|
72
351
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
:count,
|
78
|
-
:offset,
|
79
|
-
:sequence,
|
80
|
-
:condition
|
81
|
-
|
82
|
-
def sequence?
|
83
|
-
@sequence
|
84
|
-
end
|
352
|
+
# Get the underlying type size in bits
|
353
|
+
def type_size
|
354
|
+
@type.size
|
355
|
+
end
|
85
356
|
|
86
|
-
|
87
|
-
|
88
|
-
|
357
|
+
# Set the enum map
|
358
|
+
# @param m [Hash{Symbol=>Integer}] enum values
|
359
|
+
def map=(m)
|
360
|
+
@map_to = m.invert
|
361
|
+
@map_from = @map = m
|
362
|
+
end
|
363
|
+
alias set_map map=
|
89
364
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
@offset = offset
|
96
|
-
@sequence = sequence
|
97
|
-
@condition = condition
|
98
|
-
@relative_offset = relative_offset
|
99
|
-
end
|
365
|
+
# @!visibility private
|
366
|
+
def inherited(subclass)
|
367
|
+
subclass.instance_variable_set(:@type, Int32)
|
368
|
+
end
|
369
|
+
end
|
100
370
|
|
101
|
-
|
371
|
+
# Returns the underlying type +always_align+ property
|
372
|
+
def self.always_align
|
373
|
+
@type.always_align
|
374
|
+
end
|
102
375
|
|
103
|
-
|
376
|
+
# Returns the underlying type +align+ property
|
377
|
+
def self.align
|
378
|
+
@type.align
|
379
|
+
end
|
104
380
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
else
|
109
|
-
str.unpack(type.to_s).first
|
381
|
+
# Forwards the call to the underlying type
|
382
|
+
def self.size(value = nil, previous_offset = 0, parent = nil, index = nil, length = nil)
|
383
|
+
@type.size(value, previous_offset, parent, index, length)
|
110
384
|
end
|
111
|
-
}
|
112
385
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
[
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
:l => [:Int32, :int32],
|
137
|
-
:"l<" => [:Int32_LE, :int32_le],
|
138
|
-
:"l>" => [:Int32_BE, :int32_be],
|
139
|
-
:L => [:UInt32, :uint32],
|
140
|
-
:"L<" => [:UInt32_LE, :uint32_le],
|
141
|
-
:"L>" => [:UInt32_BE, :uint32_be],
|
142
|
-
:V => [:UInt32_LE, :uint32_le],
|
143
|
-
:N => [:UInt32_BE, :uint32_be],
|
144
|
-
:q => [:Int64, :int64],
|
145
|
-
:"q<" => [:Int64_LE, :int64_le],
|
146
|
-
:"q>" => [:Int64_BE, :int64_be],
|
147
|
-
:Q => [:UInt64, :uint64],
|
148
|
-
:"Q<" => [:UInt64_LE, :uint64_le],
|
149
|
-
:"Q>" => [:UInt64_BE, :uint64_be],
|
150
|
-
:F => [:Flt, :float],
|
151
|
-
:e => [:Flt_LE, :float_le],
|
152
|
-
:g => [:Flt_BE, :float_be],
|
153
|
-
:D => [:Double, :double],
|
154
|
-
:E => [:Double_LE, :double_le],
|
155
|
-
:G => [:Double_BE, :double_be],
|
156
|
-
:half => [:Half, :half],
|
157
|
-
:half_le => [:Half_LE, :half_le],
|
158
|
-
:half_be => [:Half_BE, :half_be],
|
159
|
-
:pghalf => [:PGHalf, :pghalf],
|
160
|
-
:pghalf_le => [:PGHalf_LE, :pghalf_le],
|
161
|
-
:pghalf_be => [:PGHalf_BE, :pghalf_be]
|
162
|
-
}
|
163
|
-
|
164
|
-
DATA_SIZES = Hash::new { |h,k|
|
165
|
-
if k.kind_of?(Symbol) && m = k.match(/a(\d+)/)
|
166
|
-
m[1].to_i
|
167
|
-
else
|
168
|
-
nil
|
169
|
-
end
|
170
|
-
}
|
171
|
-
DATA_SIZES.merge!( {
|
172
|
-
:c => 1,
|
173
|
-
:C => 1,
|
174
|
-
:s => 2,
|
175
|
-
:"s<" => 2,
|
176
|
-
:"s>" => 2,
|
177
|
-
:S => 2,
|
178
|
-
:"S<" => 2,
|
179
|
-
:"S>" => 2,
|
180
|
-
:v => 2,
|
181
|
-
:n => 2,
|
182
|
-
:l => 4,
|
183
|
-
:"l<" => 4,
|
184
|
-
:"l>" => 4,
|
185
|
-
:L => 4,
|
186
|
-
:"L<" => 4,
|
187
|
-
:"L>" => 4,
|
188
|
-
:V => 4,
|
189
|
-
:N => 4,
|
190
|
-
:q => 8,
|
191
|
-
:"q<" => 8,
|
192
|
-
:"q>" => 8,
|
193
|
-
:Q => 8,
|
194
|
-
:"Q<" => 8,
|
195
|
-
:"Q>" => 8,
|
196
|
-
:F => 4,
|
197
|
-
:e => 4,
|
198
|
-
:g => 4,
|
199
|
-
:D => 8,
|
200
|
-
:E => 8,
|
201
|
-
:G => 8,
|
202
|
-
:a => 1,
|
203
|
-
:"a*" => -1,
|
204
|
-
:half => 2,
|
205
|
-
:half_le => 2,
|
206
|
-
:half_be => 2,
|
207
|
-
:pghalf => 2,
|
208
|
-
:pghalf_le => 2,
|
209
|
-
:pghalf_be => 2
|
210
|
-
} )
|
211
|
-
DATA_ENDIAN = {
|
212
|
-
true => Hash::new { |h,k|
|
213
|
-
if k.kind_of?(Symbol) && m = k.match(/a(\d+)/)
|
214
|
-
l[k]
|
386
|
+
# Forwards the call to the underlying type
|
387
|
+
def self.shape(value = nil, previous_offset = 0, parent = nil, index = nil, kind = DataShape, length = nil)
|
388
|
+
@type.shape(value, previous_offset, parent, index, kind, length)
|
389
|
+
end
|
390
|
+
|
391
|
+
# Load a field of type {Enum} from +input+, and return it.
|
392
|
+
# @param input [IO] the stream to load the field from.
|
393
|
+
# @param input_big [Boolean] the endianness of +input+
|
394
|
+
# @param parent [Structure] ignored.
|
395
|
+
# @param index [Integer] ignored.
|
396
|
+
# @param length [Integer] if given the length of the vector. Else
|
397
|
+
# the length is considered to be 1.
|
398
|
+
# @return [Symbol,Integer,Array<Symbol,Integer>] the Ruby
|
399
|
+
# representation of the type, or the Array representation of the
|
400
|
+
# vector if +length+ was specified
|
401
|
+
def self.load(input, input_big = LibBin::default_big?, parent = nil, index = nil, length = nil)
|
402
|
+
v = @type.load(input, input_big, parent, index, length)
|
403
|
+
if length
|
404
|
+
v.collect { |val|
|
405
|
+
n = @map_to[val]
|
406
|
+
n = val unless n
|
407
|
+
n
|
408
|
+
}
|
215
409
|
else
|
216
|
-
|
410
|
+
n = @map_to[v]
|
411
|
+
n = v unless n
|
412
|
+
n
|
217
413
|
end
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
414
|
+
end
|
415
|
+
|
416
|
+
# Convert a field of class {Enum} by loading it from +input+ and
|
417
|
+
# dumping it to +output+. Returns the loaded field.
|
418
|
+
# @param input [IO] the stream to load the field from.
|
419
|
+
# @param output [IO] the stream to dump the field to.
|
420
|
+
# @param input_big [Boolean] the endianness of +input+
|
421
|
+
# @param output_big [Boolean] the endianness of +output+.
|
422
|
+
# @param parent [Structure] ignored.
|
423
|
+
# @param index [Integer] ignored.
|
424
|
+
# @param length [Integer] if given the length of the vector. Else
|
425
|
+
# the length is considered to be 1.
|
426
|
+
# @return [Symbol,Integer,Array<Symbol,Integer>] the Ruby representation
|
427
|
+
# of the type, or the Array representation of the vector if +length+
|
428
|
+
# was specified
|
429
|
+
def self.convert(input, output, input_big = LibBin::default_big?, output_big = !input_big, parent = nil, index = nil, length = nil)
|
430
|
+
v = @type.convert(input, output, input_big, output_big, parent, index, length)
|
431
|
+
if length
|
432
|
+
v.collect { |val|
|
433
|
+
n = @map_to[val]
|
434
|
+
n = val unless n
|
435
|
+
n
|
436
|
+
}
|
222
437
|
else
|
223
|
-
|
438
|
+
n = @map_to[v]
|
439
|
+
n = v unless n
|
440
|
+
n
|
224
441
|
end
|
225
|
-
|
226
|
-
}
|
442
|
+
end
|
227
443
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
444
|
+
# Dump a field of class {Enum} to +output+.
|
445
|
+
# @param value [Numeric, Array<Numeric>] the Ruby representation
|
446
|
+
# of the type, or the Array representation of the vector if
|
447
|
+
# +length+ is specified.
|
448
|
+
# @param output [IO] the stream to dump the field to.
|
449
|
+
# @param output_big [Boolean] the endianness of +output+.
|
450
|
+
# @param parent [Structure] ignored.
|
451
|
+
# @param index [Integer] ignored.
|
452
|
+
# @param length [Integer] if given the length of the vector. Else
|
453
|
+
# the length is considered to be 1.
|
454
|
+
# @return [nil]
|
455
|
+
def self.dump(value, output, output_big = LibBin::default_big?, parent = nil, index = nil, length = nil)
|
456
|
+
if length
|
457
|
+
v = length.times.collect { |i|
|
458
|
+
val = @map_from[value[i]]
|
459
|
+
val = value[i] unless val
|
460
|
+
val
|
461
|
+
}
|
462
|
+
else
|
463
|
+
v = @map_from[value]
|
464
|
+
v = value unless v
|
465
|
+
end
|
466
|
+
@type.dump(v, output, output_big, parent, index, length)
|
233
467
|
end
|
234
|
-
|
468
|
+
end
|
235
469
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
470
|
+
# Create a new field of a type inheriting from {Enum} and name +name+.
|
471
|
+
# See {field} for more options
|
472
|
+
# @param name [Symbol,String] name of the field.
|
473
|
+
# @param map [Hash{Symbol => Integer}] enum values.
|
474
|
+
# @param size [Integer] size in bits of the underlying integer
|
475
|
+
# @return self
|
476
|
+
def self.enum(name, map, size: 32, length: nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false, align: false, expect: nil)
|
477
|
+
klass = Class.new(Enum) do |c|
|
478
|
+
c.type_size = size
|
479
|
+
c.map = map
|
480
|
+
end
|
481
|
+
if klass.always_align
|
482
|
+
al = klass.align
|
483
|
+
if align.kind_of?(Integer)
|
484
|
+
align = align >= al ? align : al
|
485
|
+
else
|
486
|
+
align = al
|
487
|
+
end
|
488
|
+
end
|
489
|
+
if align == true
|
490
|
+
align = klass.align
|
241
491
|
else
|
242
|
-
|
492
|
+
raise "alignement must be a power of 2" if align && (align - 1) & align != 0
|
243
493
|
end
|
244
|
-
|
494
|
+
@fields.push(Field::new(name, klass, length, count, offset, sequence, condition, relative_offset, align, expect))
|
495
|
+
attr_accessor name
|
496
|
+
self
|
497
|
+
end
|
245
498
|
|
246
|
-
|
247
|
-
|
248
|
-
|
499
|
+
# @abstract A parent class that represent a bitfield that is
|
500
|
+
# loading integers as an instance of itself.
|
501
|
+
# User should inherit this base class.
|
502
|
+
class Bitfield
|
503
|
+
class << self
|
504
|
+
# Bitfield's field names and number of bits
|
505
|
+
attr_reader :map
|
506
|
+
# Signedness of the underlying type
|
507
|
+
attr_reader :signed
|
508
|
+
# Underlying type
|
509
|
+
attr_reader :type
|
510
|
+
|
511
|
+
# Set the size and signedness of the underlying type
|
512
|
+
def set_type_size(sz, signed = false)
|
513
|
+
tname = "#{signed ? "" : "U"}Int#{sz}"
|
514
|
+
t = eval tname
|
515
|
+
raise "unsupported bitfield type #{tname}" unless t
|
516
|
+
@type = t
|
517
|
+
@signed = signed
|
518
|
+
return sz
|
519
|
+
end
|
249
520
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
521
|
+
# Set the underlying type
|
522
|
+
def type=(type)
|
523
|
+
@type = type
|
524
|
+
@signed = type.name.split('::').last[0] != "U"
|
525
|
+
type
|
526
|
+
end
|
527
|
+
alias set_type type=
|
528
|
+
|
529
|
+
# Set the bitfield's field names and number of bits
|
530
|
+
# Creates accessor to fields for instance of this class
|
531
|
+
def map=(m)
|
532
|
+
raise "map already set" if @map
|
533
|
+
@map = m.each.collect { |k, v| [k, [v, k.to_sym, :"#{k}="]] }.to_h
|
534
|
+
m.each_key { |k|
|
535
|
+
attr_accessor k
|
536
|
+
}
|
537
|
+
m
|
538
|
+
end
|
539
|
+
alias set_map map=
|
540
|
+
|
541
|
+
# @!visibility private
|
542
|
+
def inherited(subclass)
|
543
|
+
subclass.instance_variable_set(:@type, UInt32)
|
544
|
+
end
|
255
545
|
end
|
256
|
-
}
|
257
546
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
:c => l["c"],
|
274
|
-
:C => l["C"],
|
275
|
-
:s => l["s>"],
|
276
|
-
:"s<" => l["s<"],
|
277
|
-
:"s>" => l["s>"],
|
278
|
-
:S => l["S>"],
|
279
|
-
:"S<" => l["S<"],
|
280
|
-
:"S>" => l["S>"],
|
281
|
-
:v => l["v"],
|
282
|
-
:n => l["n"],
|
283
|
-
:l => l["l>"],
|
284
|
-
:"l<" => l["l<"],
|
285
|
-
:"l>" => l["l>"],
|
286
|
-
:L => l["L>"],
|
287
|
-
:"L<" => l["L<"],
|
288
|
-
:"L>" => l["L>"],
|
289
|
-
:V => l["V"],
|
290
|
-
:N => l["N"],
|
291
|
-
:q => l["q>"],
|
292
|
-
:"q<" => l["q<"],
|
293
|
-
:"q>" => l["q>"],
|
294
|
-
:Q => l["Q>"],
|
295
|
-
:"Q<" => l["Q<"],
|
296
|
-
:"Q>" => l["Q>"],
|
297
|
-
:F => l["g"],
|
298
|
-
:e => l["e"],
|
299
|
-
:g => l["g"],
|
300
|
-
:D => l["G"],
|
301
|
-
:E => l["E"],
|
302
|
-
:G => l["G"],
|
303
|
-
:half => hl["S>"],
|
304
|
-
:half_le => hl["S<"],
|
305
|
-
:half_be => hl["S>"],
|
306
|
-
:pghalf => pghl["S>"],
|
307
|
-
:pghalf_le => pghl["S<"],
|
308
|
-
:pghalf_be => pghl["S>"]
|
309
|
-
} )
|
310
|
-
DATA_ENDIAN[false].merge!( {
|
311
|
-
:c => l["c"],
|
312
|
-
:C => l["C"],
|
313
|
-
:s => l["s<"],
|
314
|
-
:"s<" => l["s<"],
|
315
|
-
:"s>" => l["s>"],
|
316
|
-
:S => l["S<"],
|
317
|
-
:"S<" => l["S<"],
|
318
|
-
:"S>" => l["S>"],
|
319
|
-
:v => l["v"],
|
320
|
-
:n => l["n"],
|
321
|
-
:l => l["l<"],
|
322
|
-
:"l<" => l["l<"],
|
323
|
-
:"l>" => l["l>"],
|
324
|
-
:L => l["L<"],
|
325
|
-
:"L<" => l["L<"],
|
326
|
-
:"L>" => l["L>"],
|
327
|
-
:V => l["V"],
|
328
|
-
:N => l["N"],
|
329
|
-
:q => l["q<"],
|
330
|
-
:"q<" => l["q<"],
|
331
|
-
:"q>" => l["q>"],
|
332
|
-
:Q => l["Q<"],
|
333
|
-
:"Q<" => l["Q<"],
|
334
|
-
:"Q>" => l["Q>"],
|
335
|
-
:F => l["e"],
|
336
|
-
:e => l["e"],
|
337
|
-
:g => l["g"],
|
338
|
-
:D => l["E"],
|
339
|
-
:E => l["E"],
|
340
|
-
:G => l["G"],
|
341
|
-
:half => hl["S<"],
|
342
|
-
:half_le => hl["S<"],
|
343
|
-
:half_be => hl["S>"],
|
344
|
-
:pghalf => pghl["S<"],
|
345
|
-
:pghalf_le => pghl["S<"],
|
346
|
-
:pghalf_be => pghl["S>"]
|
347
|
-
} )
|
348
|
-
|
349
|
-
|
350
|
-
class Scalar
|
351
|
-
|
352
|
-
def self.size(*args)
|
353
|
-
@size
|
354
|
-
end
|
355
|
-
|
356
|
-
def self.shape(value, previous_offset = 0, _ = nil, _ = nil, kind = DataShape, length = nil)
|
357
|
-
length = 1 unless length
|
358
|
-
kind::new(previous_offset, previous_offset - 1 + length * @size)
|
359
|
-
end
|
360
|
-
|
361
|
-
def self.init(symbol)
|
362
|
-
@symbol = symbol
|
363
|
-
@size = DATA_SIZES[symbol]
|
364
|
-
@rl_be, @sl_be = DATA_ENDIAN[true][symbol]
|
365
|
-
@rl_le, @sl_le = DATA_ENDIAN[false][symbol]
|
366
|
-
end
|
367
|
-
|
368
|
-
def self.load(input, input_big = LibBin::default_big?, _ = nil, _ = nil, length = nil)
|
369
|
-
l = (length ? length : 1)
|
370
|
-
str = input.read(@size*l)
|
371
|
-
input_big ? @rl_be[str, length] : @rl_le[str, length]
|
372
|
-
end
|
373
|
-
|
374
|
-
def self.dump(value, output, output_big = LibBin::default_big?, _ = nil, _ = nil, length = nil)
|
375
|
-
str = (output_big ? @sl_be[value, length] : @sl_le[value, length])
|
376
|
-
output.write(str)
|
377
|
-
end
|
378
|
-
|
379
|
-
def self.convert(input, output, input_big = LibBin::default_big?, output_big = !input_big, _ = nil, _ = nil, length = nil)
|
380
|
-
l = (length ? length : 1)
|
381
|
-
str = input.read(@size*l)
|
382
|
-
value = (input_big ? @rl_be[str, length] : @rl_le[str, length])
|
383
|
-
str = (output_big ? @sl_be[value, length] : @sl_le[value, length])
|
384
|
-
output.write(str)
|
385
|
-
value
|
547
|
+
# Unused bits of underlying value
|
548
|
+
attr_accessor :__remainder
|
549
|
+
|
550
|
+
# set only the unused bits of the underlying value
|
551
|
+
def __remainder=(v) # only keep relevant bits of the remainder
|
552
|
+
if v != 0
|
553
|
+
num_bits = self.class.type.size * 8
|
554
|
+
num_used_bits = self.class.map.value.collect { |v, _, _| v }.select { |v| v > 0 }.sum(:+)
|
555
|
+
if num_used_bits < num_bits
|
556
|
+
v &= ((( 1 << (num_bits - num_used_bits)) - 1) << num_used_bits)
|
557
|
+
else
|
558
|
+
v = 0
|
559
|
+
end
|
560
|
+
end
|
561
|
+
@__remainder = v
|
386
562
|
end
|
387
563
|
|
388
|
-
|
564
|
+
# @!visibility private
|
565
|
+
def initialize
|
566
|
+
@__remainder = 0
|
567
|
+
end
|
568
|
+
|
569
|
+
# @!visibility private
|
570
|
+
def __set_value(val)
|
571
|
+
base = 0
|
572
|
+
self.class.map.each { |k, (v, _, s)|
|
573
|
+
next if v <= 0
|
574
|
+
tmp = (val >> base) & ((1 << v) - 1)
|
575
|
+
val ^= tmp << base #remove bits from val
|
576
|
+
tmp -= (1 << v) if self.class.signed && tmp >= (1 << (v - 1))
|
577
|
+
send(s, tmp)
|
578
|
+
base += v
|
579
|
+
}
|
580
|
+
@__remainder = val
|
581
|
+
end
|
582
|
+
|
583
|
+
# @!visibility private
|
584
|
+
def __get_value
|
585
|
+
base = 0
|
586
|
+
val = 0
|
587
|
+
self.class.map.each { |k, (v, g, _)|
|
588
|
+
next if v <= 0
|
589
|
+
val |= ((send(g) & ((1 << v) - 1)) << base)
|
590
|
+
base += v
|
591
|
+
}
|
592
|
+
val | @__remainder
|
593
|
+
end
|
594
|
+
|
595
|
+
# Returns the underlying type +always_align+ property
|
596
|
+
def self.always_align
|
597
|
+
@type.always_align
|
598
|
+
end
|
389
599
|
|
390
|
-
|
600
|
+
# Returns the underlying type +align+ property
|
601
|
+
def self.align
|
602
|
+
@type.align
|
603
|
+
end
|
391
604
|
|
392
|
-
|
393
|
-
|
605
|
+
# Forwards the call to the underlying type
|
606
|
+
def self.size(value = nil, previous_offset = 0, parent = nil, index = nil, length = nil)
|
607
|
+
@type.size(value, previous_offset, parent, index, length)
|
394
608
|
end
|
395
609
|
|
396
|
-
|
397
|
-
|
610
|
+
# Forwards the call to the underlying type
|
611
|
+
def self.shape(value = nil, previous_offset = 0, parent = nil, index = nil, kind = DataShape, length = nil)
|
612
|
+
@type.shape(value, previous_offset, parent, index, kind, length)
|
398
613
|
end
|
399
614
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
615
|
+
# Load a {Bitfield} from +input+, and return it.
|
616
|
+
# @param input [IO] the stream to load the field from.
|
617
|
+
# @param input_big [Boolean] the endianness of +input+
|
618
|
+
# @param parent [Structure] ignored.
|
619
|
+
# @param index [Integer] ignored.
|
620
|
+
# @param length [Integer] if given the length of the vector. Else
|
621
|
+
# the length is considered to be 1.
|
622
|
+
# @return [Bitfield,Array<Bitfield>] an instance of self, or an
|
623
|
+
# Array if length was specified
|
624
|
+
def self.load(input, input_big = LibBin::default_big?, parent = nil, index = nil, length = nil)
|
625
|
+
v = @type.load(input, input_big, parent, index, length)
|
626
|
+
if length
|
627
|
+
v.collect { |val|
|
628
|
+
bf = self.new
|
629
|
+
bf.__set_value(val)
|
630
|
+
bf
|
631
|
+
}
|
632
|
+
else
|
633
|
+
bf = self.new
|
634
|
+
bf.__set_value(v)
|
635
|
+
bf
|
636
|
+
end
|
404
637
|
end
|
405
638
|
|
406
|
-
|
639
|
+
# Convert a {Bitfield} by loading it from +input+ and
|
640
|
+
# dumping it to +output+. Returns the loaded field.
|
641
|
+
# @param input [IO] the stream to load the field from.
|
642
|
+
# @param output [IO] the stream to dump the field to.
|
643
|
+
# @param input_big [Boolean] the endianness of +input+
|
644
|
+
# @param output_big [Boolean] the endianness of +output+.
|
645
|
+
# @param parent [Structure] ignored.
|
646
|
+
# @param index [Integer] ignored.
|
647
|
+
# @param length [Integer] if given the length of the vector. Else
|
648
|
+
# the length is considered to be 1.
|
649
|
+
# @return [Bitfield,Array<Bitfield>] an instance of self, or an
|
650
|
+
# Array if length was specified
|
651
|
+
def self.convert(input, output, input_big = LibBin::default_big?, output_big = !input_big, parent = nil, index = nil, length = nil)
|
652
|
+
v = @type.convert(input, output, input_big, output_big, parent, index, length)
|
407
653
|
if length
|
408
|
-
|
654
|
+
v.collect { |val|
|
655
|
+
bf = self.new
|
656
|
+
bf.__set_value(val)
|
657
|
+
bf
|
658
|
+
}
|
409
659
|
else
|
410
|
-
|
660
|
+
bf = self.new
|
661
|
+
bf.__set_value(v)
|
662
|
+
bf
|
411
663
|
end
|
412
664
|
end
|
413
665
|
|
414
|
-
|
415
|
-
|
666
|
+
# Dump a {Bitfield} to +output+.
|
667
|
+
# @param value [Bitfield, Array<Bitfield>] the Ruby representation
|
668
|
+
# of the type, or the Array representation of the vector if
|
669
|
+
# +length+ is specified.
|
670
|
+
# @param output [IO] the stream to dump the field to.
|
671
|
+
# @param output_big [Boolean] the endianness of +output+.
|
672
|
+
# @param parent [Structure] ignored.
|
673
|
+
# @param index [Integer] ignored.
|
674
|
+
# @param length [Integer] if given the length of the vector. Else
|
675
|
+
# the length is considered to be 1.
|
676
|
+
# @return [nil]
|
677
|
+
def self.dump(value, output, output_big = LibBin::default_big?, parent = nil, index = nil, length = nil)
|
678
|
+
v =
|
679
|
+
if length
|
680
|
+
length.times.collect { |i|
|
681
|
+
value[i].__get_value
|
682
|
+
}
|
683
|
+
else
|
684
|
+
value.__get_value
|
685
|
+
end
|
686
|
+
@type.dump(v, output, output_big, parent, index, length)
|
416
687
|
end
|
417
688
|
end
|
418
689
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
690
|
+
# Create a new field of a type inheriting from {Bitfield} and name +name+.
|
691
|
+
# See {field} for more options
|
692
|
+
# @param name [Symbol,String] name of the field.
|
693
|
+
# @param map [Hash{Symbol => Integer}] bitfield field names and number of bits.
|
694
|
+
# @param size [Integer] size in bits of the underlying integer
|
695
|
+
# @param signed [Boolean] signedness of the underlying type
|
696
|
+
# @return self
|
697
|
+
def self.bitfield(name, map, size: 32, signed: false, length: nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false, align: false, expect: nil)
|
698
|
+
klass = Class.new(Bitfield) do |c|
|
699
|
+
c.set_type_size(size, signed)
|
700
|
+
c.set_map(map)
|
701
|
+
end
|
702
|
+
if klass.always_align
|
703
|
+
al = klass.align
|
704
|
+
if align.kind_of?(Integer)
|
705
|
+
align = align >= al ? align : al
|
423
706
|
else
|
424
|
-
|
707
|
+
align = al
|
425
708
|
end
|
709
|
+
end
|
710
|
+
if align == true
|
711
|
+
align = klass.align
|
426
712
|
else
|
427
|
-
|
713
|
+
raise "alignement must be a power of 2" if align && (align - 1) & align != 0
|
428
714
|
end
|
429
|
-
@fields.push(Field::new(
|
430
|
-
attr_accessor
|
431
|
-
|
432
|
-
|
433
|
-
def self.create_scalar_type(symbol)
|
434
|
-
klassname, name = SCALAR_TYPES[symbol]
|
435
|
-
eval <<EOF
|
436
|
-
class #{klassname} < Scalar
|
437
|
-
init(#{symbol.inspect})
|
438
|
-
end
|
439
|
-
|
440
|
-
def self.#{name}(field, length: nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false)
|
441
|
-
@fields.push(Field::new(field, #{klassname}, length, count, offset, sequence, condition, relative_offset))
|
442
|
-
attr_accessor field
|
443
|
-
end
|
444
|
-
EOF
|
445
|
-
end
|
446
|
-
|
447
|
-
create_scalar_type(:c)
|
448
|
-
create_scalar_type(:C)
|
449
|
-
create_scalar_type(:s)
|
450
|
-
create_scalar_type(:"s<")
|
451
|
-
create_scalar_type(:"s>")
|
452
|
-
create_scalar_type(:S)
|
453
|
-
create_scalar_type(:"S<")
|
454
|
-
create_scalar_type(:"S>")
|
455
|
-
create_scalar_type(:l)
|
456
|
-
create_scalar_type(:"l<")
|
457
|
-
create_scalar_type(:"l>")
|
458
|
-
create_scalar_type(:L)
|
459
|
-
create_scalar_type(:"L<")
|
460
|
-
create_scalar_type(:"L>")
|
461
|
-
create_scalar_type(:q)
|
462
|
-
create_scalar_type(:"q<")
|
463
|
-
create_scalar_type(:"q>")
|
464
|
-
create_scalar_type(:Q)
|
465
|
-
create_scalar_type(:"Q<")
|
466
|
-
create_scalar_type(:"Q>")
|
467
|
-
create_scalar_type(:F)
|
468
|
-
create_scalar_type(:e)
|
469
|
-
create_scalar_type(:g)
|
470
|
-
create_scalar_type(:D)
|
471
|
-
create_scalar_type(:E)
|
472
|
-
create_scalar_type(:G)
|
473
|
-
create_scalar_type(:half)
|
474
|
-
create_scalar_type(:half_le)
|
475
|
-
create_scalar_type(:half_be)
|
476
|
-
create_scalar_type(:pghalf)
|
477
|
-
create_scalar_type(:pghalf_le)
|
478
|
-
create_scalar_type(:pghalf_be)
|
479
|
-
|
480
|
-
def self.string( field, length = nil, count: nil, offset: nil, sequence: false, condition: nil, relative_offset: false)
|
481
|
-
sym = (length ? :"a" : :"a*")
|
482
|
-
c = Class::new(Str) do
|
483
|
-
init(sym)
|
484
|
-
end
|
485
|
-
@fields.push(Field::new(field, c, length, count, offset, sequence, condition, relative_offset))
|
486
|
-
attr_accessor field
|
715
|
+
@fields.push(Field::new(name, klass, length, count, offset, sequence, condition, relative_offset, align, expect))
|
716
|
+
attr_accessor name
|
717
|
+
self
|
487
718
|
end
|
488
719
|
|
489
720
|
end
|