bindata 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/BSDL +22 -0
- data/COPYING +2 -2
- data/ChangeLog +12 -0
- data/NEWS +28 -0
- data/examples/list.rb +7 -7
- data/examples/nbt.rb +21 -38
- data/lib/bindata.rb +1 -1
- data/lib/bindata/alignment.rb +1 -4
- data/lib/bindata/array.rb +8 -4
- data/lib/bindata/base.rb +20 -17
- data/lib/bindata/base_primitive.rb +1 -0
- data/lib/bindata/bits.rb +0 -1
- data/lib/bindata/choice.rb +20 -42
- data/lib/bindata/deprecated.rb +5 -8
- data/lib/bindata/dsl.rb +167 -170
- data/lib/bindata/float.rb +0 -4
- data/lib/bindata/int.rb +1 -6
- data/lib/bindata/lazy.rb +8 -9
- data/lib/bindata/primitive.rb +4 -4
- data/lib/bindata/record.rb +36 -42
- data/lib/bindata/registry.rb +5 -4
- data/lib/bindata/rest.rb +0 -2
- data/lib/bindata/sanitize.rb +180 -194
- data/lib/bindata/skip.rb +0 -2
- data/lib/bindata/string.rb +3 -3
- data/lib/bindata/stringz.rb +2 -2
- data/lib/bindata/struct.rb +104 -47
- data/lib/bindata/wrapper.rb +22 -9
- data/manual.haml +74 -41
- data/manual.md +223 -123
- data/spec/array_spec.rb +20 -0
- data/spec/base_primitive_spec.rb +10 -0
- data/spec/choice_spec.rb +20 -6
- data/spec/deprecated_spec.rb +8 -0
- data/spec/example.rb +0 -2
- data/spec/primitive_spec.rb +19 -3
- data/spec/record_spec.rb +23 -7
- data/spec/registry_spec.rb +12 -13
- data/spec/string_spec.rb +39 -0
- data/spec/struct_spec.rb +35 -0
- data/spec/wrapper_spec.rb +10 -0
- data/tasks/rspec.rake +1 -1
- metadata +6 -6
- data/GPL +0 -339
data/lib/bindata/float.rb
CHANGED
@@ -60,25 +60,21 @@ module BinData
|
|
60
60
|
|
61
61
|
# Single precision floating point number in little endian format
|
62
62
|
class FloatLe < BinData::BasePrimitive
|
63
|
-
register_self
|
64
63
|
FloatingPoint.define_methods(self, :single, :little)
|
65
64
|
end
|
66
65
|
|
67
66
|
# Single precision floating point number in big endian format
|
68
67
|
class FloatBe < BinData::BasePrimitive
|
69
|
-
register_self
|
70
68
|
FloatingPoint.define_methods(self, :single, :big)
|
71
69
|
end
|
72
70
|
|
73
71
|
# Double precision floating point number in little endian format
|
74
72
|
class DoubleLe < BinData::BasePrimitive
|
75
|
-
register_self
|
76
73
|
FloatingPoint.define_methods(self, :double, :little)
|
77
74
|
end
|
78
75
|
|
79
76
|
# Double precision floating point number in big endian format
|
80
77
|
class DoubleBe < BinData::BasePrimitive
|
81
|
-
register_self
|
82
78
|
FloatingPoint.define_methods(self, :double, :big)
|
83
79
|
end
|
84
80
|
end
|
data/lib/bindata/int.rb
CHANGED
@@ -11,7 +11,6 @@ module BinData
|
|
11
11
|
unless BinData.const_defined?(name)
|
12
12
|
BinData.module_eval <<-END
|
13
13
|
class #{name} < BinData::BasePrimitive
|
14
|
-
register_self
|
15
14
|
Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
|
16
15
|
end
|
17
16
|
END
|
@@ -81,9 +80,7 @@ module BinData
|
|
81
80
|
end
|
82
81
|
|
83
82
|
def create_uint2int_code(nbits)
|
84
|
-
|
85
|
-
|
86
|
-
"val = -(((~val) & #{mask}) + 1) if (val >= #{1 << (nbits - 1)})"
|
83
|
+
"val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
|
87
84
|
end
|
88
85
|
|
89
86
|
def create_read_code(nbits, endian)
|
@@ -151,13 +148,11 @@ module BinData
|
|
151
148
|
|
152
149
|
# Unsigned 1 byte integer.
|
153
150
|
class Uint8 < BinData::BasePrimitive
|
154
|
-
register_self
|
155
151
|
Int.define_methods(self, 8, :little, :unsigned)
|
156
152
|
end
|
157
153
|
|
158
154
|
# Signed 1 byte integer.
|
159
155
|
class Int8 < BinData::BasePrimitive
|
160
|
-
register_self
|
161
156
|
Int.define_methods(self, 8, :little, :signed)
|
162
157
|
end
|
163
158
|
|
data/lib/bindata/lazy.rb
CHANGED
@@ -22,10 +22,10 @@ module BinData
|
|
22
22
|
class << self
|
23
23
|
# Lazily evaluates +val+ in the context of +obj+, with possibility of
|
24
24
|
# +overrides+.
|
25
|
-
def eval(obj, val, overrides =
|
25
|
+
def eval(obj, val, overrides = nil)
|
26
26
|
if can_eval?(val)
|
27
|
-
env = self.new(obj
|
28
|
-
env.lazy_eval(val)
|
27
|
+
env = self.new(obj)
|
28
|
+
env.lazy_eval(val, overrides)
|
29
29
|
else
|
30
30
|
val
|
31
31
|
end
|
@@ -41,13 +41,12 @@ module BinData
|
|
41
41
|
|
42
42
|
# Creates a new evaluator. All lazy evaluation is performed in the
|
43
43
|
# context of +obj+.
|
44
|
-
|
45
|
-
def initialize(obj, overrides = {})
|
44
|
+
def initialize(obj)
|
46
45
|
@obj = obj
|
47
|
-
@overrides = overrides
|
48
46
|
end
|
49
47
|
|
50
|
-
def lazy_eval(val)
|
48
|
+
def lazy_eval(val, overrides = nil)
|
49
|
+
@overrides = overrides if overrides
|
51
50
|
if val.is_a? Symbol
|
52
51
|
__send__(val)
|
53
52
|
elsif val.respond_to? :arity
|
@@ -69,7 +68,7 @@ module BinData
|
|
69
68
|
# Returns the index of this data object inside it's nearest container
|
70
69
|
# array.
|
71
70
|
def index
|
72
|
-
return @overrides[:index] if @overrides.has_key?(:index)
|
71
|
+
return @overrides[:index] if @overrides and @overrides.has_key?(:index)
|
73
72
|
|
74
73
|
child = @obj
|
75
74
|
parent = @obj.parent
|
@@ -84,7 +83,7 @@ module BinData
|
|
84
83
|
end
|
85
84
|
|
86
85
|
def method_missing(symbol, *args)
|
87
|
-
return @overrides[symbol] if @overrides.has_key?(symbol)
|
86
|
+
return @overrides[symbol] if @overrides and @overrides.has_key?(symbol)
|
88
87
|
|
89
88
|
if @obj.parent
|
90
89
|
eval_symbol_in_parent_context(symbol, args)
|
data/lib/bindata/primitive.rb
CHANGED
@@ -62,12 +62,12 @@ module BinData
|
|
62
62
|
class Primitive < BasePrimitive
|
63
63
|
include DSLMixin
|
64
64
|
|
65
|
-
|
66
|
-
dsl_parser :
|
65
|
+
unregister_self
|
66
|
+
dsl_parser :primitive
|
67
67
|
|
68
68
|
class << self
|
69
|
-
def sanitize_parameters!(params
|
70
|
-
params[:struct_params] =
|
69
|
+
def sanitize_parameters!(params) #:nodoc:
|
70
|
+
params[:struct_params] = params.create_sanitized_params(dsl_params, BinData::Struct)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
data/lib/bindata/record.rb
CHANGED
@@ -3,25 +3,39 @@ require 'bindata/sanitize'
|
|
3
3
|
require 'bindata/struct'
|
4
4
|
|
5
5
|
module BinData
|
6
|
+
# Extracts args for Records.
|
7
|
+
#
|
8
|
+
# Foo.new(:bar => "baz) is ambiguous as to whether :bar is a value or parameter.
|
9
|
+
#
|
10
|
+
# BaseArgExtractor always assumes :bar is parameter. This extractor correctly
|
11
|
+
# identifies it as value or parameter.
|
6
12
|
class RecordArgExtractor
|
7
|
-
|
8
|
-
|
13
|
+
class << self
|
14
|
+
def extract(the_class, the_args)
|
15
|
+
value, parameters, parent = BaseArgExtractor.extract(the_class, the_args)
|
9
16
|
|
10
|
-
|
11
|
-
if field_names_in_parameters(the_class, parameters).length > 0
|
17
|
+
if parameters_is_value?(the_class, value, parameters)
|
12
18
|
value = parameters
|
13
|
-
parameters =
|
19
|
+
parameters = {}
|
14
20
|
end
|
21
|
+
|
22
|
+
[value, parameters, parent]
|
15
23
|
end
|
16
24
|
|
17
|
-
|
18
|
-
|
25
|
+
def parameters_is_value?(the_class, value, parameters)
|
26
|
+
if value.nil? and parameters.length > 0
|
27
|
+
field_names_in_parameters?(the_class, parameters)
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
19
32
|
|
20
|
-
|
21
|
-
|
22
|
-
|
33
|
+
def field_names_in_parameters?(the_class, parameters)
|
34
|
+
field_names = the_class.fields.field_names
|
35
|
+
param_keys = parameters.keys
|
23
36
|
|
24
|
-
|
37
|
+
(field_names & param_keys).length > 0
|
38
|
+
end
|
25
39
|
end
|
26
40
|
end
|
27
41
|
|
@@ -30,7 +44,7 @@ module BinData
|
|
30
44
|
# require 'bindata'
|
31
45
|
#
|
32
46
|
# class SomeDataType < BinData::Record
|
33
|
-
# hide
|
47
|
+
# hide :a
|
34
48
|
#
|
35
49
|
# int32le :a
|
36
50
|
# int16le :b
|
@@ -45,30 +59,11 @@ module BinData
|
|
45
59
|
# obj.field_names =># ["b", "s"]
|
46
60
|
# obj.s.field_names =># ["x", "y", "z"]
|
47
61
|
#
|
48
|
-
#
|
49
|
-
# == Parameters
|
50
|
-
#
|
51
|
-
# Parameters may be provided at initialisation to control the behaviour of
|
52
|
-
# an object. These params are:
|
53
|
-
#
|
54
|
-
# <tt>:fields</tt>:: An array specifying the fields for this struct.
|
55
|
-
# Each element of the array is of the form [type, name,
|
56
|
-
# params]. Type is a symbol representing a registered
|
57
|
-
# type. Name is the name of this field. Params is an
|
58
|
-
# optional hash of parameters to pass to this field
|
59
|
-
# when instantiating it.
|
60
|
-
# <tt>:hide</tt>:: A list of the names of fields that are to be hidden
|
61
|
-
# from the outside world. Hidden fields don't appear
|
62
|
-
# in #snapshot or #field_names but are still accessible
|
63
|
-
# by name.
|
64
|
-
# <tt>:endian</tt>:: Either :little or :big. This specifies the default
|
65
|
-
# endian of any numerics in this struct, or in any
|
66
|
-
# nested data objects.
|
67
62
|
class Record < BinData::Struct
|
68
63
|
include DSLMixin
|
69
64
|
|
70
|
-
|
71
|
-
dsl_parser :
|
65
|
+
unregister_self
|
66
|
+
dsl_parser :struct
|
72
67
|
|
73
68
|
class << self
|
74
69
|
|
@@ -76,12 +71,10 @@ module BinData
|
|
76
71
|
RecordArgExtractor
|
77
72
|
end
|
78
73
|
|
79
|
-
def sanitize_parameters!(params
|
80
|
-
params
|
81
|
-
params[:endian] = endian unless endian.nil?
|
82
|
-
params[:hide] = hide unless hide.empty?
|
74
|
+
def sanitize_parameters!(params) #:nodoc:
|
75
|
+
params.merge!(dsl_params)
|
83
76
|
|
84
|
-
super(params
|
77
|
+
super(params)
|
85
78
|
|
86
79
|
define_field_accessors(params[:fields].fields)
|
87
80
|
end
|
@@ -92,8 +85,9 @@ module BinData
|
|
92
85
|
def define_field_accessors(fields) #:nodoc:
|
93
86
|
unless method_defined?(:bindata_defined_accessors_for_fields?)
|
94
87
|
fields.each_with_index do |field, i|
|
95
|
-
|
96
|
-
|
88
|
+
name = field.name_as_sym
|
89
|
+
if name
|
90
|
+
define_field_accessors_for(name, i)
|
97
91
|
end
|
98
92
|
end
|
99
93
|
|
@@ -102,11 +96,11 @@ module BinData
|
|
102
96
|
end
|
103
97
|
|
104
98
|
def define_field_accessors_for(name, index)
|
105
|
-
define_method(name
|
99
|
+
define_method(name) do
|
106
100
|
instantiate_obj_at(index) unless @field_objs[index]
|
107
101
|
@field_objs[index]
|
108
102
|
end
|
109
|
-
define_method(
|
103
|
+
define_method(name.to_s + "=") do |*vals|
|
110
104
|
instantiate_obj_at(index) unless @field_objs[index]
|
111
105
|
@field_objs[index].assign(*vals)
|
112
106
|
end
|
data/lib/bindata/registry.rb
CHANGED
@@ -24,6 +24,11 @@ module BinData
|
|
24
24
|
@registry[formatted_name] = class_to_register
|
25
25
|
end
|
26
26
|
|
27
|
+
def unregister(name)
|
28
|
+
formatted_name = lookup_key(name)
|
29
|
+
@registry.delete(formatted_name)
|
30
|
+
end
|
31
|
+
|
27
32
|
def lookup(name, endian = nil)
|
28
33
|
key = lookup_key(name, endian)
|
29
34
|
try_registering_key(key) unless @registry.has_key?(key)
|
@@ -37,10 +42,6 @@ module BinData
|
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
40
|
-
def is_registered?(name, endian = nil)
|
41
|
-
lookup(name, endian) != nil
|
42
|
-
end
|
43
|
-
|
44
45
|
# Convert CamelCase +name+ to underscore style.
|
45
46
|
def underscore_name(name)
|
46
47
|
name.to_s.sub(/.*::/, "").
|
data/lib/bindata/rest.rb
CHANGED
data/lib/bindata/sanitize.rb
CHANGED
@@ -2,281 +2,267 @@ require 'bindata/registry'
|
|
2
2
|
|
3
3
|
module BinData
|
4
4
|
|
5
|
-
#
|
6
|
-
|
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.
|
16
|
-
class SanitizedParameters < Hash
|
5
|
+
# Subclasses of this are sanitized
|
6
|
+
class SanitizedParameter; end
|
17
7
|
|
18
|
-
|
19
|
-
|
8
|
+
class SanitizedPrototype < SanitizedParameter
|
9
|
+
def initialize(obj_type, obj_params, endian)
|
10
|
+
endian = endian.endian if endian.respond_to? :endian
|
11
|
+
obj_params ||= {}
|
20
12
|
|
21
|
-
@
|
22
|
-
@
|
23
|
-
ensure_no_nil_values
|
13
|
+
@obj_class = RegisteredClasses.lookup(obj_type, endian)
|
14
|
+
@obj_params = SanitizedParameters.new(obj_params, @obj_class, endian)
|
24
15
|
end
|
25
16
|
|
26
|
-
|
17
|
+
def instantiate(value = nil, parent = nil)
|
18
|
+
@factory ||= @obj_class.new(@obj_params)
|
27
19
|
|
28
|
-
|
29
|
-
|
20
|
+
@factory.new(value, parent)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
#----------------------------------------------------------------------------
|
30
24
|
|
31
|
-
|
25
|
+
class SanitizedField < SanitizedParameter
|
26
|
+
def initialize(name, field_type, field_params, endian)
|
27
|
+
@name = name
|
28
|
+
@prototype = SanitizedPrototype.new(field_type, field_params, endian)
|
32
29
|
end
|
33
30
|
|
34
|
-
|
35
|
-
|
31
|
+
attr_reader :prototype
|
32
|
+
|
33
|
+
def name_as_sym
|
34
|
+
@name.nil? ? nil : @name.to_sym
|
36
35
|
end
|
37
36
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
37
|
+
def name
|
38
|
+
@name
|
39
|
+
end
|
41
40
|
|
42
|
-
|
41
|
+
def instantiate(value = nil, parent = nil)
|
42
|
+
@prototype.instantiate(value, parent)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
#----------------------------------------------------------------------------
|
43
46
|
|
44
|
-
|
45
|
-
|
47
|
+
class SanitizedFields < SanitizedParameter
|
48
|
+
def initialize(endian)
|
49
|
+
@fields = []
|
50
|
+
@endian = endian
|
51
|
+
end
|
52
|
+
attr_reader :fields
|
46
53
|
|
47
|
-
|
48
|
-
|
54
|
+
def add_field(type, name, params)
|
55
|
+
name = nil if name == ""
|
56
|
+
|
57
|
+
@fields << SanitizedField.new(name, type, params, @endian)
|
49
58
|
end
|
50
59
|
|
51
|
-
def
|
52
|
-
|
53
|
-
unused_keys = keys - @the_class.accepted_parameters.all
|
54
|
-
unused_keys.each do |key|
|
55
|
-
dest[key] = delete(key)
|
56
|
-
end
|
57
|
-
end
|
60
|
+
def [](idx)
|
61
|
+
@fields[idx]
|
58
62
|
end
|
59
63
|
|
60
|
-
def
|
61
|
-
|
62
|
-
warn ":#{bad_key} is not used with #{@the_class}. " +
|
63
|
-
"You probably want to change this to :#{suggested_key}"
|
64
|
-
end
|
64
|
+
def empty?
|
65
|
+
@fields.empty?
|
65
66
|
end
|
66
67
|
|
67
|
-
|
68
|
-
|
68
|
+
def length
|
69
|
+
@fields.length
|
70
|
+
end
|
69
71
|
|
70
|
-
def
|
71
|
-
each
|
72
|
-
if value.nil?
|
73
|
-
raise ArgumentError,
|
74
|
-
"parameter '#{key}' has nil value in #{@the_class}"
|
75
|
-
end
|
76
|
-
end
|
72
|
+
def each(&block)
|
73
|
+
@fields.each(&block)
|
77
74
|
end
|
78
75
|
|
79
|
-
def
|
80
|
-
@
|
81
|
-
self[key] ||= value
|
82
|
-
end
|
76
|
+
def collect(&block)
|
77
|
+
@fields.collect(&block)
|
83
78
|
end
|
84
79
|
|
85
|
-
def
|
86
|
-
@
|
87
|
-
unless has_parameter?(key)
|
88
|
-
raise ArgumentError,
|
89
|
-
"parameter '#{key}' must be specified in #{@the_class}"
|
90
|
-
end
|
91
|
-
end
|
80
|
+
def field_names
|
81
|
+
@fields.collect { |field| field.name_as_sym }
|
92
82
|
end
|
93
83
|
|
94
|
-
def
|
95
|
-
|
84
|
+
def has_field_name?(name)
|
85
|
+
@fields.detect { |f| f.name_as_sym == name.to_sym }
|
86
|
+
end
|
96
87
|
|
97
|
-
|
98
|
-
|
99
|
-
raise ArgumentError, "params '#{key1}' and '#{key2}' " +
|
100
|
-
"are mutually exclusive in #{@the_class}"
|
101
|
-
end
|
102
|
-
end
|
88
|
+
def all_field_names_blank?
|
89
|
+
@fields.all? { |f| f.name == nil }
|
103
90
|
end
|
104
91
|
|
92
|
+
def no_field_names_blank?
|
93
|
+
@fields.all? { |f| f.name != nil }
|
94
|
+
end
|
95
|
+
|
96
|
+
def copy_fields(other)
|
97
|
+
@fields.concat(other.fields)
|
98
|
+
end
|
105
99
|
end
|
106
100
|
#----------------------------------------------------------------------------
|
107
101
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
class << self
|
115
|
-
# Sanitize +params+ for +the_class+.
|
116
|
-
# Returns sanitized parameters.
|
117
|
-
def sanitize(params, the_class)
|
118
|
-
if params.is_a?(SanitizedParameters) and params.all_sanitized?
|
119
|
-
params
|
102
|
+
class SanitizedChoices < SanitizedParameter
|
103
|
+
def initialize(choices, endian)
|
104
|
+
@choices = {}
|
105
|
+
choices.each_pair do |key, val|
|
106
|
+
if SanitizedParameter === val
|
107
|
+
prototype = val
|
120
108
|
else
|
121
|
-
|
122
|
-
|
109
|
+
type, param = val
|
110
|
+
prototype = SanitizedPrototype.new(type, param, endian)
|
123
111
|
end
|
112
|
+
|
113
|
+
@choices[key] = prototype
|
124
114
|
end
|
125
115
|
end
|
126
116
|
|
127
|
-
def
|
128
|
-
@
|
117
|
+
def [](key)
|
118
|
+
@choices[key]
|
129
119
|
end
|
120
|
+
end
|
121
|
+
#----------------------------------------------------------------------------
|
130
122
|
|
131
|
-
|
132
|
-
|
133
|
-
|
123
|
+
class SanitizedBigEndian < SanitizedParameter
|
124
|
+
def endian
|
125
|
+
:big
|
126
|
+
end
|
127
|
+
end
|
134
128
|
|
135
|
-
|
129
|
+
class SanitizedLittleEndian < SanitizedParameter
|
130
|
+
def endian
|
131
|
+
:little
|
136
132
|
end
|
133
|
+
end
|
134
|
+
#----------------------------------------------------------------------------
|
137
135
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
136
|
+
# BinData objects are instantiated with parameters to determine their
|
137
|
+
# behaviour. These parameters must be sanitized to ensure their values
|
138
|
+
# are valid. When instantiating many objects with identical parameters,
|
139
|
+
# such as an array of records, there is much duplicated sanitizing.
|
140
|
+
#
|
141
|
+
# The purpose of the sanitizing code is to eliminate the duplicated
|
142
|
+
# validation.
|
143
|
+
#
|
144
|
+
# SanitizedParameters is a hash-like collection of parameters. Its purpose
|
145
|
+
# is to recursively sanitize the parameters of an entire BinData object chain
|
146
|
+
# at a single time.
|
147
|
+
class SanitizedParameters < Hash
|
148
|
+
|
149
|
+
# Memoized constants
|
150
|
+
BIG_ENDIAN = SanitizedBigEndian.new
|
151
|
+
LITTLE_ENDIAN = SanitizedLittleEndian.new
|
152
|
+
|
153
|
+
class << self
|
154
|
+
def sanitize(parameters, the_class)
|
155
|
+
if SanitizedParameters === parameters
|
156
|
+
parameters
|
157
|
+
else
|
158
|
+
SanitizedParameters.new(parameters, the_class, nil)
|
159
|
+
end
|
146
160
|
end
|
147
161
|
end
|
148
162
|
|
149
|
-
def
|
150
|
-
|
151
|
-
end
|
163
|
+
def initialize(parameters, the_class, endian)
|
164
|
+
parameters.each_pair { |key, value| self[key.to_sym] = value }
|
152
165
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
166
|
+
@the_class = the_class
|
167
|
+
@endian = endian
|
168
|
+
|
169
|
+
sanitize!
|
157
170
|
end
|
158
171
|
|
159
|
-
|
160
|
-
|
172
|
+
alias_method :has_parameter?, :has_key?
|
173
|
+
|
174
|
+
def needs_sanitizing?(key)
|
175
|
+
parameter = self[key]
|
176
|
+
|
177
|
+
parameter and not parameter.is_a?(SanitizedParameter)
|
161
178
|
end
|
162
179
|
|
163
|
-
def
|
164
|
-
if
|
165
|
-
|
166
|
-
|
167
|
-
yield
|
168
|
-
@endian = saved_endian
|
169
|
-
else
|
170
|
-
yield
|
180
|
+
def warn_replacement_parameter(bad_key, suggested_key)
|
181
|
+
if has_parameter?(bad_key)
|
182
|
+
warn ":#{bad_key} is not used with #{@the_class}. " +
|
183
|
+
"You probably want to change this to :#{suggested_key}"
|
171
184
|
end
|
172
185
|
end
|
173
186
|
|
174
|
-
def
|
175
|
-
|
187
|
+
def endian
|
188
|
+
@endian || self[:endian]
|
176
189
|
end
|
190
|
+
attr_writer :endian
|
177
191
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
params
|
192
|
+
def create_sanitized_endian(endian)
|
193
|
+
if endian == :big
|
194
|
+
BIG_ENDIAN
|
195
|
+
elsif endian == :little
|
196
|
+
LITTLE_ENDIAN
|
184
197
|
else
|
185
|
-
|
198
|
+
raise ArgumentError, "unknown value for endian '#{endian}'"
|
186
199
|
end
|
187
200
|
end
|
188
|
-
end
|
189
|
-
#----------------------------------------------------------------------------
|
190
|
-
|
191
|
-
class SanitizedParameter; end
|
192
201
|
|
193
|
-
|
194
|
-
|
195
|
-
sanitizer.with_endian(endian) do
|
196
|
-
@obj_class = sanitizer.lookup_class(obj_type)
|
197
|
-
@obj_params = sanitizer.create_sanitized_params(obj_params, @obj_class)
|
198
|
-
end
|
202
|
+
def create_sanitized_params(params, the_class)
|
203
|
+
SanitizedParameters.new(params, the_class, self.endian)
|
199
204
|
end
|
200
205
|
|
201
|
-
def
|
202
|
-
|
203
|
-
|
204
|
-
@factory.new(value, parent)
|
206
|
+
def create_sanitized_choices(choices)
|
207
|
+
SanitizedChoices.new(choices, self.endian)
|
205
208
|
end
|
206
|
-
end
|
207
|
-
#----------------------------------------------------------------------------
|
208
209
|
|
209
|
-
|
210
|
-
|
211
|
-
@name = (name != nil and name != "") ? name.to_s : nil
|
212
|
-
@prototype = sanitizer.create_sanitized_object_prototype(field_type, field_params, endian)
|
210
|
+
def create_sanitized_fields
|
211
|
+
SanitizedFields.new(self.endian)
|
213
212
|
end
|
214
|
-
attr_reader :name
|
215
213
|
|
216
|
-
def
|
217
|
-
|
214
|
+
def create_sanitized_object_prototype(obj_type, obj_params)
|
215
|
+
SanitizedPrototype.new(obj_type, obj_params, self.endian)
|
218
216
|
end
|
219
|
-
end
|
220
|
-
#----------------------------------------------------------------------------
|
221
217
|
|
222
|
-
|
223
|
-
|
224
|
-
@sanitizer = sanitizer
|
225
|
-
@fields = []
|
226
|
-
@field_names = nil
|
227
|
-
end
|
228
|
-
attr_reader :fields
|
218
|
+
#---------------
|
219
|
+
private
|
229
220
|
|
230
|
-
def
|
231
|
-
|
232
|
-
|
233
|
-
end
|
221
|
+
def sanitize!
|
222
|
+
ensure_no_nil_values
|
223
|
+
merge_default_parameters!
|
234
224
|
|
235
|
-
|
236
|
-
@fields[idx]
|
237
|
-
end
|
225
|
+
@the_class.sanitize_parameters!(self)
|
238
226
|
|
239
|
-
|
240
|
-
|
227
|
+
ensure_mandatory_parameters_exist
|
228
|
+
ensure_mutual_exclusion_of_parameters
|
241
229
|
end
|
242
230
|
|
243
|
-
def
|
244
|
-
|
245
|
-
|
231
|
+
def ensure_no_nil_values
|
232
|
+
each do |key, value|
|
233
|
+
if value.nil?
|
234
|
+
raise ArgumentError,
|
235
|
+
"parameter '#{key}' has nil value in #{@the_class}"
|
236
|
+
end
|
237
|
+
end
|
246
238
|
end
|
247
239
|
|
248
|
-
def
|
249
|
-
@
|
250
|
-
|
240
|
+
def merge_default_parameters!
|
241
|
+
@the_class.default_parameters.each do |key, value|
|
242
|
+
self[key] ||= value
|
243
|
+
end
|
251
244
|
end
|
252
|
-
end
|
253
|
-
#----------------------------------------------------------------------------
|
254
245
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
@choices[key] = prototype
|
246
|
+
def ensure_mandatory_parameters_exist
|
247
|
+
@the_class.mandatory_parameters.each do |key|
|
248
|
+
unless has_parameter?(key)
|
249
|
+
raise ArgumentError,
|
250
|
+
"parameter '#{key}' must be specified in #{@the_class}"
|
251
|
+
end
|
262
252
|
end
|
263
253
|
end
|
264
254
|
|
265
|
-
def
|
266
|
-
|
267
|
-
end
|
268
|
-
end
|
269
|
-
#----------------------------------------------------------------------------
|
255
|
+
def ensure_mutual_exclusion_of_parameters
|
256
|
+
return if length < 2
|
270
257
|
|
271
|
-
|
272
|
-
|
273
|
-
|
258
|
+
@the_class.mutually_exclusive_parameters.each do |key1, key2|
|
259
|
+
if has_parameter?(key1) and has_parameter?(key2)
|
260
|
+
raise ArgumentError, "params '#{key1}' and '#{key2}' " +
|
261
|
+
"are mutually exclusive in #{@the_class}"
|
262
|
+
end
|
263
|
+
end
|
274
264
|
end
|
275
265
|
end
|
266
|
+
#----------------------------------------------------------------------------
|
276
267
|
|
277
|
-
class SanitizedLittleEndian < SanitizedParameter
|
278
|
-
def endian
|
279
|
-
:little
|
280
|
-
end
|
281
|
-
end
|
282
268
|
end
|