bindata 0.9.3 → 0.10.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.

Files changed (47) hide show
  1. data/ChangeLog +18 -0
  2. data/NEWS +59 -0
  3. data/README +22 -23
  4. data/TODO +18 -12
  5. data/examples/gzip.rb +4 -4
  6. data/lib/bindata.rb +4 -3
  7. data/lib/bindata/array.rb +202 -132
  8. data/lib/bindata/base.rb +147 -166
  9. data/lib/bindata/{single.rb → base_primitive.rb} +82 -56
  10. data/lib/bindata/bits.rb +31 -770
  11. data/lib/bindata/choice.rb +157 -82
  12. data/lib/bindata/float.rb +25 -27
  13. data/lib/bindata/int.rb +144 -177
  14. data/lib/bindata/io.rb +59 -49
  15. data/lib/bindata/lazy.rb +80 -50
  16. data/lib/bindata/params.rb +134 -26
  17. data/lib/bindata/{single_value.rb → primitive.rb} +71 -64
  18. data/lib/bindata/{multi_value.rb → record.rb} +52 -70
  19. data/lib/bindata/registry.rb +49 -17
  20. data/lib/bindata/rest.rb +6 -10
  21. data/lib/bindata/sanitize.rb +55 -70
  22. data/lib/bindata/string.rb +60 -42
  23. data/lib/bindata/stringz.rb +34 -35
  24. data/lib/bindata/struct.rb +197 -152
  25. data/lib/bindata/trace.rb +35 -0
  26. data/spec/array_spec.rb +128 -112
  27. data/spec/{single_spec.rb → base_primitive_spec.rb} +102 -61
  28. data/spec/base_spec.rb +190 -185
  29. data/spec/bits_spec.rb +126 -98
  30. data/spec/choice_spec.rb +89 -98
  31. data/spec/example.rb +19 -0
  32. data/spec/float_spec.rb +28 -44
  33. data/spec/int_spec.rb +217 -127
  34. data/spec/io_spec.rb +41 -24
  35. data/spec/lazy_spec.rb +95 -49
  36. data/spec/primitive_spec.rb +191 -0
  37. data/spec/{multi_value_spec.rb → record_spec.rb} +124 -89
  38. data/spec/registry_spec.rb +53 -12
  39. data/spec/rest_spec.rb +2 -3
  40. data/spec/sanitize_spec.rb +47 -73
  41. data/spec/spec_common.rb +13 -1
  42. data/spec/string_spec.rb +34 -23
  43. data/spec/stringz_spec.rb +10 -18
  44. data/spec/struct_spec.rb +91 -63
  45. data/spec/system_spec.rb +291 -0
  46. metadata +12 -8
  47. data/spec/single_value_spec.rb +0 -131
@@ -1,18 +1,19 @@
1
- require 'bindata/single'
1
+ require 'bindata/base_primitive'
2
+ require 'bindata/registry'
2
3
  require 'bindata/struct'
3
4
 
4
5
  module BinData
5
- # A SingleValue is a declarative way to define a new BinData data type.
6
- # The data type must contain a single value only. For new data types
7
- # that contain multiple values see BinData::MultiValue.
6
+ # A Primitive is a declarative way to define a new BinData data type.
7
+ # The data type must contain a primitive value only, i.e numbers or strings.
8
+ # For new data types that contain multiple values see BinData::Record.
8
9
  #
9
- # To define a new data type, set fields as if for MultiValue and add a
10
+ # To define a new data type, set fields as if for Record and add a
10
11
  # #get and #set method to extract / convert the data between the fields
11
12
  # and the #value of the object.
12
13
  #
13
14
  # require 'bindata'
14
15
  #
15
- # class PascalString < BinData::SingleValue
16
+ # class PascalString < BinData::Primitive
16
17
  # uint8 :len, :value => lambda { data.length }
17
18
  # string :data, :read_length => :len
18
19
  #
@@ -26,12 +27,12 @@ module BinData
26
27
  # end
27
28
  #
28
29
  # ps = PascalString.new(:initial_value => "hello")
29
- # ps.to_s #=> "\005hello"
30
+ # ps.to_binary_s #=> "\005hello"
30
31
  # ps.read("\003abcde")
31
32
  # ps.value #=> "abc"
32
33
  #
33
34
  # # Unsigned 24 bit big endian integer
34
- # class Uint24be < BinData::SingleValue
35
+ # class Uint24be < BinData::Primitive
35
36
  # uint8 :byte1
36
37
  # uint8 :byte2
37
38
  # uint8 :byte3
@@ -56,100 +57,93 @@ module BinData
56
57
  #
57
58
  # == Parameters
58
59
  #
59
- # SingleValue objects accept all the parameters that BinData::Single do.
60
+ # Primitive objects accept all the parameters that BinData::BasePrimitive do.
60
61
  #
61
- class SingleValue < Single
62
+ class Primitive < BasePrimitive
62
63
 
63
64
  class << self
64
65
 
65
- # Register the names of all subclasses of this class.
66
66
  def inherited(subclass) #:nodoc:
67
+ # Register the names of all subclasses of this class.
67
68
  register(subclass.name, subclass)
68
69
  end
69
70
 
70
- # Can this data object self reference itself?
71
71
  def recursive?
72
+ # A Primitive can possibly self reference itself.
72
73
  true
73
74
  end
74
75
 
75
- # Returns or sets the endianess of numerics used in this stucture.
76
- # Endianess is applied to the fields of this structure.
77
- # Valid values are :little and :big.
78
76
  def endian(endian = nil)
79
77
  @endian ||= nil
80
78
  if [:little, :big].include?(endian)
81
79
  @endian = endian
82
80
  elsif endian != nil
83
- raise ArgumentError, "unknown value for endian '#{endian}'"
81
+ raise ArgumentError, "unknown value for endian '#{endian}'", caller(1)
84
82
  end
85
83
  @endian
86
84
  end
87
85
 
88
- # Returns all stored fields. Should only be called by
89
- # #sanitize_parameters
90
- def fields
91
- @fields || []
92
- end
93
-
94
- # Used to define fields for the internal structure.
95
86
  def method_missing(symbol, *args)
96
87
  name, params = args
97
88
 
98
89
  type = symbol
99
- name = (name.nil? or name == "") ? nil : name.to_s
90
+ name = name.to_s
100
91
  params ||= {}
101
92
 
102
- # note that fields are stored in an instance variable not a class var
103
- @fields ||= []
104
-
105
- # check that type is known
106
- unless Sanitizer.type_exists?(type, endian)
107
- raise TypeError, "unknown type '#{type}' for #{self}", caller
108
- end
109
-
110
- # check that name is okay
111
- if name != nil
112
- # check for duplicate names
113
- @fields.each do |t, n, p|
114
- if n == name
115
- raise SyntaxError, "duplicate field '#{name}' in #{self}", caller
116
- end
117
- end
93
+ ensure_type_exists(type)
94
+ ensure_valid_name(name) unless name.nil?
118
95
 
119
- # check that name doesn't shadow an existing method
120
- if self.instance_methods.include?(name)
121
- raise NameError.new("", name),
122
- "field '#{name}' shadows an existing method", caller
123
- end
124
- end
125
-
126
- # remember this field. These fields will be recalled upon creating
127
- # an instance of this class
128
- @fields.push([type, name, params])
96
+ append_field(type, name, params)
129
97
  end
130
98
 
131
- # Ensures that +params+ is of the form expected by #initialize.
132
99
  def sanitize_parameters!(sanitizer, params)
133
100
  struct_params = {}
134
- struct_params[:fields] = self.fields
135
- struct_params[:endian] = self.endian unless self.endian.nil?
101
+ struct_params[:fields] = fields
102
+ struct_params[:endian] = endian unless endian.nil?
136
103
 
137
104
  params[:struct_params] = struct_params
138
105
 
139
106
  super(sanitizer, params)
140
107
  end
108
+
109
+ #-------------
110
+ private
111
+
112
+ def ensure_type_exists(type)
113
+ unless RegisteredClasses.is_registered?(type, endian)
114
+ raise TypeError, "unknown type '#{type}' for #{self}", caller(2)
115
+ end
116
+ end
117
+
118
+ def ensure_valid_name(name)
119
+ fields.each do |t, n, p|
120
+ if n == name
121
+ raise SyntaxError, "duplicate field '#{name}' in #{self}", caller(4)
122
+ end
123
+ end
124
+ if self.instance_methods.include?(name)
125
+ raise NameError.new("", name),
126
+ "field '#{name}' shadows an existing method", caller(2)
127
+ end
128
+ end
129
+
130
+ def append_field(type, name, params)
131
+ fields.push([type, name, params])
132
+ end
133
+
134
+ def fields
135
+ @fields ||= []
136
+ end
141
137
  end
142
138
 
143
- # These are the parameters used by this class.
144
- bindata_mandatory_parameter :struct_params
139
+ mandatory_parameter :struct_params
145
140
 
146
141
  def initialize(params = {}, parent = nil)
147
142
  super(params, parent)
148
143
 
149
- @struct = BinData::Struct.new(no_eval_param(:struct_params), self)
144
+ @struct = BinData::Struct.new(get_parameter(:struct_params), self)
150
145
  end
151
146
 
152
- # Forward method calls to the internal struct.
153
147
  def method_missing(symbol, *args, &block)
154
148
  if @struct.respond_to?(symbol)
155
149
  @struct.__send__(symbol, *args, &block)
@@ -158,25 +152,29 @@ module BinData
158
152
  end
159
153
  end
160
154
 
155
+ def debug_name_of(child)
156
+ debug_name + "-internal-"
157
+ end
158
+
159
+ def offset_of(child)
160
+ offset
161
+ end
162
+
161
163
  #---------------
162
164
  private
163
165
 
164
- # Retrieve a sensible default from the internal struct.
165
166
  def sensible_default
166
167
  get
167
168
  end
168
169
 
169
- # Read data into the fields of the internal struct then return the value.
170
- def read_val(io)
170
+ def read_and_return_value(io)
171
171
  @struct.read(io)
172
172
  get
173
173
  end
174
174
 
175
- # Sets +val+ into the fields of the internal struct then returns the
176
- # string representation.
177
- def val_to_str(val)
175
+ def value_to_binary_string(val)
178
176
  set(val)
179
- @struct.to_s
177
+ @struct.to_binary_s
180
178
  end
181
179
 
182
180
  ###########################################################################
@@ -196,4 +194,13 @@ module BinData
196
194
  # To be implemented by subclasses
197
195
  ###########################################################################
198
196
  end
197
+
198
+ class SingleValue < Primitive
199
+ class << self
200
+ def inherited(subclass) #:nodoc:
201
+ warn "BinData::BasePrimitiveValue is deprecated. Replacing with BinData::Primitive"
202
+ super
203
+ end
204
+ end
205
+ end
199
206
  end
@@ -1,18 +1,18 @@
1
- require 'bindata/params'
1
+ require 'bindata/registry'
2
2
  require 'bindata/struct'
3
3
 
4
4
  module BinData
5
- # A MultiValue is a declarative wrapper around Struct.
5
+ # A Record is a declarative wrapper around Struct.
6
6
  #
7
7
  # require 'bindata'
8
8
  #
9
- # class Tuple < BinData::MultiValue
9
+ # class Tuple < BinData::Record
10
10
  # int8 :x
11
11
  # int8 :y
12
12
  # int8 :z
13
13
  # end
14
14
  #
15
- # class SomeDataType < BinData::MultiValue
15
+ # class SomeDataType < BinData::Record
16
16
  # hide 'a'
17
17
  #
18
18
  # int32le :a
@@ -42,50 +42,36 @@ module BinData
42
42
  # <tt>:endian</tt>:: Either :little or :big. This specifies the default
43
43
  # endian of any numerics in this struct, or in any
44
44
  # nested data objects.
45
- class MultiValue < BinData::Struct
45
+ class Record < BinData::Struct
46
46
 
47
47
  class << self
48
- extend Parameters
49
48
 
50
- # Register the names of all subclasses of this class.
51
49
  def inherited(subclass) #:nodoc:
50
+ # Register the names of all subclasses of this class.
52
51
  register(subclass.name, subclass)
53
52
  end
54
53
 
55
- # Can this data object self reference itself?
56
54
  def recursive?
55
+ # A Record can self reference itself.
57
56
  true
58
57
  end
59
58
 
60
- # Returns or sets the endianess of numerics used in this stucture.
61
- # Endianess is applied to the fields of this structure.
62
- # Valid values are :little and :big.
63
59
  def endian(endian = nil)
64
60
  @endian ||= nil
65
61
  if [:little, :big].include?(endian)
66
62
  @endian = endian
67
63
  elsif endian != nil
68
- raise ArgumentError, "unknown value for endian '#{endian}'"
64
+ raise ArgumentError, "unknown value for endian '#{endian}'", caller(1)
69
65
  end
70
66
  @endian
71
67
  end
72
68
 
73
- # Returns the names of any hidden fields in this struct. Any given args
74
- # are appended to the hidden list.
75
69
  def hide(*args)
76
- # note that fields are stored in an instance variable not a class var
77
70
  @hide ||= []
78
71
  @hide.concat(args.collect { |name| name.to_s })
79
72
  @hide
80
73
  end
81
74
 
82
- # Returns all stored fields.
83
- # Should only be called by #sanitize_parameters
84
- def fields
85
- @fields ||= []
86
- end
87
-
88
- # Used to define fields for this structure.
89
75
  def method_missing(symbol, *args)
90
76
  name, params = args
91
77
 
@@ -93,78 +79,74 @@ module BinData
93
79
  name = name.to_s
94
80
  params ||= {}
95
81
 
96
- # note that fields are stored in an instance variable not a class var
97
- @fields ||= []
82
+ ensure_type_exists(type)
83
+ ensure_valid_name(name)
84
+
85
+ append_field(type, name, params)
86
+ end
87
+
88
+ def sanitize_parameters!(sanitizer, params)
89
+ merge_endian!(params)
90
+ merge_fields!(params)
91
+ merge_hide!(params)
92
+
93
+ super(sanitizer, params)
94
+ end
95
+
96
+ #-------------
97
+ private
98
98
 
99
- # check that type is known
100
- unless Sanitizer.type_exists?(type, endian)
101
- raise TypeError, "unknown type '#{type}' for #{self}", caller
99
+ def ensure_type_exists(type)
100
+ unless RegisteredClasses.is_registered?(type, endian)
101
+ raise TypeError, "unknown type '#{type}' for #{self}", caller(2)
102
102
  end
103
+ end
103
104
 
104
- # check for duplicate names
105
+ def ensure_valid_name(name)
106
+ @fields ||= []
105
107
  @fields.each do |t, n, p|
106
108
  if n == name
107
- raise SyntaxError, "duplicate field '#{name}' in #{self}", caller
109
+ raise SyntaxError, "duplicate field '#{name}' in #{self}", caller(4)
108
110
  end
109
111
  end
110
-
111
- # check that name doesn't shadow an existing method
112
112
  if self.instance_methods.include?(name)
113
113
  raise NameError.new("", name),
114
- "field '#{name}' shadows an existing method", caller
114
+ "field '#{name}' shadows an existing method", caller(2)
115
115
  end
116
-
117
- # check that name isn't reserved
118
116
  if self::RESERVED.include?(name)
119
117
  raise NameError.new("", name),
120
- "field '#{name}' is a reserved name", caller
118
+ "field '#{name}' is a reserved name", caller(2)
121
119
  end
120
+ end
122
121
 
123
- # remember this field. These fields will be recalled upon creating
124
- # an instance of this class
122
+ def append_field(type, name, params)
123
+ @fields ||= []
125
124
  @fields.push([type, name, params])
126
125
  end
127
126
 
128
- # Ensures that +params+ is of the form expected by #initialize.
129
- def sanitize_parameters!(sanitizer, params)
127
+ def merge_endian!(params)
130
128
  endian = params[:endian] || self.endian
131
- fields = params[:fields] || self.fields
132
- hide = params[:hide] || self.hide
133
-
134
129
  params[:endian] = endian unless endian.nil?
135
- params[:fields] = fields
136
- params[:hide] = hide
137
-
138
- # add default parameters
139
- default_parameters.each do |k,v|
140
- params[k] = v unless params.has_key?(k)
141
- end
142
-
143
- # ensure mandatory parameters exist
144
- mandatory_parameters.each do |prm|
145
- if not params.has_key?(prm)
146
- raise ArgumentError, "parameter ':#{prm}' must be specified " +
147
- "in #{self}"
148
- end
149
- end
150
-
151
- super(sanitizer, params)
152
130
  end
153
131
 
154
- # Sets the mandatory parameters used by this class.
155
- def mandatory_parameters(*args) ; end
156
-
157
- define_x_parameters(:mandatory, []) do |array, args|
158
- args.each { |arg| array << arg.to_sym }
159
- array.uniq!
132
+ def merge_fields!(params)
133
+ @fields ||= []
134
+ fields = params[:fields] || @fields || []
135
+ params[:fields] = fields
160
136
  end
161
137
 
162
- # Sets the default parameters used by this class.
163
- def default_parameters(params = {}); end
138
+ def merge_hide!(params)
139
+ hide = params[:hide] || self.hide
140
+ params[:hide] = hide
141
+ end
142
+ end
143
+ end
164
144
 
165
- define_x_parameters(:default, {}) do |hash, args|
166
- params = args.length > 0 ? args[0] : {}
167
- hash.merge!(params)
145
+ class MultiValue < Record
146
+ class << self
147
+ def inherited(subclass) #:nodoc:
148
+ warn "BinData::MultiValue is deprecated. Replacing with BinData::Record"
149
+ super
168
150
  end
169
151
  end
170
152
  end
@@ -1,14 +1,34 @@
1
- require 'singleton'
2
-
3
1
  module BinData
4
2
  # This registry contains a register of name -> class mappings.
3
+ #
4
+ # Names are stored in under_score_style, not camelCase.
5
5
  class Registry
6
- include Singleton
7
6
 
8
7
  def initialize
9
8
  @registry = {}
10
9
  end
11
10
 
11
+ def register(name, class_to_register)
12
+ formatted_name = underscore_name(name)
13
+ warn_if_name_is_already_registered(formatted_name, class_to_register)
14
+
15
+ @registry[formatted_name] = class_to_register
16
+ end
17
+
18
+ def lookup(name, endian = nil)
19
+ key = underscore_name(name.to_s)
20
+
21
+ result = @registry[key]
22
+ if result.nil?
23
+ result = @registry[merge_key_and_endian(key, endian)]
24
+ end
25
+ result
26
+ end
27
+
28
+ def is_registered?(name, endian = nil)
29
+ lookup(name, endian) != nil
30
+ end
31
+
12
32
  # Convert camelCase +name+ to underscore style.
13
33
  def underscore_name(name)
14
34
  name.to_s.sub(/.*::/, "").
@@ -18,25 +38,37 @@ module BinData
18
38
  downcase
19
39
  end
20
40
 
21
- # Registers the mapping of +name+ to +klass+. +name+ is converted
22
- # from camelCase to underscore style.
23
- # Returns the converted name
24
- def register(name, klass)
25
- # convert camelCase name to underscore style
26
- key = underscore_name(name)
41
+ #---------------
42
+ private
27
43
 
28
- # warn if replacing an existing class
29
- if $VERBOSE and (existing = @registry[key])
30
- warn "warning: replacing registered class #{existing} with #{klass}"
44
+ def merge_key_and_endian(key, endian)
45
+ result = key
46
+ if endian != nil
47
+ if /^u?int\d+$/ =~ key
48
+ result = key + ((endian == :little) ? "le" : "be")
49
+ elsif /^(float|double)$/ =~ key
50
+ result = key + ((endian == :little) ? "_le" : "_be")
51
+ end
31
52
  end
53
+ result
54
+ end
32
55
 
33
- @registry[key] = klass
34
- key.dup
56
+ def warn_if_name_is_already_registered(name, class_to_register)
57
+ if $VERBOSE and @registry.has_key?(name)
58
+ prev_class = @registry[name]
59
+ warn "warning: replacing registered class #{prev_class} " +
60
+ "with #{class_to_register}"
61
+ end
35
62
  end
63
+ end
64
+
65
+ # A singleton registry of all registered classes.
66
+ RegisteredClasses = Registry.new
36
67
 
37
- # Returns the class matching a previously registered +name+.
38
- def lookup(name)
39
- @registry[name.to_s]
68
+ class Registry
69
+ def Registry.instance
70
+ warn "'Registry.instance' is deprecated. Replacing with 'RegisteredClasses'"
71
+ RegisteredClasses
40
72
  end
41
73
  end
42
74
  end