bindata 1.8.2 → 1.8.3
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.
- data/ChangeLog.rdoc +8 -0
- data/examples/gzip.rb +2 -2
- data/lib/bindata/base.rb +5 -4
- data/lib/bindata/bits.rb +84 -15
- data/lib/bindata/buffer.rb +1 -1
- data/lib/bindata/choice.rb +8 -24
- data/lib/bindata/deprecated.rb +9 -3
- data/lib/bindata/int.rb +2 -10
- data/lib/bindata/io.rb +13 -4
- data/lib/bindata/lazy.rb +1 -1
- data/lib/bindata/record.rb +1 -19
- data/lib/bindata/sanitize.rb +20 -0
- data/lib/bindata/string.rb +5 -4
- data/lib/bindata/struct.rb +81 -20
- data/lib/bindata/version.rb +1 -1
- data/test/array_test.rb +3 -3
- data/test/bits_test.rb +38 -34
- data/test/io_test.rb +15 -0
- data/test/lazy_test.rb +11 -3
- data/test/record_test.rb +12 -6
- data/test/struct_test.rb +40 -9
- data/test/system_test.rb +1 -1
- metadata +4 -4
data/ChangeLog.rdoc
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
= BinData Changelog
|
2
2
|
|
3
|
+
== Version 1.8.3 (2014-04-16)
|
4
|
+
|
5
|
+
* #eval_parameters can now call private methods. Requested by Ole Rasmussen.
|
6
|
+
* Can now determine state of :onlyif fields. Requested by Ole Rasmussen.
|
7
|
+
* Renamed #offset to #abs_offset for clarity. #offset is now deprecated.
|
8
|
+
* Support :byte_align for fields in structs. Requested by Igor Yamolov.
|
9
|
+
* Added bit fields with dynamic length. Requested by Jacob Dam.
|
10
|
+
|
3
11
|
== Version 1.8.2 (2014-02-02)
|
4
12
|
|
5
13
|
* Virtual fields can now have values.
|
data/examples/gzip.rb
CHANGED
@@ -59,7 +59,7 @@ class Gzip
|
|
59
59
|
|
60
60
|
attr_accessor :compressed
|
61
61
|
def_delegators :@header, :file_name=, :file_name
|
62
|
-
def_delegators :@header, :comment=, :comment
|
62
|
+
def_delegators :@header, :comment=, :comment, :comment?
|
63
63
|
def_delegators :@header, :compression_method
|
64
64
|
def_delegators :@footer, :crc32, :uncompressed_size
|
65
65
|
|
@@ -153,7 +153,7 @@ if __FILE__ == $0
|
|
153
153
|
g.uncompressed_size,
|
154
154
|
ratio,
|
155
155
|
g.file_name]
|
156
|
-
puts "Comment: #{g.comment}" if g.comment
|
156
|
+
puts "Comment: #{g.comment}" if g.comment?
|
157
157
|
puts
|
158
158
|
|
159
159
|
puts "Executing gzip -l -v"
|
data/lib/bindata/base.rb
CHANGED
@@ -233,16 +233,17 @@ module BinData
|
|
233
233
|
end
|
234
234
|
end
|
235
235
|
|
236
|
-
# Returns the offset of this object
|
237
|
-
|
236
|
+
# Returns the offset (in bytes) of this object with respect to its most
|
237
|
+
# distant ancestor.
|
238
|
+
def abs_offset
|
238
239
|
if @parent
|
239
|
-
@parent.
|
240
|
+
@parent.abs_offset + @parent.offset_of(self)
|
240
241
|
else
|
241
242
|
0
|
242
243
|
end
|
243
244
|
end
|
244
245
|
|
245
|
-
# Returns the offset of this object
|
246
|
+
# Returns the offset (in bytes) of this object with respect to its parent.
|
246
247
|
def rel_offset
|
247
248
|
if @parent
|
248
249
|
@parent.offset_of(self)
|
data/lib/bindata/bits.rb
CHANGED
@@ -6,13 +6,11 @@ module BinData
|
|
6
6
|
|
7
7
|
module BitField #:nodoc: all
|
8
8
|
class << self
|
9
|
-
def define_class(nbits, endian, signed = :unsigned)
|
10
|
-
name = ((signed == :signed ) ? "Sbit" : "Bit") + nbits.to_s
|
11
|
-
name << "le" if endian == :little
|
9
|
+
def define_class(name, nbits, endian, signed = :unsigned)
|
12
10
|
unless BinData.const_defined?(name)
|
13
11
|
BinData.module_eval <<-END
|
14
12
|
class #{name} < BinData::BasePrimitive
|
15
|
-
BitField.define_methods(self, #{nbits},
|
13
|
+
BitField.define_methods(self, #{nbits.inspect}, #{endian.inspect}, #{signed.inspect})
|
16
14
|
end
|
17
15
|
END
|
18
16
|
end
|
@@ -22,27 +20,34 @@ module BinData
|
|
22
20
|
|
23
21
|
def define_methods(bit_class, nbits, endian, signed)
|
24
22
|
bit_class.module_eval <<-END
|
23
|
+
#{create_params_code(nbits)}
|
24
|
+
|
25
25
|
def assign(val)
|
26
|
+
#{create_nbits_code(nbits)}
|
26
27
|
#{create_clamp_code(nbits, signed)}
|
27
28
|
super(val)
|
28
29
|
end
|
29
30
|
|
30
31
|
def do_write(io)
|
32
|
+
#{create_nbits_code(nbits)}
|
31
33
|
val = _value
|
32
|
-
#{create_int2uint_code(nbits
|
34
|
+
#{create_int2uint_code(nbits, signed)}
|
33
35
|
io.writebits(val, #{nbits}, :#{endian})
|
34
36
|
end
|
35
37
|
|
36
38
|
def do_num_bytes
|
37
|
-
#{nbits
|
39
|
+
#{create_nbits_code(nbits)}
|
40
|
+
#{create_do_num_bytes_code(nbits)}
|
38
41
|
end
|
39
42
|
|
40
43
|
#---------------
|
41
44
|
private
|
42
45
|
|
46
|
+
|
43
47
|
def read_and_return_value(io)
|
48
|
+
#{create_nbits_code(nbits)}
|
44
49
|
val = io.readbits(#{nbits}, :#{endian})
|
45
|
-
#{create_uint2int_code(nbits
|
50
|
+
#{create_uint2int_code(nbits, signed)}
|
46
51
|
val
|
47
52
|
end
|
48
53
|
|
@@ -52,7 +57,51 @@ module BinData
|
|
52
57
|
END
|
53
58
|
end
|
54
59
|
|
60
|
+
def create_params_code(nbits)
|
61
|
+
if nbits == :nbits
|
62
|
+
"mandatory_parameter :nbits"
|
63
|
+
else
|
64
|
+
""
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_nbits_code(nbits)
|
69
|
+
if nbits == :nbits
|
70
|
+
"nbits = eval_parameter(:nbits)"
|
71
|
+
else
|
72
|
+
""
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_do_num_bytes_code(nbits)
|
77
|
+
if nbits == :nbits
|
78
|
+
"nbits / 8.0"
|
79
|
+
else
|
80
|
+
nbits / 8.0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
55
84
|
def create_clamp_code(nbits, signed)
|
85
|
+
if nbits == :nbits
|
86
|
+
create_dynamic_clamp_code(nbits, signed)
|
87
|
+
else
|
88
|
+
create_fixed_clamp_code(nbits, signed)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_dynamic_clamp_code(nbits, signed)
|
93
|
+
if signed == :signed
|
94
|
+
max = "max = (1 << (nbits - 1)) - 1"
|
95
|
+
min = "min = -(max + 1)"
|
96
|
+
else
|
97
|
+
max = "max = (1 << nbits) - 1"
|
98
|
+
min = "min = 0"
|
99
|
+
end
|
100
|
+
|
101
|
+
"#{max}; #{min}; val = (val < min) ? min : (val > max) ? max : val"
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_fixed_clamp_code(nbits, signed)
|
56
105
|
if nbits == 1 and signed == :signed
|
57
106
|
raise "signed bitfield must have more than one bit"
|
58
107
|
end
|
@@ -75,30 +124,50 @@ module BinData
|
|
75
124
|
"val = #{clamp}"
|
76
125
|
end
|
77
126
|
|
78
|
-
def create_int2uint_code(nbits)
|
79
|
-
|
127
|
+
def create_int2uint_code(nbits, signed)
|
128
|
+
if signed != :signed
|
129
|
+
""
|
130
|
+
elsif nbits == :nbits
|
131
|
+
"val &= (1 << nbits) - 1"
|
132
|
+
else
|
133
|
+
"val &= #{(1 << nbits) - 1}"
|
134
|
+
end
|
80
135
|
end
|
81
136
|
|
82
|
-
def create_uint2int_code(nbits)
|
83
|
-
|
137
|
+
def create_uint2int_code(nbits, signed)
|
138
|
+
if signed != :signed
|
139
|
+
""
|
140
|
+
elsif nbits == :nbits
|
141
|
+
"val -= (1 << nbits) if (val >= (1 << (nbits - 1)))"
|
142
|
+
else
|
143
|
+
"val -= #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
|
144
|
+
end
|
84
145
|
end
|
85
146
|
end
|
86
147
|
end
|
87
148
|
|
149
|
+
# Create classes for dynamic bitfields
|
150
|
+
{
|
151
|
+
"Bit" => :big,
|
152
|
+
"BitLe" => :little,
|
153
|
+
"Sbit" => [:big, :signed],
|
154
|
+
"SbitLe" => [:little, :signed],
|
155
|
+
}.each_pair { |name, args| BitField.define_class(name, :nbits, *args) }
|
156
|
+
|
88
157
|
# Create classes on demand
|
89
158
|
module BitFieldFactory
|
90
159
|
def const_missing(name)
|
91
160
|
mappings = {
|
92
|
-
/^Bit(\d+)$/
|
93
|
-
/^Bit(\d+)le$/
|
94
|
-
/^Sbit(\d+)$/
|
161
|
+
/^Bit(\d+)$/ => :big,
|
162
|
+
/^Bit(\d+)le$/ => :little,
|
163
|
+
/^Sbit(\d+)$/ => [:big, :signed],
|
95
164
|
/^Sbit(\d+)le$/ => [:little, :signed]
|
96
165
|
}
|
97
166
|
|
98
167
|
mappings.each_pair do |regex, args|
|
99
168
|
if regex =~ name.to_s
|
100
169
|
nbits = $1.to_i
|
101
|
-
return BitField.define_class(nbits, *args)
|
170
|
+
return BitField.define_class(name, nbits, *args)
|
102
171
|
end
|
103
172
|
end
|
104
173
|
|
data/lib/bindata/buffer.rb
CHANGED
data/lib/bindata/choice.rb
CHANGED
@@ -125,40 +125,24 @@ module BinData
|
|
125
125
|
selection
|
126
126
|
end
|
127
127
|
|
128
|
-
def
|
129
|
-
|
130
|
-
end
|
131
|
-
|
132
|
-
def assign(val)
|
133
|
-
current_choice.assign(val)
|
134
|
-
end
|
135
|
-
|
136
|
-
def snapshot
|
137
|
-
current_choice.snapshot
|
128
|
+
def safe_respond_to?(symbol, include_private = false) #:nodoc:
|
129
|
+
base_respond_to?(symbol, include_private)
|
138
130
|
end
|
139
131
|
|
140
132
|
def respond_to?(symbol, include_private = false) #:nodoc:
|
141
133
|
current_choice.respond_to?(symbol, include_private) || super
|
142
134
|
end
|
143
135
|
|
144
|
-
def safe_respond_to?(symbol, include_private = false) #:nodoc:
|
145
|
-
base_respond_to?(symbol, include_private)
|
146
|
-
end
|
147
|
-
|
148
136
|
def method_missing(symbol, *args, &block) #:nodoc:
|
149
137
|
current_choice.__send__(symbol, *args, &block)
|
150
138
|
end
|
151
139
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
159
|
-
|
160
|
-
def do_num_bytes #:nodoc:
|
161
|
-
current_choice.do_num_bytes
|
140
|
+
%w(clear? assign snapshot do_read do_write do_num_bytes).each do |m|
|
141
|
+
self.module_eval <<-END
|
142
|
+
def #{m}(*args)
|
143
|
+
current_choice.#{m}(*args)
|
144
|
+
end
|
145
|
+
END
|
162
146
|
end
|
163
147
|
|
164
148
|
#---------------
|
data/lib/bindata/deprecated.rb
CHANGED
@@ -20,12 +20,12 @@ module BinData
|
|
20
20
|
|
21
21
|
# Don't override initialize. If you are defining a new kind of datatype
|
22
22
|
# (list, array, choice etc) then put your initialization code in
|
23
|
-
# #initialize_instance.
|
24
|
-
#
|
23
|
+
# #initialize_instance. BinData objects might be initialized as prototypes
|
24
|
+
# and your initialization code may not be called.
|
25
25
|
#
|
26
26
|
# If you're subclassing BinData::Record, you are definitely doing the wrong
|
27
27
|
# thing. Read the documentation on how to use BinData.
|
28
|
-
# http://
|
28
|
+
# http://github.com/dmendel/bindata/wiki/Records
|
29
29
|
alias_method :initialize_without_warning, :initialize
|
30
30
|
def initialize_with_warning(*args)
|
31
31
|
owner = method(:initialize).owner
|
@@ -46,5 +46,11 @@ module BinData
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
# #offset has been renamed to #abs_offset.
|
50
|
+
# Eventually #rel_offset will be renamed to #offset.
|
51
|
+
def offset
|
52
|
+
warn "#offset is deprecated in #{debug_name}. Use #abs_offset instead"
|
53
|
+
abs_offset
|
54
|
+
end
|
49
55
|
end
|
50
56
|
end
|
data/lib/bindata/int.rb
CHANGED
@@ -6,8 +6,7 @@ module BinData
|
|
6
6
|
|
7
7
|
module Int #:nodoc: all
|
8
8
|
class << self
|
9
|
-
def define_class(nbits, endian, signed)
|
10
|
-
name = class_name(nbits, endian, signed)
|
9
|
+
def define_class(name, nbits, endian, signed)
|
11
10
|
unless BinData.const_defined?(name)
|
12
11
|
BinData.module_eval <<-END
|
13
12
|
class #{name} < BinData::BasePrimitive
|
@@ -19,13 +18,6 @@ module BinData
|
|
19
18
|
BinData.const_get(name)
|
20
19
|
end
|
21
20
|
|
22
|
-
def class_name(nbits, endian, signed)
|
23
|
-
endian_str = (endian == :big) ? "be" : "le"
|
24
|
-
base = (signed == :signed) ? "Int" : "Uint"
|
25
|
-
|
26
|
-
"#{base}#{nbits}#{endian_str}"
|
27
|
-
end
|
28
|
-
|
29
21
|
def define_methods(int_class, nbits, endian, signed)
|
30
22
|
raise "nbits must be divisible by 8" unless (nbits % 8).zero?
|
31
23
|
|
@@ -170,7 +162,7 @@ module BinData
|
|
170
162
|
if regex =~ name.to_s
|
171
163
|
nbits = $1.to_i
|
172
164
|
if (nbits % 8).zero?
|
173
|
-
return Int.define_class(nbits, *args)
|
165
|
+
return Int.define_class(name, nbits, *args)
|
174
166
|
end
|
175
167
|
end
|
176
168
|
end
|
data/lib/bindata/io.rb
CHANGED
@@ -190,8 +190,8 @@ module BinData
|
|
190
190
|
|
191
191
|
# Use #seek and #pos on seekable streams
|
192
192
|
module SeekableStream
|
193
|
-
# Returns the current offset of the io stream.
|
194
|
-
#
|
193
|
+
# Returns the current offset of the io stream. Offset will be rounded
|
194
|
+
# up when reading bitfields.
|
195
195
|
def offset
|
196
196
|
raw_io.pos - @initial_pos
|
197
197
|
end
|
@@ -225,8 +225,8 @@ module BinData
|
|
225
225
|
|
226
226
|
# Manually keep track of offset for unseekable streams.
|
227
227
|
module UnSeekableStream
|
228
|
-
# Returns the current offset of the io stream.
|
229
|
-
#
|
228
|
+
# Returns the current offset of the io stream. Offset will be rounded
|
229
|
+
# up when reading bitfields.
|
230
230
|
def offset
|
231
231
|
@read_count ||= 0
|
232
232
|
end
|
@@ -284,6 +284,8 @@ module BinData
|
|
284
284
|
@wval = 0
|
285
285
|
@wendian = nil
|
286
286
|
|
287
|
+
@write_count = 0
|
288
|
+
|
287
289
|
@bytes_remaining = nil
|
288
290
|
end
|
289
291
|
|
@@ -307,6 +309,12 @@ module BinData
|
|
307
309
|
end
|
308
310
|
end
|
309
311
|
|
312
|
+
# Returns the current offset of the io stream. Offset will be rounded
|
313
|
+
# up when writing bitfields.
|
314
|
+
def offset
|
315
|
+
@write_count + (@wnbits > 0 ? 1 : 0)
|
316
|
+
end
|
317
|
+
|
310
318
|
# Writes the given string of bytes to the io stream.
|
311
319
|
def writebytes(str)
|
312
320
|
flushbits
|
@@ -394,6 +402,7 @@ module BinData
|
|
394
402
|
@bytes_remaining -= data.size
|
395
403
|
end
|
396
404
|
|
405
|
+
@write_count += data.size
|
397
406
|
@raw_io.write(data)
|
398
407
|
end
|
399
408
|
|
data/lib/bindata/lazy.rb
CHANGED
data/lib/bindata/record.rb
CHANGED
@@ -1,28 +1,10 @@
|
|
1
1
|
require 'bindata/dsl'
|
2
|
-
require 'bindata/sanitize'
|
3
2
|
require 'bindata/struct'
|
4
3
|
|
5
4
|
module BinData
|
6
5
|
# A Record is a declarative wrapper around Struct.
|
7
6
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# class SomeDataType < BinData::Record
|
11
|
-
# hide :a
|
12
|
-
#
|
13
|
-
# int32le :a
|
14
|
-
# int16le :b
|
15
|
-
# struct :s do
|
16
|
-
# int8 :x
|
17
|
-
# int8 :y
|
18
|
-
# int8 :z
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# obj = SomeDataType.new
|
23
|
-
# obj.field_names =># ["b", "s"]
|
24
|
-
# obj.s.field_names =># ["x", "y", "z"]
|
25
|
-
#
|
7
|
+
# See +Struct+ for more info.
|
26
8
|
class Record < BinData::Struct
|
27
9
|
include DSLMixin
|
28
10
|
|
data/lib/bindata/sanitize.rb
CHANGED
@@ -24,6 +24,14 @@ module BinData
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
def has_parameter?(param)
|
28
|
+
if @factory
|
29
|
+
@factory.has_parameter?(param)
|
30
|
+
else
|
31
|
+
@obj_params.has_parameter?(param)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
27
35
|
def instantiate(value = nil, parent = nil)
|
28
36
|
@factory ||= @obj_class.new(@obj_params)
|
29
37
|
|
@@ -48,6 +56,10 @@ module BinData
|
|
48
56
|
@name
|
49
57
|
end
|
50
58
|
|
59
|
+
def has_parameter?(param)
|
60
|
+
@prototype.has_parameter?(param)
|
61
|
+
end
|
62
|
+
|
51
63
|
def instantiate(value = nil, parent = nil)
|
52
64
|
@prototype.instantiate(value, parent)
|
53
65
|
end
|
@@ -83,6 +95,10 @@ module BinData
|
|
83
95
|
@fields.each(&block)
|
84
96
|
end
|
85
97
|
|
98
|
+
def each_with_index(&block)
|
99
|
+
@fields.each_with_index(&block)
|
100
|
+
end
|
101
|
+
|
86
102
|
def collect(&block)
|
87
103
|
@fields.collect(&block)
|
88
104
|
end
|
@@ -103,6 +119,10 @@ module BinData
|
|
103
119
|
@fields.all? { |f| f.name != nil }
|
104
120
|
end
|
105
121
|
|
122
|
+
def any_field_has_parameter?(parameter)
|
123
|
+
@fields.any? { |f| f.has_parameter?(parameter) }
|
124
|
+
end
|
125
|
+
|
106
126
|
def copy_fields(other)
|
107
127
|
@fields.concat(other.fields)
|
108
128
|
end
|
data/lib/bindata/string.rb
CHANGED
@@ -91,13 +91,14 @@ module BinData
|
|
91
91
|
|
92
92
|
def snapshot
|
93
93
|
# override to trim padding
|
94
|
-
|
95
|
-
|
94
|
+
snap = super
|
95
|
+
snap = clamp_to_length(snap)
|
96
96
|
|
97
97
|
if get_parameter(:trim_padding)
|
98
|
-
|
98
|
+
trim_padding(snap)
|
99
|
+
else
|
100
|
+
snap
|
99
101
|
end
|
100
|
-
result
|
101
102
|
end
|
102
103
|
|
103
104
|
#---------------
|
data/lib/bindata/struct.rb
CHANGED
@@ -3,7 +3,7 @@ require 'bindata/base'
|
|
3
3
|
module BinData
|
4
4
|
|
5
5
|
class Base
|
6
|
-
optional_parameter :onlyif # Used by Struct
|
6
|
+
optional_parameter :onlyif, :byte_align # Used by Struct
|
7
7
|
end
|
8
8
|
|
9
9
|
# A Struct is an ordered collection of named data objects.
|
@@ -47,9 +47,11 @@ module BinData
|
|
47
47
|
#
|
48
48
|
# Fields may have have extra parameters as listed below:
|
49
49
|
#
|
50
|
-
# [<tt>:onlyif</tt>]
|
51
|
-
#
|
52
|
-
#
|
50
|
+
# [<tt>:onlyif</tt>] Used to indicate a data object is optional.
|
51
|
+
# if +false+, this object will not be included in any
|
52
|
+
# calls to #read, #write, #num_bytes or #snapshot.
|
53
|
+
# [<tt>:byte_align</tt>] This field's rel_offset must be a multiple of
|
54
|
+
# <tt>:byte_align</tt>.
|
53
55
|
class Struct < BinData::Base
|
54
56
|
|
55
57
|
mandatory_parameter :fields
|
@@ -137,7 +139,9 @@ module BinData
|
|
137
139
|
end
|
138
140
|
|
139
141
|
def initialize_shared_instance
|
140
|
-
|
142
|
+
fields = get_parameter(:fields)
|
143
|
+
@field_names = fields.field_names.freeze
|
144
|
+
extend ByteAlignPlugin if fields.any_field_has_parameter?(:byte_align)
|
141
145
|
super
|
142
146
|
end
|
143
147
|
|
@@ -162,7 +166,7 @@ module BinData
|
|
162
166
|
snapshot = Snapshot.new
|
163
167
|
field_names.each do |name|
|
164
168
|
obj = find_obj_for_name(name)
|
165
|
-
snapshot[name] = obj.snapshot if include_obj(obj)
|
169
|
+
snapshot[name] = obj.snapshot if include_obj?(obj)
|
166
170
|
end
|
167
171
|
snapshot
|
168
172
|
end
|
@@ -182,7 +186,7 @@ module BinData
|
|
182
186
|
def respond_to?(symbol, include_private = false) #:nodoc:
|
183
187
|
@field_names.include?(base_field_name(symbol)) || super
|
184
188
|
end
|
185
|
-
|
189
|
+
|
186
190
|
def method_missing(symbol, *args, &block) #:nodoc:
|
187
191
|
obj = find_obj_for_name(symbol)
|
188
192
|
if obj
|
@@ -205,12 +209,12 @@ module BinData
|
|
205
209
|
|
206
210
|
def do_read(io) #:nodoc:
|
207
211
|
instantiate_all_objs
|
208
|
-
@field_objs.each { |f| f.do_read(io) if include_obj(f) }
|
212
|
+
@field_objs.each { |f| f.do_read(io) if include_obj?(f) }
|
209
213
|
end
|
210
214
|
|
211
215
|
def do_write(io) #:nodoc
|
212
216
|
instantiate_all_objs
|
213
|
-
@field_objs.each { |f| f.do_write(io) if include_obj(f) }
|
217
|
+
@field_objs.each { |f| f.do_write(io) if include_obj?(f) }
|
214
218
|
end
|
215
219
|
|
216
220
|
def do_num_bytes #:nodoc:
|
@@ -242,16 +246,15 @@ module BinData
|
|
242
246
|
#---------------
|
243
247
|
private
|
244
248
|
|
245
|
-
def base_field_name(name)
|
246
|
-
name.to_s.chomp("=").to_sym
|
247
|
-
end
|
248
|
-
|
249
249
|
def invoke_field(obj, symbol, args)
|
250
250
|
name = symbol.to_s
|
251
251
|
is_writer = (name[-1, 1] == "=")
|
252
|
+
is_query = (name[-1, 1] == "?")
|
252
253
|
|
253
254
|
if is_writer
|
254
255
|
obj.assign(*args)
|
256
|
+
elsif is_query
|
257
|
+
include_obj?(obj)
|
255
258
|
else
|
256
259
|
obj
|
257
260
|
end
|
@@ -271,6 +274,10 @@ module BinData
|
|
271
274
|
end
|
272
275
|
end
|
273
276
|
|
277
|
+
def base_field_name(name)
|
278
|
+
name.to_s.sub(/(=|\?)\z/, "").to_sym
|
279
|
+
end
|
280
|
+
|
274
281
|
def instantiate_all_objs
|
275
282
|
@field_names.each_index { |i| instantiate_obj_at(i) }
|
276
283
|
end
|
@@ -310,19 +317,18 @@ module BinData
|
|
310
317
|
end
|
311
318
|
|
312
319
|
def sum_num_bytes_below_index(index)
|
313
|
-
sum
|
314
|
-
(0...index).each do |i|
|
320
|
+
(0...index).inject(0) do |sum, i|
|
315
321
|
obj = @field_objs[i]
|
316
|
-
if include_obj(obj)
|
322
|
+
if include_obj?(obj)
|
317
323
|
nbytes = obj.do_num_bytes
|
318
|
-
|
324
|
+
(nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
|
325
|
+
else
|
326
|
+
sum
|
319
327
|
end
|
320
328
|
end
|
321
|
-
|
322
|
-
sum
|
323
329
|
end
|
324
330
|
|
325
|
-
def include_obj(obj)
|
331
|
+
def include_obj?(obj)
|
326
332
|
not obj.has_parameter?(:onlyif) or obj.eval_parameter(:onlyif)
|
327
333
|
end
|
328
334
|
|
@@ -381,4 +387,59 @@ module BinData
|
|
381
387
|
end
|
382
388
|
end
|
383
389
|
end
|
390
|
+
|
391
|
+
# Align fields to a multiple of :byte_align
|
392
|
+
module ByteAlignPlugin
|
393
|
+
def do_read(io)
|
394
|
+
initial_offset = io.offset
|
395
|
+
instantiate_all_objs
|
396
|
+
@field_objs.each do |f|
|
397
|
+
if include_obj?(f)
|
398
|
+
if align_obj?(f)
|
399
|
+
io.seekbytes(bytes_to_align(f, io.offset - initial_offset))
|
400
|
+
end
|
401
|
+
f.do_read(io)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def do_write(io)
|
407
|
+
initial_offset = io.offset
|
408
|
+
instantiate_all_objs
|
409
|
+
@field_objs.each do |f|
|
410
|
+
if include_obj?(f)
|
411
|
+
if align_obj?(f)
|
412
|
+
io.writebytes("\x00" * bytes_to_align(f, io.offset - initial_offset))
|
413
|
+
end
|
414
|
+
f.do_write(io)
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def sum_num_bytes_below_index(index)
|
420
|
+
sum = 0
|
421
|
+
(0...@field_objs.length).each do |i|
|
422
|
+
obj = @field_objs[i]
|
423
|
+
if include_obj?(obj)
|
424
|
+
sum = sum.ceil + bytes_to_align(obj, sum.ceil) if align_obj?(obj)
|
425
|
+
|
426
|
+
break if i >= index
|
427
|
+
|
428
|
+
nbytes = obj.do_num_bytes
|
429
|
+
sum = (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
sum
|
434
|
+
end
|
435
|
+
|
436
|
+
def bytes_to_align(obj, rel_offset)
|
437
|
+
align = obj.eval_parameter(:byte_align)
|
438
|
+
(align - (rel_offset % align)) % align
|
439
|
+
end
|
440
|
+
|
441
|
+
def align_obj?(obj)
|
442
|
+
obj.has_parameter?(:byte_align)
|
443
|
+
end
|
444
|
+
end
|
384
445
|
end
|
data/lib/bindata/version.rb
CHANGED
data/test/array_test.rb
CHANGED
@@ -168,7 +168,7 @@ describe BinData::Array, "with several elements" do
|
|
168
168
|
end
|
169
169
|
|
170
170
|
it "has correct offset" do
|
171
|
-
obj[2].
|
171
|
+
obj[2].rel_offset.must_equal ExampleSingle.new.num_bytes * 2
|
172
172
|
end
|
173
173
|
|
174
174
|
it "has correct num_bytes" do
|
@@ -364,8 +364,8 @@ describe BinData::Array, "of bits" do
|
|
364
364
|
end
|
365
365
|
|
366
366
|
it "has correct offset" do
|
367
|
-
obj[7].
|
368
|
-
obj[8].
|
367
|
+
obj[7].rel_offset.must_equal 0
|
368
|
+
obj[8].rel_offset.must_equal 1
|
369
369
|
end
|
370
370
|
end
|
371
371
|
|
data/test/bits_test.rb
CHANGED
@@ -5,66 +5,58 @@ require File.expand_path(File.join(File.dirname(__FILE__), "common"))
|
|
5
5
|
module AllBitfields
|
6
6
|
|
7
7
|
def test_has_a_sensible_value_of_zero
|
8
|
-
|
9
|
-
|
8
|
+
all_objects do |obj, nbits|
|
9
|
+
obj.must_equal 0
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_avoids_underflow
|
14
|
-
|
15
|
-
obj = bit_class.new
|
16
|
-
|
14
|
+
all_objects do |obj, nbits|
|
17
15
|
obj.assign(min_value - 1)
|
18
16
|
obj.must_equal min_value
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
22
20
|
def test_avoids_overflow
|
23
|
-
|
24
|
-
obj = bit_class.new
|
25
|
-
|
21
|
+
all_objects do |obj, nbits|
|
26
22
|
obj.assign(max_value + 1)
|
27
23
|
obj.must_equal max_value
|
28
24
|
end
|
29
25
|
end
|
30
26
|
|
31
27
|
def test_assign_values
|
32
|
-
|
28
|
+
all_objects do |obj, nbits|
|
33
29
|
some_values_within_range.each do |val|
|
34
|
-
obj = bit_class.new
|
35
30
|
obj.assign(val)
|
36
|
-
|
37
31
|
obj.must_equal val
|
38
32
|
end
|
39
33
|
end
|
40
34
|
end
|
41
35
|
|
42
36
|
def test_assign_values_from_other_bit_objects
|
43
|
-
|
37
|
+
all_objects do |obj, nbits|
|
44
38
|
some_values_within_range.each do |val|
|
45
|
-
obj
|
46
|
-
obj.assign(bit_class.new(val))
|
47
|
-
|
39
|
+
obj.assign(obj.new(val))
|
48
40
|
obj.must_equal val
|
49
41
|
end
|
50
42
|
end
|
51
43
|
end
|
52
44
|
|
53
45
|
def test_symmetrically_read_and_write
|
54
|
-
|
46
|
+
all_objects do |obj, nbits|
|
55
47
|
some_values_within_range.each do |val|
|
56
|
-
obj = bit_class.new
|
57
48
|
obj.assign(val)
|
58
|
-
|
59
|
-
obj.
|
49
|
+
other = obj.new
|
50
|
+
other.read(obj.to_binary_s)
|
51
|
+
other.must_equal obj
|
60
52
|
end
|
61
53
|
end
|
62
54
|
end
|
63
55
|
|
64
|
-
def
|
65
|
-
@bits.
|
56
|
+
def all_objects(&block)
|
57
|
+
@bits.each do |obj, nbits|
|
66
58
|
@nbits = nbits
|
67
|
-
yield
|
59
|
+
yield obj, nbits
|
68
60
|
end
|
69
61
|
end
|
70
62
|
|
@@ -98,7 +90,7 @@ module AllBitfields
|
|
98
90
|
end
|
99
91
|
|
100
92
|
def generate_bit_classes_to_test(endian, signed)
|
101
|
-
bits =
|
93
|
+
bits = []
|
102
94
|
if signed
|
103
95
|
base = "Sbit"
|
104
96
|
start = 2
|
@@ -107,12 +99,20 @@ def generate_bit_classes_to_test(endian, signed)
|
|
107
99
|
start = 1
|
108
100
|
end
|
109
101
|
|
110
|
-
(start ..
|
102
|
+
(start .. 64).each do |nbits|
|
111
103
|
name = "#{base}#{nbits}"
|
112
104
|
name << "le" if endian == :little
|
113
|
-
|
114
|
-
bits[
|
105
|
+
obj = BinData.const_get(name).new
|
106
|
+
bits << [obj, nbits]
|
107
|
+
end
|
108
|
+
|
109
|
+
(start .. 64).each do |nbits|
|
110
|
+
name = "#{base}"
|
111
|
+
name << "Le" if endian == :little
|
112
|
+
obj = BinData.const_get(name).new(:nbits => nbits)
|
113
|
+
bits << [obj, nbits]
|
115
114
|
end
|
115
|
+
|
116
116
|
bits
|
117
117
|
end
|
118
118
|
|
@@ -125,11 +125,12 @@ describe "Unsigned big endian bitfields" do
|
|
125
125
|
end
|
126
126
|
|
127
127
|
it "read big endian values" do
|
128
|
-
|
128
|
+
all_objects do |obj, nbits|
|
129
129
|
nbytes = (nbits + 7) / 8
|
130
130
|
str = [0b1000_0000].pack("C") + "\000" * (nbytes - 1)
|
131
131
|
|
132
|
-
|
132
|
+
obj.read(str)
|
133
|
+
obj.must_equal 1 << (nbits - 1)
|
133
134
|
end
|
134
135
|
end
|
135
136
|
end
|
@@ -143,11 +144,12 @@ describe "Signed big endian bitfields" do
|
|
143
144
|
end
|
144
145
|
|
145
146
|
it "read big endian values" do
|
146
|
-
|
147
|
+
all_objects do |obj, nbits|
|
147
148
|
nbytes = (nbits + 7) / 8
|
148
149
|
str = [0b0100_0000].pack("C") + "\000" * (nbytes - 1)
|
149
150
|
|
150
|
-
|
151
|
+
obj.read(str)
|
152
|
+
obj.must_equal 1 << (nbits - 2)
|
151
153
|
end
|
152
154
|
end
|
153
155
|
end
|
@@ -161,11 +163,12 @@ describe "Unsigned little endian bitfields" do
|
|
161
163
|
end
|
162
164
|
|
163
165
|
it "read little endian values" do
|
164
|
-
|
166
|
+
all_objects do |obj, nbits|
|
165
167
|
nbytes = (nbits + 7) / 8
|
166
168
|
str = [0b0000_0001].pack("C") + "\000" * (nbytes - 1)
|
167
169
|
|
168
|
-
|
170
|
+
obj.read(str)
|
171
|
+
obj.must_equal 1
|
169
172
|
end
|
170
173
|
end
|
171
174
|
end
|
@@ -179,11 +182,12 @@ describe "Signed little endian bitfields" do
|
|
179
182
|
end
|
180
183
|
|
181
184
|
it "read little endian values" do
|
182
|
-
|
185
|
+
all_objects do |obj, nbits|
|
183
186
|
nbytes = (nbits + 7) / 8
|
184
187
|
str = [0b0000_0001].pack("C") + "\000" * (nbytes - 1)
|
185
188
|
|
186
|
-
|
189
|
+
obj.read(str)
|
190
|
+
obj.must_equal 1
|
187
191
|
end
|
188
192
|
end
|
189
193
|
end
|
data/test/io_test.rb
CHANGED
@@ -152,6 +152,21 @@ describe BinData::IO::Write, "when writing" do
|
|
152
152
|
stream.value.must_equal "abcd"
|
153
153
|
end
|
154
154
|
|
155
|
+
it "has #offset" do
|
156
|
+
io.offset.must_equal 0
|
157
|
+
|
158
|
+
io.writebytes("abcd")
|
159
|
+
io.offset.must_equal 4
|
160
|
+
|
161
|
+
io.writebytes("ABCD")
|
162
|
+
io.offset.must_equal 8
|
163
|
+
end
|
164
|
+
|
165
|
+
it "rounds up #offset when writing bits" do
|
166
|
+
io.writebits(123, 9, :little)
|
167
|
+
io.offset.must_equal 2
|
168
|
+
end
|
169
|
+
|
155
170
|
it "flushes" do
|
156
171
|
io.writebytes("abcd")
|
157
172
|
io.flush
|
data/test/lazy_test.rb
CHANGED
@@ -73,12 +73,16 @@ end
|
|
73
73
|
|
74
74
|
describe BinData::LazyEvaluator, "with one parent" do
|
75
75
|
subject {
|
76
|
-
parent_methods = {:m1 => 'Pm1', :com1 => 'PmC', :mm => 3}
|
76
|
+
parent_methods = {:m1 => 'Pm1', :m2 => 'Pm2', :com1 => 'PmC', :mm => 3}
|
77
77
|
parent_params = {:p1 => 'Pp1', :com1 => 'PpC'}
|
78
78
|
parent_obj = MockBinDataObject.new(parent_methods, parent_params)
|
79
79
|
|
80
|
-
|
81
|
-
|
80
|
+
class << parent_obj
|
81
|
+
def echo(a1, a2)
|
82
|
+
[a1, a2]
|
83
|
+
end
|
84
|
+
|
85
|
+
private :m2
|
82
86
|
end
|
83
87
|
|
84
88
|
methods = {:m1 => 'm1', :com1 => 'mC'}
|
@@ -118,6 +122,10 @@ describe BinData::LazyEvaluator, "with one parent" do
|
|
118
122
|
lazy_eval(lambda { echo(p1, m1) }).must_equal ['Pp1', 'Pm1']
|
119
123
|
end
|
120
124
|
|
125
|
+
it "invokes private methods in the parent" do
|
126
|
+
lazy_eval(lambda { m2 }).must_equal 'Pm2'
|
127
|
+
end
|
128
|
+
|
121
129
|
it "resolves parameters in preference to methods in the parent" do
|
122
130
|
lazy_eval(lambda { com1 }).must_equal 'PpC'
|
123
131
|
end
|
data/test/record_test.rb
CHANGED
@@ -241,12 +241,12 @@ describe BinData::Record, "with nested structs" do
|
|
241
241
|
obj.c.y.must_equal 3
|
242
242
|
end
|
243
243
|
|
244
|
-
it "returns correct
|
245
|
-
obj.
|
246
|
-
obj.b.
|
247
|
-
obj.b.w.
|
248
|
-
obj.c.
|
249
|
-
obj.c.z.
|
244
|
+
it "returns correct abs_offset" do
|
245
|
+
obj.abs_offset.must_equal 0
|
246
|
+
obj.b.abs_offset.must_equal 1
|
247
|
+
obj.b.w.abs_offset.must_equal 1
|
248
|
+
obj.c.abs_offset.must_equal 3
|
249
|
+
obj.c.z.abs_offset.must_equal 4
|
250
250
|
end
|
251
251
|
|
252
252
|
it "returns correct rel_offset" do
|
@@ -476,6 +476,12 @@ describe BinData::Record, "with :onlyif" do
|
|
476
476
|
obj.to_binary_s.must_equal "\x03\x05"
|
477
477
|
end
|
478
478
|
|
479
|
+
it "identifies if fields are included" do
|
480
|
+
obj.a?.must_equal true
|
481
|
+
obj.b?.must_equal true
|
482
|
+
obj.c?.must_equal false
|
483
|
+
end
|
484
|
+
|
479
485
|
it "reads as lambdaed" do
|
480
486
|
obj.read("\x01\x02")
|
481
487
|
obj.snapshot.must_equal({"a" => 1, "c" => 2})
|
data/test/struct_test.rb
CHANGED
@@ -250,11 +250,11 @@ describe BinData::Struct, "with nested structs" do
|
|
250
250
|
obj.c.z.must_equal 0
|
251
251
|
end
|
252
252
|
|
253
|
-
it "returns correct
|
254
|
-
obj.b.
|
255
|
-
obj.b.w.
|
256
|
-
obj.c.
|
257
|
-
obj.c.z.
|
253
|
+
it "returns correct abs_offset" do
|
254
|
+
obj.b.abs_offset.must_equal 1
|
255
|
+
obj.b.w.abs_offset.must_equal 1
|
256
|
+
obj.c.abs_offset.must_equal 3
|
257
|
+
obj.c.z.abs_offset.must_equal 4
|
258
258
|
end
|
259
259
|
end
|
260
260
|
|
@@ -313,10 +313,10 @@ describe BinData::Struct, "with bit fields" do
|
|
313
313
|
end
|
314
314
|
|
315
315
|
it "has correct offsets" do
|
316
|
-
obj.a.
|
317
|
-
obj.b.
|
318
|
-
obj.c.
|
319
|
-
obj.d.
|
316
|
+
obj.a.rel_offset.must_equal 0
|
317
|
+
obj.b.rel_offset.must_equal 0
|
318
|
+
obj.c.rel_offset.must_equal 1
|
319
|
+
obj.d.rel_offset.must_equal 2
|
320
320
|
end
|
321
321
|
end
|
322
322
|
|
@@ -338,6 +338,37 @@ describe BinData::Struct, "with nested endian" do
|
|
338
338
|
end
|
339
339
|
end
|
340
340
|
|
341
|
+
describe BinData::Struct, "with byte_align" do
|
342
|
+
let(:obj) {
|
343
|
+
params = { :fields => [[:int8, :a],
|
344
|
+
[:int8, :b, {:byte_align => 5}],
|
345
|
+
[:bit2, :c],
|
346
|
+
[:int8, :d, {:byte_align => 3}]] }
|
347
|
+
BinData::Struct.new(params)
|
348
|
+
}
|
349
|
+
|
350
|
+
it "has #num_bytes" do
|
351
|
+
obj.num_bytes.must_equal 10
|
352
|
+
end
|
353
|
+
|
354
|
+
it "reads" do
|
355
|
+
obj.read("\x01\x00\x00\x00\x00\x02\xc0\x00\x00\x04")
|
356
|
+
obj.snapshot.must_equal({ "a" => 1, "b" => 2, "c" => 3, "d" => 4 })
|
357
|
+
end
|
358
|
+
|
359
|
+
it "writes" do
|
360
|
+
obj.assign(:a => 1, :b => 2, :c => 3, :d => 4)
|
361
|
+
obj.to_binary_s.must_equal binary("\x01\x00\x00\x00\x00\x02\xc0\x00\x00\x04")
|
362
|
+
end
|
363
|
+
|
364
|
+
it "has correct offsets" do
|
365
|
+
obj.a.rel_offset.must_equal 0
|
366
|
+
obj.b.rel_offset.must_equal 5
|
367
|
+
obj.c.rel_offset.must_equal 6
|
368
|
+
obj.d.rel_offset.must_equal 9
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
341
372
|
describe BinData::Struct, "with dynamically named types" do
|
342
373
|
it "instantiates" do
|
343
374
|
dyn = BinData::Struct.new(:name => :my_struct, :fields => [[:int8, :a, {:initial_value => 3}]])
|
data/test/system_test.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bindata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 49
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 1.8.
|
9
|
+
- 3
|
10
|
+
version: 1.8.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Dion Mendel
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2014-
|
18
|
+
date: 2014-04-16 00:00:00 +08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|