bindata 2.5.0 → 3.0.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.
data/lib/bindata/name.rb CHANGED
@@ -11,16 +11,26 @@ module BinData
11
11
  # BinData::Struct.new(name: :my_struct, fields: ...)
12
12
  # array = BinData::Array.new(type: :my_struct)
13
13
  # </pre></code>
14
+ # <tt>:namespace</tt>:: The namespace that this object belongs to may be
15
+ # set explicitly. This is only useful when dynamically
16
+ # generating types.
17
+ # <code><pre>
18
+ # BinData::Struct.new(name: :my_struct, namespace: :ns1, fields: ...)
19
+ # BinData::Struct.new(name: :my_struct, namespace: :ns2, fields: ...)
20
+ # array = BinData::Array.new(namespace: :ns1, type: :my_struct)
21
+ # </pre></code>
14
22
  module RegisterNamePlugin
15
23
 
16
24
  def self.included(base) # :nodoc:
17
- # The registered name may be provided explicitly.
18
- base.optional_parameter :name
25
+ # +name+ and +namespace+ may be provided explicitly.
26
+ base.optional_parameter :name, :namespace
19
27
  end
20
28
 
21
29
  def initialize_shared_instance
22
30
  if has_parameter?(:name)
23
- RegisteredClasses.register(get_parameter(:name), self)
31
+ name = get_parameter(:name)
32
+ namespace = get_parameter(:namespace) || ""
33
+ RegisteredClasses.register(namespace, name, self)
24
34
  end
25
35
  super
26
36
  end
@@ -77,9 +77,9 @@ module BinData
77
77
  @struct.respond_to?(symbol, include_private) || super
78
78
  end
79
79
 
80
- def method_missing(symbol, *args, &block) # :nodoc:
80
+ def method_missing(symbol, *args, **kwargs, &block) # :nodoc:
81
81
  if @struct.respond_to?(symbol)
82
- @struct.__send__(symbol, *args, &block)
82
+ @struct.__send__(symbol, *args, **kwargs, &block)
83
83
  else
84
84
  super
85
85
  end
@@ -17,7 +17,7 @@ module BinData
17
17
  include MultiFieldArgSeparator
18
18
 
19
19
  def sanitize_parameters!(obj_class, params)
20
- super(obj_class, params.merge!(obj_class.dsl_params))
20
+ super(obj_class, params.merge_dsl_params)
21
21
  end
22
22
  end
23
23
  end
@@ -7,50 +7,65 @@ module BinData
7
7
  # Numerics (integers and floating point numbers) have an endian property as
8
8
  # part of their name (e.g. int32be, float_le).
9
9
  #
10
+ # Classes exist in a namespace that mirrors the Ruby module hierarchy.
11
+ #
10
12
  # Classes can be looked up based on their full name or an abbreviated +name+
11
13
  # with +hints+.
12
14
  #
13
- # There are two hints supported, :endian and :search_prefix.
15
+ # There are two hints supported, :endian and :search_namespace.
14
16
  #
15
- # #lookup("int32", { endian: :big }) will return Int32Be.
17
+ # #lookup("", "int32", { endian: :big }) will return Int32Be.
16
18
  #
17
- # #lookup("my_type", { search_prefix: :ns }) will return NsMyType.
19
+ # #lookup("", "my_type", { search_namespace: :ns }) will return Ns::MyType.
18
20
  #
19
21
  # Names are stored in under_score_style, not camelCase.
20
22
  class Registry
21
23
  def initialize
22
24
  @registry = {}
25
+ @backwards_compatible_registry = {}
23
26
  end
24
27
 
25
- def register(name, class_to_register)
26
- return if name.nil? || class_to_register.nil?
28
+ def register(namespace, name, class_to_register)
29
+ return if namespace.nil? || name.nil? || class_to_register.nil?
27
30
 
28
- formatted_name = underscore_name(name)
29
- warn_if_name_is_already_registered(formatted_name, class_to_register)
31
+ search = name_with_prefix(name, namespace)
32
+ warn_if_name_is_already_registered(search, class_to_register)
30
33
 
31
- @registry[formatted_name] = class_to_register
34
+ @registry[search] = class_to_register
35
+ @backwards_compatible_registry[underscore_name(name)] = search
32
36
  end
33
37
 
34
- def unregister(name)
35
- @registry.delete(underscore_name(name))
38
+ def unregister(namespace, name)
39
+ search = name_with_prefix(name, namespace)
40
+
41
+ @registry.delete(search)
42
+ @backwards_compatible_registry.delete(underscore_name(name))
36
43
  end
37
44
 
38
- def lookup(name, hints = {})
39
- the_class = @registry[normalize_name(name, hints)]
40
- if the_class
41
- the_class
42
- elsif @registry[normalize_name(name, hints.merge(endian: :big))]
43
- raise(UnRegisteredTypeError, "#{name}, do you need to specify endian?")
44
- else
45
- raise(UnRegisteredTypeError, name)
45
+ def lookup(namespace, name, hints = {})
46
+ search_names(namespace, name, hints).each do |search|
47
+ register_dynamic_class(search)
48
+ if @registry.has_key?(search)
49
+ return @registry[search]
50
+ end
51
+ end
52
+
53
+ # give the user a hint if the endian keyword is missing
54
+ search_names(namespace, name, hints.merge(endian: :big)).each do |search|
55
+ register_dynamic_class(search)
56
+ if @registry.has_key?(search)
57
+ raise(UnRegisteredTypeError, "#{name}, do you need to specify endian?")
58
+ end
46
59
  end
60
+
61
+ raise(UnRegisteredTypeError, name)
47
62
  end
48
63
 
49
64
  # Convert CamelCase +name+ to underscore style.
50
65
  def underscore_name(name)
51
66
  name
52
67
  .to_s
53
- .sub(/.*::/, "")
68
+ .gsub(/::/, "_")
54
69
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
55
70
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
56
71
  .tr('-', '_')
@@ -60,31 +75,62 @@ module BinData
60
75
  #---------------
61
76
  private
62
77
 
63
- def normalize_name(name, hints)
64
- name = underscore_name(name)
78
+ def search_names(namespace, name, hints)
79
+ searches = backwards_compatible_search_names(name, hints)
80
+ return searches unless searches.empty?
65
81
 
66
- if !registered?(name)
67
- search_prefix = [""] + Array(hints[:search_prefix])
68
- search_prefix.each do |prefix|
69
- nwp = name_with_prefix(name, prefix)
70
- if registered?(nwp)
71
- name = nwp
72
- break
73
- end
82
+ searches = []
83
+
84
+ # prioritise BinData classes
85
+ nwp = name_with_prefix(name, "BinData")
86
+ nwe = name_with_endian(nwp, hints[:endian])
87
+ searches << nwp
88
+ searches << nwe if nwe
74
89
 
90
+ ns = underscore_name(namespace)
91
+ loop do
92
+ search_prefix = [""] + Array(hints[:search_namespace])
93
+ search_prefix.each do |prefix|
94
+ nwp = name_with_prefix(name, name_with_prefix(prefix, ns))
75
95
  nwe = name_with_endian(nwp, hints[:endian])
76
- if registered?(nwe)
77
- name = nwe
78
- break
79
- end
96
+
97
+ searches << nwp
98
+ searches << nwe if nwe
99
+ end
100
+
101
+ break if ns == ""
102
+ ns.sub!(/(?:^|_)[^_]*$/, "")
103
+ end
104
+
105
+ searches
106
+ end
107
+
108
+ # The old way of providing namespaces was to prefix the class name.
109
+ # Here we provide the backward compatibility for existing code.
110
+ def backwards_compatible_search_names(name, hints)
111
+ return [] unless hints.has_key?(:search_prefix)
112
+
113
+ searches = []
114
+ search_prefix = Array(hints[:search_prefix])
115
+ search_prefix.each do |prefix|
116
+ nwp = name_with_prefix(name, prefix)
117
+ nwe = name_with_endian(nwp, hints[:endian])
118
+
119
+ found_search = @backwards_compatible_registry[nwp]
120
+ searches << found_search if found_search
121
+
122
+ if nwe
123
+ found_search = @backwards_compatible_registry[nwe]
124
+ searches << found_search if found_search
80
125
  end
81
126
  end
82
127
 
83
- name
128
+ searches
84
129
  end
85
130
 
86
131
  def name_with_prefix(name, prefix)
87
- prefix = prefix.to_s.chomp('_')
132
+ name = underscore_name(name)
133
+ prefix = underscore_name(prefix).chomp('_')
88
134
  if prefix == ""
89
135
  name
90
136
  else
@@ -93,27 +139,24 @@ module BinData
93
139
  end
94
140
 
95
141
  def name_with_endian(name, endian)
96
- return name if endian.nil?
142
+ return nil if endian.nil?
97
143
 
98
144
  suffix = (endian == :little) ? 'le' : 'be'
99
- if /^u?int\d+$/.match?(name)
145
+ if /^bin_data_u?int\d+$/.match?(name)
100
146
  name + suffix
101
147
  else
102
148
  name + '_' + suffix
103
149
  end
104
150
  end
105
151
 
106
- def registered?(name)
107
- register_dynamic_class(name) unless @registry.key?(name)
108
-
109
- @registry.key?(name)
110
- end
111
-
112
152
  def register_dynamic_class(name)
113
- if /^u?int\d+(le|be)$/.match?(name) || /^s?bit\d+(le)?$/.match?(name)
114
- class_name = name.gsub(/(?:^|_)(.)/) { $1.upcase }
153
+ return unless /^bin_data_/ =~ name
154
+
155
+ if /^bin_data_u?int\d+(le|be)$/.match?(name) || /^bin_data_s?bit\d+(le)?$/.match?(name)
156
+ base_name = name.sub(/^bin_data_/, "")
157
+ class_name = base_name.gsub(/(?:^|_)(.)/) { $1.upcase }
115
158
  begin
116
- # call const_get for side effects
159
+ # call const_get for side effect of creating class
117
160
  BinData.const_get(class_name)
118
161
  rescue NameError
119
162
  end
@@ -124,7 +167,7 @@ module BinData
124
167
  prev_class = @registry[name]
125
168
  if prev_class && prev_class != class_to_register
126
169
  Kernel.warn "warning: replacing registered class #{prev_class} " \
127
- "with #{class_to_register}"
170
+ "with #{class_to_register}"
128
171
  end
129
172
  end
130
173
  end
@@ -6,7 +6,7 @@ module BinData
6
6
  class SanitizedParameter; end
7
7
 
8
8
  class SanitizedPrototype < SanitizedParameter
9
- def initialize(obj_type, obj_params, hints)
9
+ def initialize(obj_type, obj_params, namespace, hints)
10
10
  raw_hints = hints.dup
11
11
  if raw_hints[:endian].respond_to?(:endian)
12
12
  raw_hints[:endian] = raw_hints[:endian].endian
@@ -16,14 +16,14 @@ module BinData
16
16
  if BinData::Base === obj_type
17
17
  obj_class = obj_type
18
18
  else
19
- obj_class = RegisteredClasses.lookup(obj_type, raw_hints)
19
+ obj_class = RegisteredClasses.lookup(namespace, obj_type, raw_hints)
20
20
  end
21
21
 
22
22
  if BinData::Base === obj_class
23
23
  @factory = obj_class
24
24
  else
25
25
  @obj_class = obj_class
26
- @obj_params = SanitizedParameters.new(obj_params, @obj_class, hints)
26
+ @obj_params = SanitizedParameters.new(obj_params, @obj_class, namespace, hints)
27
27
  end
28
28
  end
29
29
 
@@ -44,9 +44,9 @@ module BinData
44
44
  #----------------------------------------------------------------------------
45
45
 
46
46
  class SanitizedField < SanitizedParameter
47
- def initialize(name, field_type, field_params, hints)
47
+ def initialize(name, field_type, field_params, namespace, hints)
48
48
  @name = name
49
- @prototype = SanitizedPrototype.new(field_type, field_params, hints)
49
+ @prototype = SanitizedPrototype.new(field_type, field_params, namespace, hints)
50
50
  end
51
51
 
52
52
  attr_reader :prototype, :name
@@ -68,7 +68,8 @@ module BinData
68
68
  class SanitizedFields < SanitizedParameter
69
69
  include Enumerable
70
70
 
71
- def initialize(hints, base_fields = nil)
71
+ def initialize(namespace, hints, base_fields = nil)
72
+ @namespace = namespace
72
73
  @hints = hints
73
74
  @fields = base_fields ? base_fields.raw_fields : []
74
75
  end
@@ -76,7 +77,7 @@ module BinData
76
77
  def add_field(type, name, params)
77
78
  name = nil if name == ""
78
79
 
79
- @fields << SanitizedField.new(name, type, params, @hints)
80
+ @fields << SanitizedField.new(name, type, params, @namespace, @hints)
80
81
  end
81
82
 
82
83
  def raw_fields
@@ -122,14 +123,14 @@ module BinData
122
123
  #----------------------------------------------------------------------------
123
124
 
124
125
  class SanitizedChoices < SanitizedParameter
125
- def initialize(choices, hints)
126
+ def initialize(choices, namespace, hints)
126
127
  @choices = {}
127
128
  choices.each_pair do |key, val|
128
129
  if SanitizedParameter === val
129
130
  prototype = val
130
131
  else
131
132
  type, param = val
132
- prototype = SanitizedPrototype.new(type, param, hints)
133
+ prototype = SanitizedPrototype.new(type, param, namespace, hints)
133
134
  end
134
135
 
135
136
  if key == :default
@@ -180,20 +181,30 @@ module BinData
180
181
  if SanitizedParameters === parameters
181
182
  parameters
182
183
  else
183
- SanitizedParameters.new(parameters, the_class, {})
184
+ namespace = parameters[:namespace]
185
+ unless namespace
186
+ m = /(.*)::[^:].*/.match(the_class.name)
187
+ namespace = m ? m[1] : ""
188
+ end
189
+ SanitizedParameters.new(parameters, the_class, namespace, {})
184
190
  end
185
191
  end
186
192
  end
187
193
 
188
- def initialize(parameters, the_class, hints)
194
+ def initialize(parameters, the_class, namespace, hints)
189
195
  parameters.each_pair { |key, value| self[key.to_sym] = value }
190
196
 
191
197
  @the_class = the_class
198
+ @namespace = namespace
192
199
 
193
200
  if hints[:endian]
194
201
  self[:endian] ||= hints[:endian]
195
202
  end
196
203
 
204
+ if hints[:search_namespace] && !hints[:search_namespace].empty?
205
+ self[:search_namespace] = Array(self[:search_namespace]).concat(Array(hints[:search_namespace]))
206
+ end
207
+
197
208
  if hints[:search_prefix] && !hints[:search_prefix].empty?
198
209
  self[:search_prefix] = Array(self[:search_prefix]).concat(Array(hints[:search_prefix]))
199
210
  end
@@ -211,6 +222,12 @@ module BinData
211
222
  false
212
223
  end
213
224
 
225
+ def must_have_at_least_one_of(*keys)
226
+ unless has_at_least_one_of?(*keys)
227
+ raise ArgumentError, "#{@the_class} requires one of #{keys}"
228
+ end
229
+ end
230
+
214
231
  def warn_replacement_parameter(bad_key, suggested_key)
215
232
  if has_parameter?(bad_key)
216
233
  Kernel.warn ":#{bad_key} is not used with #{@the_class}. " \
@@ -247,6 +264,10 @@ module BinData
247
264
  end
248
265
  end
249
266
 
267
+ def merge_dsl_params
268
+ self.merge!(@the_class.dsl_params)
269
+ end
270
+
250
271
  def sanitize_object_prototype(key)
251
272
  sanitize(key) do |obj_type, obj_params|
252
273
  create_sanitized_object_prototype(obj_type, obj_params)
@@ -278,11 +299,15 @@ module BinData
278
299
  end
279
300
 
280
301
  def create_sanitized_params(params, the_class)
281
- SanitizedParameters.new(params, the_class, hints)
302
+ SanitizedParameters.new(params, the_class, @namespace, hints)
282
303
  end
283
304
 
284
305
  def hints
285
- { endian: self[:endian], search_prefix: self[:search_prefix] }
306
+ {
307
+ endian: self[:endian],
308
+ search_namespace: self[:search_namespace],
309
+ search_prefix: self[:search_prefix]
310
+ }
286
311
  end
287
312
 
288
313
  #---------------
@@ -350,15 +375,15 @@ module BinData
350
375
  end
351
376
 
352
377
  def create_sanitized_choices(choices)
353
- SanitizedChoices.new(choices, hints)
378
+ SanitizedChoices.new(choices, @namespace, hints)
354
379
  end
355
380
 
356
381
  def create_sanitized_fields
357
- SanitizedFields.new(hints)
382
+ SanitizedFields.new(@namespace, hints)
358
383
  end
359
384
 
360
385
  def create_sanitized_object_prototype(obj_type, obj_params)
361
- SanitizedPrototype.new(obj_type, obj_params, hints)
386
+ SanitizedPrototype.new(obj_type, obj_params, @namespace, hints)
362
387
  end
363
388
  end
364
389
  #----------------------------------------------------------------------------
@@ -65,8 +65,8 @@ module BinData
65
65
  @type.respond_to?(symbol, include_all) || super
66
66
  end
67
67
 
68
- def method_missing(symbol, *args, &block) # :nodoc:
69
- @type.__send__(symbol, *args, &block)
68
+ def method_missing(symbol, *args, **kwargs, &block) # :nodoc:
69
+ @type.__send__(symbol, *args, **kwargs, &block)
70
70
  end
71
71
 
72
72
  def do_read(io) # :nodoc:
@@ -90,7 +90,7 @@ module BinData
90
90
  include MultiFieldArgSeparator
91
91
 
92
92
  def sanitize_parameters!(obj_class, params)
93
- params.merge!(obj_class.dsl_params)
93
+ params.merge_dsl_params
94
94
  params.sanitize_object_prototype(:type)
95
95
  end
96
96
  end
data/lib/bindata/skip.rb CHANGED
@@ -208,13 +208,8 @@ module BinData
208
208
 
209
209
  class SkipArgProcessor < BaseArgProcessor
210
210
  def sanitize_parameters!(obj_class, params)
211
- params.merge!(obj_class.dsl_params)
212
-
213
- unless params.has_at_least_one_of?(:length, :to_abs_offset, :until_valid)
214
- raise ArgumentError,
215
- "#{obj_class} requires :length, :to_abs_offset or :until_valid"
216
- end
217
-
211
+ params.merge_dsl_params
212
+ params.must_have_at_least_one_of(:length, :to_abs_offset, :until_valid)
218
213
  params.must_be_integer(:to_abs_offset, :length)
219
214
  params.sanitize_object_prototype(:until_valid)
220
215
  end
@@ -42,9 +42,9 @@ 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
- # <tt>:search_prefix</tt>:: Allows abbreviated type names. If a type is
46
- # unrecognised, then each prefix is applied until
47
- # a match is found.
45
+ # <tt>:search_namespace</tt>:: Allows namespaced type names. If a type is
46
+ # unrecognised, then each namespace is searched
47
+ # until a match is found.
48
48
  #
49
49
  # == Field Parameters
50
50
  #
@@ -59,7 +59,8 @@ module BinData
59
59
  arg_processor :struct
60
60
 
61
61
  mandatory_parameter :fields
62
- optional_parameters :endian, :search_prefix, :hide
62
+ optional_parameters :endian, :search_namespace, :search_prefix, :hide
63
+ mutually_exclusive_parameters :search_namespace, :search_prefix
63
64
 
64
65
  # These reserved words may not be used as field names
65
66
  RESERVED =
@@ -71,7 +72,7 @@ module BinData
71
72
  when while yield] +
72
73
  %w[array element index value] +
73
74
  %w[type initial_length read_until] +
74
- %w[fields endian search_prefix hide onlyif byte_align] +
75
+ %w[fields endian search_namespace search_prefix hide onlyif byte_align] +
75
76
  %w[choices selection copy_on_change] +
76
77
  %w[read_abs_offset struct_params])
77
78
  .collect(&:to_sym)
@@ -292,7 +293,7 @@ module BinData
292
293
  key?(symbol) || super
293
294
  end
294
295
 
295
- def method_missing(symbol, *args)
296
+ def method_missing(symbol, *args, **kwargs)
296
297
  key?(symbol) ? self[symbol] : super
297
298
  end
298
299
  end
@@ -367,6 +368,7 @@ module BinData
367
368
  class StructArgProcessor < BaseArgProcessor
368
369
  def sanitize_parameters!(obj_class, params)
369
370
  sanitize_endian(params)
371
+ sanitize_search_namespace(params)
370
372
  sanitize_search_prefix(params)
371
373
  sanitize_fields(obj_class, params)
372
374
  sanitize_hide(params)
@@ -379,6 +381,12 @@ module BinData
379
381
  params.sanitize_endian(:endian)
380
382
  end
381
383
 
384
+ def sanitize_search_namespace(params)
385
+ params.sanitize(:search_namespace) do |snamespace|
386
+ Array(snamespace).collect &:to_s
387
+ end
388
+ end
389
+
382
390
  def sanitize_search_prefix(params)
383
391
  params.sanitize(:search_prefix) do |sprefix|
384
392
  search_prefix = Array(sprefix).collect do |prefix|
@@ -1,3 +1,3 @@
1
1
  module BinData
2
- VERSION = '2.5.0'
2
+ VERSION = '3.0.0'
3
3
  end
data/lib/bindata.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # BinData -- Binary data manipulator.
2
- # Copyright (c) 2007 - 2018 Dion Mendel.
2
+ # Copyright (c) 2007 - 2026 Dion Mendel.
3
3
 
4
4
  require 'bindata/version'
5
5
  require 'bindata/array'
@@ -33,6 +33,6 @@ require 'bindata/warnings'
33
33
  #
34
34
  # == License
35
35
  #
36
- # BinData is released under the same license as Ruby.
36
+ # BinData is released under the BSD 2-Clause License.
37
37
  #
38
- # Copyright (c) 2007 - 2018 Dion Mendel.
38
+ # Copyright (c) 2007 - 2026 Dion Mendel.
@@ -25,7 +25,7 @@ end
25
25
  describe BinData::BasePrimitive do
26
26
  it "is not registered" do
27
27
  _ {
28
- BinData::RegisteredClasses.lookup("BasePrimitive")
28
+ BinData::RegisteredClasses.lookup("BinData", "BasePrimitive")
29
29
  }.must_raise BinData::UnRegisteredTypeError
30
30
  end
31
31
  end
@@ -5,7 +5,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
5
5
  describe BinData::Primitive do
6
6
  it "is not registered" do
7
7
  _ {
8
- BinData::RegisteredClasses.lookup("Primitive")
8
+ BinData::RegisteredClasses.lookup("BinData", "Primitive")
9
9
  }.must_raise BinData::UnRegisteredTypeError
10
10
  end
11
11
  end