jbangert-bindata 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.gitignore +1 -0
  2. data/BSDL +22 -0
  3. data/COPYING +52 -0
  4. data/ChangeLog.rdoc +204 -0
  5. data/Gemfile +2 -0
  6. data/INSTALL +11 -0
  7. data/NEWS.rdoc +164 -0
  8. data/README.md +54 -0
  9. data/Rakefile +13 -0
  10. data/bindata.gemspec +31 -0
  11. data/doc/manual.haml +407 -0
  12. data/doc/manual.md +1649 -0
  13. data/examples/NBT.txt +149 -0
  14. data/examples/gzip.rb +161 -0
  15. data/examples/ip_address.rb +22 -0
  16. data/examples/list.rb +124 -0
  17. data/examples/nbt.rb +178 -0
  18. data/lib/bindata.rb +33 -0
  19. data/lib/bindata/alignment.rb +83 -0
  20. data/lib/bindata/array.rb +335 -0
  21. data/lib/bindata/base.rb +388 -0
  22. data/lib/bindata/base_primitive.rb +214 -0
  23. data/lib/bindata/bits.rb +87 -0
  24. data/lib/bindata/choice.rb +216 -0
  25. data/lib/bindata/count_bytes_remaining.rb +35 -0
  26. data/lib/bindata/deprecated.rb +50 -0
  27. data/lib/bindata/dsl.rb +312 -0
  28. data/lib/bindata/float.rb +80 -0
  29. data/lib/bindata/int.rb +184 -0
  30. data/lib/bindata/io.rb +274 -0
  31. data/lib/bindata/lazy.rb +105 -0
  32. data/lib/bindata/offset.rb +91 -0
  33. data/lib/bindata/params.rb +135 -0
  34. data/lib/bindata/primitive.rb +135 -0
  35. data/lib/bindata/record.rb +110 -0
  36. data/lib/bindata/registry.rb +92 -0
  37. data/lib/bindata/rest.rb +35 -0
  38. data/lib/bindata/sanitize.rb +290 -0
  39. data/lib/bindata/skip.rb +48 -0
  40. data/lib/bindata/string.rb +145 -0
  41. data/lib/bindata/stringz.rb +96 -0
  42. data/lib/bindata/struct.rb +388 -0
  43. data/lib/bindata/trace.rb +94 -0
  44. data/lib/bindata/version.rb +3 -0
  45. data/setup.rb +1585 -0
  46. data/spec/alignment_spec.rb +61 -0
  47. data/spec/array_spec.rb +331 -0
  48. data/spec/base_primitive_spec.rb +238 -0
  49. data/spec/base_spec.rb +376 -0
  50. data/spec/bits_spec.rb +163 -0
  51. data/spec/choice_spec.rb +263 -0
  52. data/spec/count_bytes_remaining_spec.rb +38 -0
  53. data/spec/deprecated_spec.rb +31 -0
  54. data/spec/example.rb +21 -0
  55. data/spec/float_spec.rb +37 -0
  56. data/spec/int_spec.rb +216 -0
  57. data/spec/io_spec.rb +352 -0
  58. data/spec/lazy_spec.rb +217 -0
  59. data/spec/primitive_spec.rb +202 -0
  60. data/spec/record_spec.rb +530 -0
  61. data/spec/registry_spec.rb +108 -0
  62. data/spec/rest_spec.rb +26 -0
  63. data/spec/skip_spec.rb +27 -0
  64. data/spec/spec_common.rb +58 -0
  65. data/spec/string_spec.rb +300 -0
  66. data/spec/stringz_spec.rb +118 -0
  67. data/spec/struct_spec.rb +350 -0
  68. data/spec/system_spec.rb +380 -0
  69. data/tasks/manual.rake +36 -0
  70. data/tasks/rspec.rake +17 -0
  71. metadata +208 -0
@@ -0,0 +1,87 @@
1
+ require 'bindata/base_primitive'
2
+
3
+ module BinData
4
+ # Defines a number of classes that contain a bit based integer.
5
+ # The integer is defined by endian and number of bits.
6
+
7
+ module BitField #:nodoc: all
8
+ class << self
9
+ def define_class(nbits, endian)
10
+ name = "Bit#{nbits}"
11
+ name << "le" if endian == :little
12
+ unless BinData.const_defined?(name)
13
+ BinData.module_eval <<-END
14
+ class #{name} < BinData::BasePrimitive
15
+ BitField.define_methods(self, #{nbits}, :#{endian})
16
+ end
17
+ END
18
+ end
19
+
20
+ BinData.const_get(name)
21
+ end
22
+
23
+ def define_methods(bit_class, nbits, endian)
24
+ bit_class.module_eval <<-END
25
+ def assign(val)
26
+ #{create_clamp_code(nbits)}
27
+ super(val)
28
+ end
29
+
30
+ def do_write(io)
31
+ io.writebits(_value, #{nbits}, :#{endian})
32
+ end
33
+
34
+ def do_num_bytes
35
+ #{nbits / 8.0}
36
+ end
37
+
38
+ #---------------
39
+ private
40
+
41
+ def read_and_return_value(io)
42
+ io.readbits(#{nbits}, :#{endian})
43
+ end
44
+
45
+ def sensible_default
46
+ 0
47
+ end
48
+ END
49
+ end
50
+
51
+ def create_clamp_code(nbits)
52
+ min = 0
53
+ max = (1 << nbits) - 1
54
+ clamp = "(val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
55
+
56
+ if nbits == 1
57
+ # allow single bits to be used as booleans
58
+ clamp = "(val == true) ? 1 : (not val) ? 0 : #{clamp}"
59
+ end
60
+
61
+ "val = #{clamp}"
62
+ end
63
+ end
64
+ end
65
+
66
+ # Create classes on demand
67
+ class << self
68
+ alias_method :const_missing_without_bits, :const_missing
69
+ def const_missing_with_bits(name)
70
+ name = name.to_s
71
+ mappings = {
72
+ /^Bit(\d+)$/ => :big,
73
+ /^Bit(\d+)le$/ => :little
74
+ }
75
+
76
+ mappings.each_pair do |regex, endian|
77
+ if regex =~ name
78
+ nbits = $1.to_i
79
+ return BitField.define_class(nbits, endian)
80
+ end
81
+ end
82
+
83
+ const_missing_without_bits(name)
84
+ end
85
+ alias_method :const_missing, :const_missing_with_bits
86
+ end
87
+ end
@@ -0,0 +1,216 @@
1
+ require 'bindata/base'
2
+ require 'bindata/dsl'
3
+
4
+ module BinData
5
+ # A Choice is a collection of data objects of which only one is active
6
+ # at any particular time. Method calls will be delegated to the active
7
+ # choice.
8
+ #
9
+ # require 'bindata'
10
+ #
11
+ # type1 = [:string, {:value => "Type1"}]
12
+ # type2 = [:string, {:value => "Type2"}]
13
+ #
14
+ # choices = {5 => type1, 17 => type2}
15
+ # a = BinData::Choice.new(:choices => choices, :selection => 5)
16
+ # a # => "Type1"
17
+ #
18
+ # choices = [ type1, type2 ]
19
+ # a = BinData::Choice.new(:choices => choices, :selection => 1)
20
+ # a # => "Type2"
21
+ #
22
+ # choices = [ nil, nil, nil, type1, nil, type2 ]
23
+ # a = BinData::Choice.new(:choices => choices, :selection => 3)
24
+ # a # => "Type1"
25
+ #
26
+ #
27
+ # Chooser = Struct.new(:choice)
28
+ # mychoice = Chooser.new
29
+ # mychoice.choice = 'big'
30
+ #
31
+ # choices = {'big' => :uint16be, 'little' => :uint16le}
32
+ # a = BinData::Choice.new(:choices => choices, :copy_on_change => true,
33
+ # :selection => lambda { mychoice.choice })
34
+ # a.assign(256)
35
+ # a.to_binary_s #=> "\001\000"
36
+ #
37
+ # mychoice.choice = 'little'
38
+ # a.to_binary_s #=> "\000\001"
39
+ #
40
+ # == Parameters
41
+ #
42
+ # Parameters may be provided at initialisation to control the behaviour of
43
+ # an object. These params are:
44
+ #
45
+ # <tt>:choices</tt>:: Either an array or a hash specifying the possible
46
+ # data objects. The format of the
47
+ # array/hash.values is a list of symbols
48
+ # representing the data object type. If a choice
49
+ # is to have params passed to it, then it should
50
+ # be provided as [type_symbol, hash_params]. An
51
+ # implementation constraint is that the hash may
52
+ # not contain symbols as keys, with the exception
53
+ # of :default. :default is to be used when then
54
+ # :selection does not exist in the :choices hash.
55
+ # <tt>:selection</tt>:: An index/key into the :choices array/hash which
56
+ # specifies the currently active choice.
57
+ # <tt>:copy_on_change</tt>:: If set to true, copy the value of the previous
58
+ # selection to the current selection whenever the
59
+ # selection changes. Default is false.
60
+ class Choice < BinData::Base
61
+ include DSLMixin
62
+
63
+ dsl_parser :choice
64
+
65
+ mandatory_parameters :choices, :selection
66
+ optional_parameter :copy_on_change
67
+
68
+ class << self
69
+
70
+ def sanitize_parameters!(params) #:nodoc:
71
+ params.merge!(dsl_params)
72
+
73
+ if params.needs_sanitizing?(:choices)
74
+ choices = choices_as_hash(params[:choices])
75
+ ensure_valid_keys(choices)
76
+ params[:choices] = params.create_sanitized_choices(choices)
77
+ end
78
+ end
79
+
80
+ #-------------
81
+ private
82
+
83
+ def choices_as_hash(choices)
84
+ if choices.respond_to?(:to_ary)
85
+ key_array_by_index(choices.to_ary)
86
+ else
87
+ choices
88
+ end
89
+ end
90
+
91
+ def key_array_by_index(array)
92
+ result = {}
93
+ array.each_with_index do |el, i|
94
+ result[i] = el unless el.nil?
95
+ end
96
+ result
97
+ end
98
+
99
+ def ensure_valid_keys(choices)
100
+ if choices.has_key?(nil)
101
+ raise ArgumentError, ":choices hash may not have nil key"
102
+ end
103
+ if choices.keys.detect { |key| key.is_a?(Symbol) and key != :default }
104
+ raise ArgumentError, ":choices hash may not have symbols for keys"
105
+ end
106
+ end
107
+ end
108
+
109
+ def initialize_shared_instance
110
+ if eval_parameter(:copy_on_change) == true
111
+ class << self
112
+ alias_method :hook_after_current_choice, :copy_previous_value
113
+ end
114
+ end
115
+ end
116
+
117
+ def initialize_instance
118
+ @choices = {}
119
+ @last_selection = nil
120
+ end
121
+
122
+ # A convenience method that returns the current selection.
123
+ def selection
124
+ eval_parameter(:selection)
125
+ end
126
+
127
+ def clear #:nodoc:
128
+ initialize_instance
129
+ end
130
+
131
+ def clear? #:nodoc:
132
+ current_choice.clear?
133
+ end
134
+
135
+ def assign(val)
136
+ current_choice.assign(val)
137
+ end
138
+
139
+ def snapshot
140
+ current_choice.snapshot
141
+ end
142
+
143
+ def respond_to?(symbol, include_private = false) #:nodoc:
144
+ current_choice.respond_to?(symbol, include_private) || super
145
+ end
146
+
147
+ def safe_respond_to?(symbol, include_private = false) #:nodoc:
148
+ orig_respond_to?(symbol, include_private)
149
+ end
150
+
151
+ def method_missing(symbol, *args, &block) #:nodoc:
152
+ current_choice.__send__(symbol, *args, &block)
153
+ end
154
+
155
+ def do_read(io) #:nodoc:
156
+ hook_before_do_read
157
+ current_choice.do_read(io)
158
+ end
159
+
160
+ def do_write(io) #:nodoc:
161
+ current_choice.do_write(io)
162
+ end
163
+
164
+ def do_num_bytes #:nodoc:
165
+ current_choice.do_num_bytes
166
+ end
167
+
168
+ #---------------
169
+ private
170
+
171
+ def hook_before_do_read; end
172
+ def hook_after_current_choice(*args); end
173
+
174
+ def current_choice
175
+ selection = eval_parameter(:selection)
176
+ if selection.nil?
177
+ raise IndexError, ":selection returned nil for #{debug_name}"
178
+ end
179
+
180
+ obj = get_or_instantiate_choice(selection)
181
+ hook_after_current_choice(selection, obj)
182
+
183
+ obj
184
+ end
185
+
186
+ def get_or_instantiate_choice(selection)
187
+ @choices[selection] ||= instantiate_choice(selection)
188
+ end
189
+
190
+ def instantiate_choice(selection)
191
+ prototype = get_parameter(:choices)[selection]
192
+ if prototype.nil?
193
+ raise IndexError, "selection '#{selection}' does not exist in :choices for #{debug_name}"
194
+ end
195
+ prototype.instantiate(nil, self)
196
+ end
197
+
198
+ def copy_previous_value(selection, obj)
199
+ prev = get_previous_choice(selection)
200
+ obj.assign(prev) unless prev.nil?
201
+ remember_current_selection(selection)
202
+ end
203
+
204
+ def get_previous_choice(selection)
205
+ if selection != @last_selection and @last_selection != nil
206
+ @choices[@last_selection]
207
+ else
208
+ nil
209
+ end
210
+ end
211
+
212
+ def remember_current_selection(selection)
213
+ @last_selection = selection
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,35 @@
1
+ require "bindata/base_primitive"
2
+
3
+ module BinData
4
+ # Counts the number of bytes remaining in the input stream from the current
5
+ # position to the end of the stream. This only makes sense for seekable
6
+ # streams.
7
+ #
8
+ # require 'bindata'
9
+ #
10
+ # class A < BinData::Record
11
+ # count_bytes_remaining :bytes_remaining
12
+ # string :all_data, :read_length => :bytes_remaining
13
+ # end
14
+ #
15
+ # obj = A.read("abcdefghij")
16
+ # obj.all_data #=> "abcdefghij"
17
+ #
18
+ class CountBytesRemaining < BinData::BasePrimitive
19
+
20
+ #---------------
21
+ private
22
+
23
+ def value_to_binary_string(val)
24
+ ""
25
+ end
26
+
27
+ def read_and_return_value(io)
28
+ io.num_bytes_remaining
29
+ end
30
+
31
+ def sensible_default
32
+ 0
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ # Implement Kernel#instance_exec for Ruby 1.8.6 and below
2
+ unless Object.respond_to? :instance_exec
3
+ module Kernel
4
+ # Taken from http://eigenclass.org/hiki/instance_exec
5
+ def instance_exec(*args, &block)
6
+ mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
7
+ Object.class_eval{ define_method(mname, &block) }
8
+ begin
9
+ ret = send(mname, *args)
10
+ ensure
11
+ Object.class_eval{ undef_method(mname) } rescue nil
12
+ end
13
+ ret
14
+ end
15
+ end
16
+ end
17
+
18
+ module BinData
19
+ class Base
20
+
21
+ # Don't override initialize. If you are defining a new kind of datatype
22
+ # (list, array, choice etc) then put your initialization code in
23
+ # #initialize_instance. This is because BinData objects can be initialized
24
+ # as prototypes and your initialization code may not be called.
25
+ #
26
+ # If you're subclassing BinData::Record, you are definitely doing the wrong
27
+ # thing. Read the documentation on how to use BinData.
28
+ # http://bindata.rubyforge.org/manual.html#records
29
+ alias_method :initialize_without_warning, :initialize
30
+ def initialize_with_warning(*args)
31
+ owner = method(:initialize).owner
32
+ if owner != BinData::Base
33
+ msg = "Don't override #initialize on #{owner}."
34
+ if %w(BinData::Base BinData::BasePrimitive).include? self.class.superclass.name
35
+ msg += "\nrename #initialize to #initialize_instance."
36
+ end
37
+ fail msg
38
+ end
39
+ initialize_without_warning(*args)
40
+ end
41
+ alias_method :initialize, :initialize_with_warning
42
+
43
+ def initialize_instance(*args)
44
+ unless args.empty?
45
+ fail "#{caller[0]} remove the call to super in #initialize_instance"
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,312 @@
1
+ module BinData
2
+ module DSLMixin
3
+ def self.included(base) #:nodoc:
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ def dsl_parser(parser_type = nil)
10
+ unless defined? @dsl_parser
11
+ parser_type = superclass.dsl_parser.parser_type if parser_type.nil?
12
+ @dsl_parser = DSLParser.new(self, parser_type)
13
+ end
14
+ @dsl_parser
15
+ end
16
+
17
+ def method_missing(symbol, *args, &block) #:nodoc:
18
+ dsl_parser.__send__(symbol, *args, &block)
19
+ end
20
+
21
+ # Assert object is not an array or string.
22
+ def to_ary; nil; end
23
+ def to_str; nil; end
24
+ end
25
+
26
+ # A DSLParser parses and accumulates field definitions of the form
27
+ #
28
+ # type name, params
29
+ #
30
+ # where:
31
+ # * +type+ is the under_scored name of a registered type
32
+ # * +name+ is the (possible optional) name of the field
33
+ # * +params+ is a hash containing any parameters
34
+ #
35
+ class DSLParser
36
+ def initialize(the_class, parser_type)
37
+ @the_class = the_class
38
+ @parser_type = parser_type
39
+ @endian = parent_attribute(:endian, nil)
40
+ end
41
+
42
+ attr_reader :parser_type
43
+
44
+ def endian(endian = nil)
45
+ if endian.nil?
46
+ @endian
47
+ elsif endian == :big or endian == :little
48
+ @endian = endian
49
+ else
50
+ dsl_raise ArgumentError, "unknown value for endian '#{endian}'"
51
+ end
52
+ end
53
+
54
+ def hide(*args)
55
+ if option?(:hidden_fields)
56
+ hidden = args.collect do |name|
57
+ unless Symbol === name
58
+ warn "Hidden field '#{name}' should be provided as a symbol. Using strings is deprecated"
59
+ end
60
+ name.to_sym
61
+ end
62
+
63
+ unless defined? @hide
64
+ @hide = parent_attribute(:hide, []).dup
65
+ end
66
+
67
+ @hide.concat(hidden.compact)
68
+ @hide
69
+ end
70
+ end
71
+
72
+ def fields
73
+ unless defined? @fields
74
+ fields = parent_attribute(:fields, nil)
75
+ @fields = SanitizedFields.new(endian)
76
+ @fields.copy_fields(fields) if fields
77
+ end
78
+
79
+ @fields
80
+ end
81
+
82
+ def dsl_params
83
+ case @parser_type
84
+ when :struct
85
+ to_struct_params
86
+ when :array
87
+ to_array_params
88
+ when :choice
89
+ to_choice_params
90
+ when :primitive
91
+ to_struct_params
92
+ else
93
+ raise "unknown parser type #{@parser_type}"
94
+ end
95
+ end
96
+
97
+ def method_missing(symbol, *args, &block) #:nodoc:
98
+ type = symbol
99
+ name = name_from_field_declaration(args)
100
+ params = params_from_field_declaration(type, args, &block)
101
+
102
+ append_field(type, name, params)
103
+ end
104
+
105
+ #-------------
106
+ private
107
+
108
+ def option?(opt)
109
+ options.include?(opt)
110
+ end
111
+
112
+ def options
113
+ case @parser_type
114
+ when :struct
115
+ [:multiple_fields, :optional_fieldnames, :hidden_fields]
116
+ when :array
117
+ [:multiple_fields, :optional_fieldnames]
118
+ when :choice
119
+ [:multiple_fields, :all_or_none_fieldnames, :fieldnames_are_values]
120
+ when :primitive
121
+ [:multiple_fields, :optional_fieldnames]
122
+ else
123
+ raise "unknown parser type #{parser_type}"
124
+ end
125
+ end
126
+
127
+ def parent_attribute(attr, default = nil)
128
+ parent = @the_class.superclass.respond_to?(:dsl_parser) ? @the_class.superclass.dsl_parser : nil
129
+ if parent and parent.respond_to?(attr)
130
+ parent.send(attr)
131
+ else
132
+ default
133
+ end
134
+ end
135
+
136
+ def name_from_field_declaration(args)
137
+ name, params = args
138
+ if name == "" or name.is_a?(Hash)
139
+ nil
140
+ else
141
+ name
142
+ end
143
+ end
144
+
145
+ def params_from_field_declaration(type, args, &block)
146
+ params = params_from_args(args)
147
+
148
+ if block_given?
149
+ params.merge(params_from_block(type, &block))
150
+ else
151
+ params
152
+ end
153
+ end
154
+
155
+ def params_from_args(args)
156
+ name, params = args
157
+ params = name if name.is_a?(Hash)
158
+
159
+ params || {}
160
+ end
161
+
162
+ def params_from_block(type, &block)
163
+ bindata_classes = {
164
+ :array => BinData::Array,
165
+ :choice => BinData::Choice,
166
+ :struct => BinData::Struct
167
+ }
168
+
169
+ if bindata_classes.include?(type)
170
+ parser = DSLParser.new(bindata_classes[type], type)
171
+ parser.endian(endian)
172
+ parser.instance_eval(&block)
173
+
174
+ parser.dsl_params
175
+ else
176
+ {}
177
+ end
178
+ end
179
+
180
+ def append_field(type, name, params)
181
+ ensure_valid_field(name)
182
+
183
+ fields.add_field(type, name, params)
184
+ rescue ArgumentError => err
185
+ dsl_raise ArgumentError, err.message
186
+ rescue UnRegisteredTypeError => err
187
+ dsl_raise TypeError, "unknown type '#{err.message}'"
188
+ end
189
+
190
+ def ensure_valid_field(field_name)
191
+ if too_many_fields?
192
+ dsl_raise SyntaxError, "attempting to wrap more than one type"
193
+ end
194
+
195
+ if must_not_have_a_name_failed?(field_name)
196
+ dsl_raise SyntaxError, "field must not have a name"
197
+ end
198
+
199
+ if all_or_none_names_failed?(field_name)
200
+ dsl_raise SyntaxError, "fields must either all have names, or none must have names"
201
+ end
202
+
203
+ if must_have_a_name_failed?(field_name)
204
+ dsl_raise SyntaxError, "field must have a name"
205
+ end
206
+
207
+ ensure_valid_name(field_name)
208
+ end
209
+
210
+ def ensure_valid_name(name)
211
+ if name and not option?(:fieldnames_are_values)
212
+ if malformed_name?(name)
213
+ dsl_raise NameError.new("", name), "field '#{name}' is an illegal fieldname"
214
+ end
215
+
216
+ if duplicate_name?(name)
217
+ dsl_raise SyntaxError, "duplicate field '#{name}'"
218
+ end
219
+
220
+ if name_shadows_method?(name)
221
+ dsl_raise NameError.new("", name), "field '#{name}' shadows an existing method"
222
+ end
223
+
224
+ if name_is_reserved?(name)
225
+ dsl_raise NameError.new("", name), "field '#{name}' is a reserved name"
226
+ end
227
+ end
228
+ end
229
+
230
+ def too_many_fields?
231
+ option?(:only_one_field) and not fields.empty?
232
+ end
233
+
234
+ def must_not_have_a_name_failed?(name)
235
+ option?(:no_fieldnames) and name != nil
236
+ end
237
+
238
+ def must_have_a_name_failed?(name)
239
+ option?(:mandatory_fieldnames) and name.nil?
240
+ end
241
+
242
+ def all_or_none_names_failed?(name)
243
+ if option?(:all_or_none_fieldnames) and not fields.empty?
244
+ all_names_blank = fields.all_field_names_blank?
245
+ no_names_blank = fields.no_field_names_blank?
246
+
247
+ (name != nil and all_names_blank) or (name == nil and no_names_blank)
248
+ else
249
+ false
250
+ end
251
+ end
252
+
253
+ def malformed_name?(name)
254
+ /^[a-z_]\w*$/ !~ name.to_s
255
+ end
256
+
257
+ def duplicate_name?(name)
258
+ fields.has_field_name?(name)
259
+ end
260
+
261
+ def name_shadows_method?(name)
262
+ @the_class.method_defined?(name)
263
+ end
264
+
265
+ def name_is_reserved?(name)
266
+ BinData::Struct::RESERVED.include?(name.to_sym)
267
+ end
268
+
269
+ def dsl_raise(exception, message)
270
+ backtrace = caller
271
+ backtrace.shift while %r{bindata/dsl.rb} =~ backtrace.first
272
+
273
+ raise exception, message + " in #{@the_class}", backtrace
274
+ end
275
+
276
+ def to_array_params
277
+ case fields.length
278
+ when 0
279
+ {}
280
+ when 1
281
+ {:type => fields[0].prototype}
282
+ else
283
+ {:type => [:struct, to_struct_params]}
284
+ end
285
+ end
286
+
287
+ def to_choice_params
288
+ if fields.length == 0
289
+ {}
290
+ elsif fields.all_field_names_blank?
291
+ {:choices => fields.collect { |f| f.prototype }}
292
+ else
293
+ choices = {}
294
+ fields.each { |f| choices[f.name] = f.prototype }
295
+ {:choices => choices}
296
+ end
297
+ end
298
+
299
+ def to_struct_params
300
+ result = {:fields => fields}
301
+ if not endian.nil?
302
+ result[:endian] = endian
303
+ end
304
+ if option?(:hidden_fields) and not hide.empty?
305
+ result[:hide] = hide
306
+ end
307
+
308
+ result
309
+ end
310
+ end
311
+ end
312
+ end