bindata 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/ChangeLog +7 -0
- data/README +32 -1167
- data/lib/bindata.rb +3 -3
- data/lib/bindata/array.rb +5 -6
- data/lib/bindata/base.rb +40 -58
- data/lib/bindata/base_primitive.rb +7 -11
- data/lib/bindata/bits.rb +47 -44
- data/lib/bindata/choice.rb +7 -11
- data/lib/bindata/deprecated.rb +17 -2
- data/lib/bindata/dsl.rb +332 -0
- data/lib/bindata/float.rb +48 -50
- data/lib/bindata/int.rb +66 -88
- data/lib/bindata/params.rb +112 -59
- data/lib/bindata/primitive.rb +8 -88
- data/lib/bindata/record.rb +11 -99
- data/lib/bindata/registry.rb +16 -3
- data/lib/bindata/rest.rb +1 -1
- data/lib/bindata/sanitize.rb +71 -53
- data/lib/bindata/skip.rb +2 -1
- data/lib/bindata/string.rb +3 -3
- data/lib/bindata/stringz.rb +1 -1
- data/lib/bindata/struct.rb +21 -20
- data/lib/bindata/trace.rb +8 -0
- data/lib/bindata/wrapper.rb +13 -69
- data/manual.haml +2 -2
- data/spec/array_spec.rb +1 -1
- data/spec/base_primitive_spec.rb +4 -4
- data/spec/base_spec.rb +19 -6
- data/spec/bits_spec.rb +5 -1
- data/spec/choice_spec.rb +13 -2
- data/spec/deprecated_spec.rb +31 -0
- data/spec/example.rb +5 -1
- data/spec/io_spec.rb +2 -4
- data/spec/lazy_spec.rb +10 -5
- data/spec/primitive_spec.rb +13 -5
- data/spec/record_spec.rb +149 -45
- data/spec/registry_spec.rb +18 -6
- data/spec/spec_common.rb +31 -6
- data/spec/string_spec.rb +0 -1
- data/spec/stringz_spec.rb +4 -4
- data/spec/struct_spec.rb +2 -2
- data/spec/system_spec.rb +26 -19
- data/spec/wrapper_spec.rb +52 -4
- data/tasks/manual.rake +1 -1
- data/tasks/pkg.rake +13 -0
- metadata +121 -46
- data/TODO +0 -3
data/lib/bindata/int.rb
CHANGED
@@ -9,16 +9,14 @@ module BinData
|
|
9
9
|
def define_class(nbits, endian, signed)
|
10
10
|
name = class_name(nbits, endian, signed)
|
11
11
|
unless BinData.const_defined?(name)
|
12
|
-
int_type = (signed == :signed) ? 'int' : 'uint'
|
13
|
-
creation_method = "create_#{int_type}_methods"
|
14
|
-
|
15
12
|
BinData.module_eval <<-END
|
16
13
|
class #{name} < BinData::BasePrimitive
|
17
|
-
|
18
|
-
Int
|
14
|
+
register_self
|
15
|
+
Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
|
19
16
|
end
|
20
17
|
END
|
21
18
|
end
|
19
|
+
|
22
20
|
BinData.const_get(name)
|
23
21
|
end
|
24
22
|
|
@@ -29,37 +27,51 @@ module BinData
|
|
29
27
|
"#{base}#{nbits}#{endian_str}"
|
30
28
|
end
|
31
29
|
|
32
|
-
def
|
30
|
+
def define_methods(int_class, nbits, endian, signed)
|
33
31
|
raise "nbits must be divisible by 8" unless (nbits % 8).zero?
|
34
32
|
|
35
|
-
|
36
|
-
|
33
|
+
int_class.module_eval <<-END
|
34
|
+
#---------------
|
35
|
+
private
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
def _assign(val)
|
38
|
+
#{create_clamp_code(nbits, signed)}
|
39
|
+
super(val)
|
40
|
+
end
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
def _do_num_bytes
|
43
|
+
#{nbits / 8}
|
44
|
+
end
|
44
45
|
|
45
|
-
|
46
|
-
|
46
|
+
def sensible_default
|
47
|
+
0
|
48
|
+
end
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
+
def value_to_binary_string(val)
|
51
|
+
#{create_clamp_code(nbits, signed)}
|
52
|
+
#{create_int2uint_code(nbits) if signed == :signed}
|
53
|
+
#{create_to_binary_s_code(nbits, endian)}
|
54
|
+
end
|
50
55
|
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
def read_and_return_value(io)
|
57
|
+
val = #{create_read_code(nbits, endian)}
|
58
|
+
#{create_uint2int_code(nbits) if signed == :signed}
|
59
|
+
end
|
60
|
+
END
|
61
|
+
end
|
54
62
|
|
55
|
-
|
56
|
-
|
63
|
+
#-------------
|
64
|
+
private
|
57
65
|
|
58
|
-
|
59
|
-
|
60
|
-
|
66
|
+
def create_clamp_code(nbits, signed)
|
67
|
+
if signed == :signed
|
68
|
+
max = (1 << (nbits - 1)) - 1
|
69
|
+
min = -(max + 1)
|
70
|
+
else
|
71
|
+
min = 0
|
72
|
+
max = (1 << nbits) - 1
|
73
|
+
end
|
61
74
|
|
62
|
-
def create_clamp_code(min, max)
|
63
75
|
"val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
|
64
76
|
end
|
65
77
|
|
@@ -75,31 +87,22 @@ module BinData
|
|
75
87
|
end
|
76
88
|
|
77
89
|
def create_read_code(nbits, endian)
|
78
|
-
|
79
|
-
if (nbits % 32).zero?
|
80
|
-
bytes_per_word = 4
|
81
|
-
d = (endian == :big) ? 'N' : 'V'
|
82
|
-
elsif (nbits % 16).zero?
|
83
|
-
bytes_per_word = 2
|
84
|
-
d = (endian == :big) ? 'n' : 'v'
|
85
|
-
else
|
86
|
-
bytes_per_word = 1
|
87
|
-
d = 'C'
|
88
|
-
end
|
89
|
-
|
90
|
-
bits_per_word = bytes_per_word * 8
|
90
|
+
bits_per_word = bytes_per_word(nbits) * 8
|
91
91
|
nwords = nbits / bits_per_word
|
92
92
|
nbytes = nbits / 8
|
93
93
|
|
94
94
|
idx = (0 ... nwords).to_a
|
95
95
|
idx.reverse! if (endian == :big)
|
96
96
|
|
97
|
-
unpack_str = "a = io.readbytes(#{nbytes}).unpack('#{d * nwords}')"
|
98
|
-
|
99
97
|
parts = (0 ... nwords).collect do |i|
|
100
|
-
i.zero?
|
101
|
-
|
98
|
+
if i.zero?
|
99
|
+
"a.at(#{idx[i]})"
|
100
|
+
else
|
101
|
+
"(a.at(#{idx[i]}) << #{bits_per_word * i})"
|
102
|
+
end
|
102
103
|
end
|
104
|
+
|
105
|
+
unpack_str = "a = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits, endian)}')"
|
103
106
|
assemble_str = parts.join(" + ")
|
104
107
|
|
105
108
|
"(#{unpack_str}; #{assemble_str})"
|
@@ -109,19 +112,7 @@ module BinData
|
|
109
112
|
# special case 8bit integers for speed
|
110
113
|
return "val.chr" if nbits == 8
|
111
114
|
|
112
|
-
|
113
|
-
if (nbits % 32).zero?
|
114
|
-
bytes_per_word = 4
|
115
|
-
d = (endian == :big) ? 'N' : 'V'
|
116
|
-
elsif (nbits % 16).zero?
|
117
|
-
bytes_per_word = 2
|
118
|
-
d = (endian == :big) ? 'n' : 'v'
|
119
|
-
else
|
120
|
-
bytes_per_word = 1
|
121
|
-
d = 'C'
|
122
|
-
end
|
123
|
-
|
124
|
-
bits_per_word = bytes_per_word * 8
|
115
|
+
bits_per_word = bytes_per_word(nbits) * 8
|
125
116
|
nwords = nbits / bits_per_word
|
126
117
|
mask = (1 << bits_per_word) - 1
|
127
118
|
|
@@ -133,39 +124,26 @@ module BinData
|
|
133
124
|
parts = (0 ... nwords).collect { |i| "#{vals[i]} & #{mask}" }
|
134
125
|
array_str = "[" + parts.join(", ") + "]"
|
135
126
|
|
136
|
-
"#{array_str}.pack('#{
|
127
|
+
"#{array_str}.pack('#{pack_directive(nbits, endian)}')"
|
137
128
|
end
|
138
129
|
|
139
|
-
def
|
140
|
-
|
141
|
-
|
142
|
-
#---------------
|
143
|
-
private
|
144
|
-
|
145
|
-
def _assign(val)
|
146
|
-
#{clamp}
|
147
|
-
super(val)
|
148
|
-
end
|
149
|
-
|
150
|
-
def _do_num_bytes
|
151
|
-
#{nbytes}
|
152
|
-
end
|
130
|
+
def bytes_per_word(nbits)
|
131
|
+
(nbits % 32).zero? ? 4 : (nbits % 16).zero? ? 2 : 1
|
132
|
+
end
|
153
133
|
|
154
|
-
|
155
|
-
|
156
|
-
|
134
|
+
def pack_directive(nbits, endian)
|
135
|
+
bits_per_word = bytes_per_word(nbits) * 8
|
136
|
+
nwords = nbits / bits_per_word
|
157
137
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
138
|
+
if (nbits % 32).zero?
|
139
|
+
d = (endian == :big) ? 'N' : 'V'
|
140
|
+
elsif (nbits % 16).zero?
|
141
|
+
d = (endian == :big) ? 'n' : 'v'
|
142
|
+
else
|
143
|
+
d = 'C'
|
144
|
+
end
|
163
145
|
|
164
|
-
|
165
|
-
val = #{read}
|
166
|
-
#{uint2int unless uint2int.nil?}
|
167
|
-
end
|
168
|
-
END
|
146
|
+
d * nwords
|
169
147
|
end
|
170
148
|
end
|
171
149
|
end
|
@@ -173,14 +151,14 @@ module BinData
|
|
173
151
|
|
174
152
|
# Unsigned 1 byte integer.
|
175
153
|
class Uint8 < BinData::BasePrimitive
|
176
|
-
|
177
|
-
Int.
|
154
|
+
register_self
|
155
|
+
Int.define_methods(self, 8, :little, :unsigned)
|
178
156
|
end
|
179
157
|
|
180
158
|
# Signed 1 byte integer.
|
181
159
|
class Int8 < BinData::BasePrimitive
|
182
|
-
|
183
|
-
Int.
|
160
|
+
register_self
|
161
|
+
Int.define_methods(self, 8, :little, :signed)
|
184
162
|
end
|
185
163
|
|
186
164
|
# Create classes on demand
|
data/lib/bindata/params.rb
CHANGED
@@ -1,82 +1,135 @@
|
|
1
1
|
require 'bindata/lazy'
|
2
2
|
|
3
3
|
module BinData
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class AcceptedParameters
|
8
|
-
|
9
|
-
def self.invalid_parameter_names
|
10
|
-
unless defined? @invalid_names
|
11
|
-
all_names = LazyEvaluator.instance_methods(true) + Kernel.methods
|
12
|
-
all_names.collect! { |name| name.to_s }
|
13
|
-
allowed_names = ["type"]
|
14
|
-
@invalid_names = all_names - allowed_names
|
15
|
-
end
|
16
|
-
@invalid_names
|
4
|
+
module AcceptedParametersMixin
|
5
|
+
def self.included(base) #:nodoc:
|
6
|
+
base.extend ClassMethods
|
17
7
|
end
|
18
8
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
9
|
+
# Class methods to mix in to BinData::Base
|
10
|
+
module ClassMethods
|
11
|
+
# Mandatory parameters must be present when instantiating a data object.
|
12
|
+
def mandatory_parameters(*args)
|
13
|
+
accepted_parameters.mandatory(*args)
|
14
|
+
end
|
26
15
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
@mandatory.concat(args.collect { |arg| arg.to_sym })
|
31
|
-
@mandatory.uniq!
|
16
|
+
# Optional parameters may be present when instantiating a data object.
|
17
|
+
def optional_parameters(*args)
|
18
|
+
accepted_parameters.optional(*args)
|
32
19
|
end
|
33
|
-
@mandatory.dup
|
34
|
-
end
|
35
20
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@optional.concat(args.collect { |arg| arg.to_sym })
|
40
|
-
@optional.uniq!
|
21
|
+
# Default parameters can be overridden when instantiating a data object.
|
22
|
+
def default_parameters(*args)
|
23
|
+
accepted_parameters.default(*args)
|
41
24
|
end
|
42
|
-
@optional.dup
|
43
|
-
end
|
44
25
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
args
|
49
|
-
|
26
|
+
# Mutually exclusive parameters may not all be present when
|
27
|
+
# instantiating a data object.
|
28
|
+
def mutually_exclusive_parameters(*args)
|
29
|
+
accepted_parameters.mutually_exclusive(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :mandatory_parameter, :mandatory_parameters
|
33
|
+
alias_method :optional_parameter, :optional_parameters
|
34
|
+
alias_method :default_parameter, :default_parameters
|
35
|
+
|
36
|
+
def accepted_parameters #:nodoc:
|
37
|
+
unless defined? @accepted_parameters
|
38
|
+
ancestor_params = superclass.respond_to?(:accepted_parameters) ?
|
39
|
+
superclass.accepted_parameters : nil
|
40
|
+
@accepted_parameters = AcceptedParameters.new(ancestor_params)
|
50
41
|
end
|
42
|
+
@accepted_parameters
|
51
43
|
end
|
52
|
-
@default.dup
|
53
44
|
end
|
54
45
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
46
|
+
# BinData objects accept parameters when initializing. AcceptedParameters
|
47
|
+
# allow a BinData class to declaratively identify accepted parameters as
|
48
|
+
# mandatory, optional, default or mutually exclusive.
|
49
|
+
class AcceptedParameters
|
50
|
+
|
51
|
+
def initialize(ancestor_parameters = nil)
|
52
|
+
if ancestor_parameters
|
53
|
+
@mandatory = ancestor_parameters.mandatory.dup
|
54
|
+
@optional = ancestor_parameters.optional.dup
|
55
|
+
@default = ancestor_parameters.default.dup
|
56
|
+
@mutually_exclusive = ancestor_parameters.mutually_exclusive.dup
|
57
|
+
else
|
58
|
+
@mandatory = []
|
59
|
+
@optional = []
|
60
|
+
@default = Hash.new
|
61
|
+
@mutually_exclusive = []
|
62
|
+
end
|
60
63
|
end
|
61
|
-
@mutually_exclusive.dup
|
62
|
-
end
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
def mandatory(*args)
|
66
|
+
if not args.empty?
|
67
|
+
@mandatory.concat(to_syms(args))
|
68
|
+
@mandatory.uniq!
|
69
|
+
end
|
70
|
+
@mandatory
|
71
|
+
end
|
67
72
|
|
68
|
-
|
69
|
-
|
73
|
+
def optional(*args)
|
74
|
+
if not args.empty?
|
75
|
+
@optional.concat(to_syms(args))
|
76
|
+
@optional.uniq!
|
77
|
+
end
|
78
|
+
@optional
|
79
|
+
end
|
70
80
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
"as it shadows an existing method.", name)
|
81
|
+
def default(args = {})
|
82
|
+
if not args.empty?
|
83
|
+
to_syms(args.keys) # call for side effect of validating names
|
84
|
+
args.each_pair do |param, value|
|
85
|
+
@default[param.to_sym] = value
|
86
|
+
end
|
78
87
|
end
|
88
|
+
@default
|
89
|
+
end
|
90
|
+
|
91
|
+
def mutually_exclusive(*args)
|
92
|
+
arg1, arg2 = args
|
93
|
+
if arg1 != nil && arg2 != nil
|
94
|
+
@mutually_exclusive.push([arg1.to_sym, arg2.to_sym])
|
95
|
+
@mutually_exclusive.uniq!
|
96
|
+
end
|
97
|
+
@mutually_exclusive
|
98
|
+
end
|
99
|
+
|
100
|
+
def all
|
101
|
+
(@mandatory + @optional + @default.keys).uniq
|
102
|
+
end
|
103
|
+
|
104
|
+
#---------------
|
105
|
+
private
|
106
|
+
|
107
|
+
def to_syms(args)
|
108
|
+
syms = args.collect { |el| el.to_sym }
|
109
|
+
ensure_valid_names(syms)
|
110
|
+
syms
|
111
|
+
end
|
112
|
+
|
113
|
+
def ensure_valid_names(names)
|
114
|
+
invalid_names = self.class.invalid_parameter_names
|
115
|
+
names.each do |name|
|
116
|
+
if invalid_names.include?(name)
|
117
|
+
raise NameError.new("Rename parameter '#{name}' " +
|
118
|
+
"as it shadows an existing method.", name)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.invalid_parameter_names
|
124
|
+
unless defined? @invalid_names
|
125
|
+
all_names = LazyEvaluator.instance_methods(true) + Kernel.methods
|
126
|
+
allowed_names = ["type", :type] # ruby 1.8 vs 1.9
|
127
|
+
invalid_names = (all_names - allowed_names).uniq
|
128
|
+
@invalid_names = Hash[*invalid_names.collect { |key| [key.to_sym, true] }.flatten]
|
129
|
+
end
|
130
|
+
@invalid_names
|
79
131
|
end
|
80
132
|
end
|
81
133
|
end
|
82
134
|
end
|
135
|
+
|
data/lib/bindata/primitive.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'bindata/base_primitive'
|
2
|
+
require 'bindata/dsl'
|
2
3
|
require 'bindata/struct'
|
3
4
|
|
4
5
|
module BinData
|
@@ -59,98 +60,21 @@ module BinData
|
|
59
60
|
# Primitive objects accept all the parameters that BinData::BasePrimitive do.
|
60
61
|
#
|
61
62
|
class Primitive < BasePrimitive
|
63
|
+
include DSLMixin
|
62
64
|
|
63
|
-
|
64
|
-
|
65
|
-
def inherited(subclass) #:nodoc:
|
66
|
-
# Register the names of all subclasses of this class.
|
67
|
-
register(subclass.name, subclass)
|
68
|
-
end
|
69
|
-
|
70
|
-
def endian(endian = nil)
|
71
|
-
@endian ||= default_endian
|
72
|
-
if [:little, :big].include?(endian)
|
73
|
-
@endian = endian
|
74
|
-
elsif endian != nil
|
75
|
-
raise ArgumentError,
|
76
|
-
"unknown value for endian '#{endian}' in #{self}", caller(1)
|
77
|
-
end
|
78
|
-
@endian
|
79
|
-
end
|
80
|
-
|
81
|
-
def fields #:nodoc:
|
82
|
-
@fields ||= default_fields
|
83
|
-
end
|
84
|
-
|
85
|
-
def method_missing(symbol, *args) #:nodoc:
|
86
|
-
name, params = args
|
87
|
-
|
88
|
-
if name.is_a?(Hash)
|
89
|
-
params = name
|
90
|
-
name = nil
|
91
|
-
end
|
92
|
-
|
93
|
-
type = symbol
|
94
|
-
name = name.to_s
|
95
|
-
params ||= {}
|
96
|
-
|
97
|
-
append_field(type, name, params)
|
98
|
-
end
|
65
|
+
register_subclasses
|
66
|
+
dsl_parser :multiple_fields, :optional_fieldnames, :sanitize_fields
|
99
67
|
|
68
|
+
class << self
|
100
69
|
def sanitize_parameters!(params, sanitizer) #:nodoc:
|
101
|
-
struct_params =
|
102
|
-
struct_params[:fields] = fields
|
103
|
-
struct_params[:endian] = endian unless endian.nil?
|
104
|
-
|
105
|
-
params[:struct_params] = struct_params
|
106
|
-
end
|
107
|
-
|
108
|
-
#-------------
|
109
|
-
private
|
110
|
-
|
111
|
-
def parent_primitive
|
112
|
-
ancestors[1..-1].find { |cls|
|
113
|
-
cls.ancestors[1..-1].include?(BinData::Primitive)
|
114
|
-
}
|
115
|
-
end
|
116
|
-
|
117
|
-
def default_endian
|
118
|
-
prim = parent_primitive
|
119
|
-
prim ? prim.endian : nil
|
120
|
-
end
|
121
|
-
|
122
|
-
def default_fields
|
123
|
-
prim = parent_primitive
|
124
|
-
if prim
|
125
|
-
Sanitizer.new.clone_sanitized_fields(prim.fields)
|
126
|
-
else
|
127
|
-
Sanitizer.new.create_sanitized_fields
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def append_field(type, name, params)
|
132
|
-
ensure_valid_name(name)
|
133
|
-
|
134
|
-
fields.add_field(type, name, params, endian)
|
135
|
-
rescue UnknownTypeError => err
|
136
|
-
raise TypeError, "unknown type '#{err.message}' for #{self}", caller(2)
|
137
|
-
end
|
138
|
-
|
139
|
-
def ensure_valid_name(name)
|
140
|
-
if fields.field_names.include?(name)
|
141
|
-
raise SyntaxError, "duplicate field '#{name}' in #{self}", caller(3)
|
142
|
-
end
|
143
|
-
if self.instance_methods.collect { |meth| meth.to_s }.include?(name)
|
144
|
-
raise NameError.new("", name),
|
145
|
-
"field '#{name}' shadows an existing method in #{self}", caller(3)
|
146
|
-
end
|
70
|
+
params[:struct_params] = sanitizer.create_sanitized_params(to_struct_params, BinData::Struct)
|
147
71
|
end
|
148
72
|
end
|
149
73
|
|
150
74
|
mandatory_parameter :struct_params
|
151
75
|
|
152
|
-
def initialize(
|
153
|
-
super
|
76
|
+
def initialize(parameters = {}, parent = nil)
|
77
|
+
super
|
154
78
|
|
155
79
|
@struct = BinData::Struct.new(get_parameter(:struct_params), self)
|
156
80
|
end
|
@@ -163,10 +87,6 @@ module BinData
|
|
163
87
|
debug_name + "-internal-"
|
164
88
|
end
|
165
89
|
|
166
|
-
def offset_of(child) #:nodoc:
|
167
|
-
@struct.offset_of(child)
|
168
|
-
end
|
169
|
-
|
170
90
|
#---------------
|
171
91
|
private
|
172
92
|
|