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/record.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'bindata/dsl'
|
1
2
|
require 'bindata/sanitize'
|
2
3
|
require 'bindata/struct'
|
3
4
|
|
@@ -6,22 +7,21 @@ module BinData
|
|
6
7
|
#
|
7
8
|
# require 'bindata'
|
8
9
|
#
|
9
|
-
# class Tuple < BinData::Record
|
10
|
-
# int8 :x
|
11
|
-
# int8 :y
|
12
|
-
# int8 :z
|
13
|
-
# end
|
14
|
-
#
|
15
10
|
# class SomeDataType < BinData::Record
|
16
11
|
# hide 'a'
|
17
12
|
#
|
18
13
|
# int32le :a
|
19
14
|
# int16le :b
|
20
|
-
#
|
15
|
+
# struct :s do
|
16
|
+
# int8 :x
|
17
|
+
# int8 :y
|
18
|
+
# int8 :z
|
19
|
+
# end
|
21
20
|
# end
|
22
21
|
#
|
23
22
|
# obj = SomeDataType.new
|
24
23
|
# obj.field_names =># ["b", "s"]
|
24
|
+
# obj.s.field_names =># ["x", "y", "z"]
|
25
25
|
#
|
26
26
|
#
|
27
27
|
# == Parameters
|
@@ -43,50 +43,12 @@ module BinData
|
|
43
43
|
# endian of any numerics in this struct, or in any
|
44
44
|
# nested data objects.
|
45
45
|
class Record < BinData::Struct
|
46
|
+
include DSLMixin
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
def inherited(subclass) #:nodoc:
|
50
|
-
# Register the names of all subclasses of this class.
|
51
|
-
register(subclass.name, subclass)
|
52
|
-
end
|
53
|
-
|
54
|
-
def endian(endian = nil)
|
55
|
-
@endian ||= default_endian
|
56
|
-
if [:little, :big].include?(endian)
|
57
|
-
@endian = endian
|
58
|
-
elsif endian != nil
|
59
|
-
raise ArgumentError,
|
60
|
-
"unknown value for endian '#{endian}' in #{self}", caller(1)
|
61
|
-
end
|
62
|
-
@endian
|
63
|
-
end
|
64
|
-
|
65
|
-
def hide(*args)
|
66
|
-
@hide ||= default_hide
|
67
|
-
@hide.concat(args.collect { |name| name.to_s })
|
68
|
-
@hide
|
69
|
-
end
|
70
|
-
|
71
|
-
def fields #:nodoc:
|
72
|
-
@fields ||= default_fields
|
73
|
-
end
|
74
|
-
|
75
|
-
def method_missing(symbol, *args) #:nodoc:
|
76
|
-
name, params = args
|
77
|
-
|
78
|
-
if name.is_a?(Hash)
|
79
|
-
params = name
|
80
|
-
name = nil
|
81
|
-
end
|
82
|
-
|
83
|
-
type = symbol
|
84
|
-
name = name.to_s
|
85
|
-
params ||= {}
|
86
|
-
|
87
|
-
append_field(type, name, params)
|
88
|
-
end
|
48
|
+
register_subclasses
|
49
|
+
dsl_parser :multiple_fields, :optional_fieldnames, :sanitize_fields, :hidden_fields
|
89
50
|
|
51
|
+
class << self
|
90
52
|
def sanitize_parameters!(params, sanitizer) #:nodoc:
|
91
53
|
params[:fields] = fields
|
92
54
|
params[:endian] = endian unless endian.nil?
|
@@ -94,56 +56,6 @@ module BinData
|
|
94
56
|
|
95
57
|
super(params, sanitizer)
|
96
58
|
end
|
97
|
-
|
98
|
-
#-------------
|
99
|
-
private
|
100
|
-
|
101
|
-
def parent_record
|
102
|
-
ancestors[1..-1].find { |cls|
|
103
|
-
cls.ancestors[1..-1].include?(BinData::Record)
|
104
|
-
}
|
105
|
-
end
|
106
|
-
|
107
|
-
def default_endian
|
108
|
-
rec = parent_record
|
109
|
-
rec ? rec.endian : nil
|
110
|
-
end
|
111
|
-
|
112
|
-
def default_hide
|
113
|
-
rec = parent_record
|
114
|
-
rec ? rec.hide.dup : []
|
115
|
-
end
|
116
|
-
|
117
|
-
def default_fields
|
118
|
-
rec = parent_record
|
119
|
-
if rec
|
120
|
-
Sanitizer.new.clone_sanitized_fields(rec.fields)
|
121
|
-
else
|
122
|
-
Sanitizer.new.create_sanitized_fields
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def append_field(type, name, params)
|
127
|
-
ensure_valid_name(name)
|
128
|
-
|
129
|
-
fields.add_field(type, name, params, endian)
|
130
|
-
rescue UnknownTypeError => err
|
131
|
-
raise TypeError, "unknown type '#{err.message}' for #{self}", caller(2)
|
132
|
-
end
|
133
|
-
|
134
|
-
def ensure_valid_name(name)
|
135
|
-
if fields.field_names.include?(name)
|
136
|
-
raise SyntaxError, "duplicate field '#{name}' in #{self}", caller(3)
|
137
|
-
end
|
138
|
-
if self.instance_methods.collect { |meth| meth.to_s }.include?(name)
|
139
|
-
raise NameError.new("", name),
|
140
|
-
"field '#{name}' shadows an existing method in #{self}", caller(3)
|
141
|
-
end
|
142
|
-
if self::RESERVED.include?(name)
|
143
|
-
raise NameError.new("", name),
|
144
|
-
"field '#{name}' is a reserved name in #{self}", caller(3)
|
145
|
-
end
|
146
|
-
end
|
147
59
|
end
|
148
60
|
end
|
149
61
|
end
|
data/lib/bindata/registry.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
module BinData
|
2
|
+
|
3
|
+
class UnRegisteredTypeError < StandardError ; end
|
4
|
+
|
2
5
|
# This registry contains a register of name -> class mappings.
|
3
6
|
#
|
7
|
+
# Numerics (integers and floating point numbers) have an endian property as
|
8
|
+
# part of their name (e.g. int32be, float_le). The lookup can either be
|
9
|
+
# on the full name, or on the shortened name plus endian (e.g. "int32", :big)
|
10
|
+
#
|
4
11
|
# Names are stored in under_score_style, not camelCase.
|
5
12
|
class Registry
|
6
13
|
|
@@ -21,7 +28,13 @@ module BinData
|
|
21
28
|
key = lookup_key(name, endian)
|
22
29
|
try_registering_key(key) unless @registry.has_key?(key)
|
23
30
|
|
24
|
-
@registry[key]
|
31
|
+
@registry[key] || raise(UnRegisteredTypeError, name.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize_name(name, endian = nil)
|
35
|
+
if lookup(name, endian)
|
36
|
+
lookup_key(name, endian)
|
37
|
+
end
|
25
38
|
end
|
26
39
|
|
27
40
|
def is_registered?(name, endian = nil)
|
@@ -65,8 +78,8 @@ module BinData
|
|
65
78
|
end
|
66
79
|
|
67
80
|
def warn_if_name_is_already_registered(name, class_to_register)
|
68
|
-
|
69
|
-
|
81
|
+
prev_class = @registry[name]
|
82
|
+
if $VERBOSE and prev_class and prev_class != class_to_register
|
70
83
|
warn "warning: replacing registered class #{prev_class} " +
|
71
84
|
"with #{class_to_register}"
|
72
85
|
end
|
data/lib/bindata/rest.rb
CHANGED
data/lib/bindata/sanitize.rb
CHANGED
@@ -2,18 +2,25 @@ require 'bindata/registry'
|
|
2
2
|
|
3
3
|
module BinData
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# When a BinData object is instantiated, it can be supplied parameters to
|
6
|
+
# determine its behaviour. These parameters must be sanitized to ensure
|
7
|
+
# their values are valid. When instantiating many objects, such as an array
|
8
|
+
# of records, there is much duplicated validation.
|
9
|
+
#
|
10
|
+
# The purpose of the sanitizing code is to eliminate the duplicated
|
11
|
+
# validation.
|
12
|
+
#
|
13
|
+
# SanitizedParameters is a hash-like collection of parameters. Its purpose
|
14
|
+
# it to recursively sanitize the parameters of an entire BinData object chain
|
15
|
+
# at a single time.
|
9
16
|
class SanitizedParameters
|
10
17
|
|
11
|
-
def initialize(
|
18
|
+
def initialize(parameters, the_class)
|
12
19
|
@all_sanitized = false
|
13
20
|
@the_class = the_class
|
14
21
|
|
15
22
|
@parameters = {}
|
16
|
-
|
23
|
+
parameters.each { |key, value| @parameters[key.to_sym] = value }
|
17
24
|
|
18
25
|
ensure_no_nil_values
|
19
26
|
end
|
@@ -23,20 +30,22 @@ module BinData
|
|
23
30
|
end
|
24
31
|
alias_method :size, :length
|
25
32
|
|
26
|
-
def [](
|
27
|
-
@parameters[
|
33
|
+
def [](key)
|
34
|
+
@parameters[key]
|
28
35
|
end
|
29
36
|
|
30
|
-
def []=(
|
31
|
-
@parameters[
|
37
|
+
def []=(key, value)
|
38
|
+
@parameters[key] = value unless @all_sanitized
|
32
39
|
end
|
33
40
|
|
34
|
-
def has_parameter?(
|
35
|
-
@parameters.has_key?(
|
41
|
+
def has_parameter?(key)
|
42
|
+
@parameters.has_key?(key)
|
36
43
|
end
|
37
44
|
|
38
|
-
def needs_sanitizing?(
|
39
|
-
|
45
|
+
def needs_sanitizing?(key)
|
46
|
+
parameter = @parameters[key]
|
47
|
+
|
48
|
+
parameter and not parameter.is_a?(SanitizedParameter)
|
40
49
|
end
|
41
50
|
|
42
51
|
def all_sanitized?
|
@@ -59,40 +68,42 @@ module BinData
|
|
59
68
|
def move_unknown_parameters_to(dest)
|
60
69
|
unless @all_sanitized
|
61
70
|
unused_keys = @parameters.keys - @the_class.accepted_parameters.all
|
62
|
-
unused_keys.each do |
|
63
|
-
|
64
|
-
dest[param] = @parameters.delete(param)
|
71
|
+
unused_keys.each do |key|
|
72
|
+
dest[key] = @parameters.delete(key)
|
65
73
|
end
|
66
74
|
end
|
67
75
|
end
|
68
76
|
|
69
|
-
def
|
70
|
-
|
77
|
+
def warn_replacement_parameter(bad_key, suggested_key)
|
78
|
+
if has_parameter?(bad_key)
|
79
|
+
warn ":#{bad_key} is not used with #{@the_class}. " +
|
80
|
+
"You probably want to change this to :#{suggested_key}"
|
81
|
+
end
|
71
82
|
end
|
72
83
|
|
73
84
|
#---------------
|
74
85
|
private
|
75
86
|
|
76
87
|
def ensure_no_nil_values
|
77
|
-
@parameters.each do |
|
88
|
+
@parameters.each do |key, value|
|
78
89
|
if value.nil?
|
79
90
|
raise ArgumentError,
|
80
|
-
"parameter '#{
|
91
|
+
"parameter '#{key}' has nil value in #{@the_class}"
|
81
92
|
end
|
82
93
|
end
|
83
94
|
end
|
84
95
|
|
85
96
|
def merge_default_parameters!
|
86
|
-
@the_class.default_parameters.each do |
|
87
|
-
|
97
|
+
@the_class.default_parameters.each do |key, value|
|
98
|
+
@parameters[key] ||= value
|
88
99
|
end
|
89
100
|
end
|
90
101
|
|
91
102
|
def ensure_mandatory_parameters_exist
|
92
|
-
@the_class.mandatory_parameters.each do |
|
93
|
-
unless has_parameter?(
|
103
|
+
@the_class.mandatory_parameters.each do |key|
|
104
|
+
unless has_parameter?(key)
|
94
105
|
raise ArgumentError,
|
95
|
-
"parameter '#{
|
106
|
+
"parameter '#{key}' must be specified in #{@the_class}"
|
96
107
|
end
|
97
108
|
end
|
98
109
|
end
|
@@ -100,9 +111,9 @@ module BinData
|
|
100
111
|
def ensure_mutual_exclusion_of_parameters
|
101
112
|
return if length < 2
|
102
113
|
|
103
|
-
@the_class.mutually_exclusive_parameters.each do |
|
104
|
-
if has_parameter?(
|
105
|
-
raise ArgumentError, "params '#{
|
114
|
+
@the_class.mutually_exclusive_parameters.each do |key1, key2|
|
115
|
+
if has_parameter?(key1) and has_parameter?(key2)
|
116
|
+
raise ArgumentError, "params '#{key1}' and '#{key2}' " +
|
106
117
|
"are mutually exclusive in #{@the_class}"
|
107
118
|
end
|
108
119
|
end
|
@@ -142,20 +153,23 @@ module BinData
|
|
142
153
|
end
|
143
154
|
|
144
155
|
def create_sanitized_endian(endian)
|
145
|
-
|
156
|
+
# memoize return value to reduce memory usage
|
157
|
+
if endian == :big
|
158
|
+
@@sbe ||= SanitizedBigEndian.new
|
159
|
+
elsif endian == :little
|
160
|
+
@@sle ||= SanitizedLittleEndian.new
|
161
|
+
else
|
162
|
+
raise ArgumentError, "unknown value for endian '#{endian}'"
|
163
|
+
end
|
146
164
|
end
|
147
165
|
|
148
166
|
def create_sanitized_choices(choices)
|
149
167
|
SanitizedChoices.new(self, choices)
|
150
168
|
end
|
151
169
|
|
152
|
-
def create_sanitized_fields
|
153
|
-
SanitizedFields.new(self)
|
154
|
-
end
|
155
|
-
|
156
|
-
def clone_sanitized_fields(fields)
|
170
|
+
def create_sanitized_fields(fields = nil)
|
157
171
|
new_fields = SanitizedFields.new(self)
|
158
|
-
new_fields.copy_fields(fields)
|
172
|
+
new_fields.copy_fields(fields) if fields
|
159
173
|
new_fields
|
160
174
|
end
|
161
175
|
|
@@ -166,7 +180,7 @@ module BinData
|
|
166
180
|
def with_endian(endian, &block)
|
167
181
|
if endian != nil
|
168
182
|
saved_endian = @endian
|
169
|
-
@endian = endian.
|
183
|
+
@endian = endian.respond_to?(:endian) ? endian.endian : endian
|
170
184
|
yield
|
171
185
|
@endian = saved_endian
|
172
186
|
else
|
@@ -175,11 +189,7 @@ module BinData
|
|
175
189
|
end
|
176
190
|
|
177
191
|
def lookup_class(type)
|
178
|
-
|
179
|
-
if registered_class.nil?
|
180
|
-
raise UnknownTypeError, type.to_s
|
181
|
-
end
|
182
|
-
registered_class
|
192
|
+
RegisteredClasses.lookup(type, @endian)
|
183
193
|
end
|
184
194
|
|
185
195
|
#---------------
|
@@ -228,9 +238,12 @@ module BinData
|
|
228
238
|
def initialize(sanitizer)
|
229
239
|
@sanitizer = sanitizer
|
230
240
|
@fields = []
|
241
|
+
@field_names = nil
|
231
242
|
end
|
243
|
+
attr_reader :fields
|
232
244
|
|
233
245
|
def add_field(type, name, params, endian)
|
246
|
+
@field_names = nil
|
234
247
|
@fields << SanitizedField.new(@sanitizer, name, type, params, endian)
|
235
248
|
end
|
236
249
|
|
@@ -238,13 +251,18 @@ module BinData
|
|
238
251
|
@fields[idx]
|
239
252
|
end
|
240
253
|
|
254
|
+
def empty?
|
255
|
+
@fields.empty?
|
256
|
+
end
|
257
|
+
|
241
258
|
def field_names
|
242
|
-
|
259
|
+
# memoize field names to reduce duplicate copies
|
260
|
+
@field_names ||= @fields.collect { |field| field.name }
|
243
261
|
end
|
244
262
|
|
245
263
|
def copy_fields(other)
|
246
|
-
|
247
|
-
@fields.concat(
|
264
|
+
@field_names = nil
|
265
|
+
@fields.concat(other.fields)
|
248
266
|
end
|
249
267
|
end
|
250
268
|
#----------------------------------------------------------------------------
|
@@ -265,15 +283,15 @@ module BinData
|
|
265
283
|
end
|
266
284
|
#----------------------------------------------------------------------------
|
267
285
|
|
268
|
-
class
|
269
|
-
def
|
270
|
-
|
271
|
-
raise ArgumentError, "unknown value for endian '#{endian}'"
|
272
|
-
end
|
273
|
-
|
274
|
-
@endian = endian
|
286
|
+
class SanitizedBigEndian < SanitizedParameter
|
287
|
+
def endian
|
288
|
+
:big
|
275
289
|
end
|
290
|
+
end
|
276
291
|
|
277
|
-
|
292
|
+
class SanitizedLittleEndian < SanitizedParameter
|
293
|
+
def endian
|
294
|
+
:little
|
295
|
+
end
|
278
296
|
end
|
279
297
|
end
|
data/lib/bindata/skip.rb
CHANGED
data/lib/bindata/string.rb
CHANGED
@@ -20,7 +20,7 @@ module BinData
|
|
20
20
|
# obj.value = "abcd"
|
21
21
|
# obj.value #=> "abcd\000\000"
|
22
22
|
#
|
23
|
-
# obj = BinData::String.new(:length => 6, :
|
23
|
+
# obj = BinData::String.new(:length => 6, :trim_padding => true)
|
24
24
|
# obj.value = "abcd"
|
25
25
|
# obj.value #=> "abcd"
|
26
26
|
# obj.to_binary_s #=> "abcd\000\000"
|
@@ -47,7 +47,7 @@ module BinData
|
|
47
47
|
# not be trimmed when writing.
|
48
48
|
class String < BinData::BasePrimitive
|
49
49
|
|
50
|
-
|
50
|
+
register_self
|
51
51
|
|
52
52
|
optional_parameters :read_length, :length, :trim_padding
|
53
53
|
default_parameters :pad_char => "\0"
|
@@ -57,7 +57,7 @@ module BinData
|
|
57
57
|
class << self
|
58
58
|
|
59
59
|
def sanitize_parameters!(params, sanitizer) #:nodoc:
|
60
|
-
warn_replacement_parameter(
|
60
|
+
params.warn_replacement_parameter(:initial_length, :read_length)
|
61
61
|
|
62
62
|
if params.has_parameter?(:pad_char)
|
63
63
|
ch = params[:pad_char]
|