Class: BinData::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/bindata/base.rb

Overview

This is the abstract base class for all data objects.

Parameters

Parameters may be provided at initialisation to control the behaviour of an object. These params are:

:readwrite

If false, calls to #read or #write will not perform any I/O. Default is true.

:check_offset

Raise an error if the current IO offset doesn’t meet this criteria. A boolean return indicates success or failure. Any other return is compared to the current offset. The variable offset is made available to any lambda assigned to this parameter. This parameter is only checked before reading.

:adjust_offset

Ensures that the current IO offset is at this position before reading. This is like :check_offset, except that it will adjust the IO offset instead of raising an error.

Direct Known Subclasses

Array, Choice, Single, Struct

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}, env = nil) ⇒ Base

Creates a new data object.

params is a hash containing symbol keys. Some params may reference callable objects (methods or procs). env is the environment that these callable objects are evaluated in.



186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/bindata/base.rb', line 186

def initialize(params = {}, env = nil)
  unless SanitizedParameters === params
    params = SanitizedParameters.new(self.class, params)
  end

  @params = params.accepted_parameters

  # set up the environment
  @env             = env || LazyEvalEnv.new
  @env.params      = params.extra_parameters
  @env.data_object = self
end

Class Method Details

.accepted_parametersObject

Returns a list of parameters that are accepted by this object



110
111
112
# File 'lib/bindata/base.rb', line 110

def accepted_parameters
  (mandatory_parameters + optional_parameters + default_parameters.keys).uniq
end

.default_parameters(params = {}) ⇒ Object Also known as: default_parameter

Returns the default parameters used by this class. Any given args are appended to the parameters list. The parameters for a class will include the parameters of its ancestors.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/bindata/base.rb', line 75

def default_parameters(params = {})
  unless defined? @default_parameters
    @default_parameters = {}
    ancestors[1..-1].each do |parent|
      if parent.respond_to?(:default_parameters)
        @default_parameters = @default_parameters.merge(parent.default_parameters)
      end
    end
  end
  if not params.empty?
    @default_parameters = @default_parameters.merge(params)
  end
  @default_parameters
end

.lookup(name, endian = nil) ⇒ Object

Returns the class matching a previously registered name.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/bindata/base.rb', line 159

def lookup(name, endian = nil)
  name = name.to_s
  klass = Registry.instance.lookup(name)
  if klass.nil? and endian != nil
    # lookup failed so attempt endian lookup
    if /^u?int\d{1,3}$/ =~ name
      new_name = name + ((endian == :little) ? "le" : "be")
      klass = Registry.instance.lookup(new_name)
    elsif ["float", "double"].include?(name)
      new_name = name + ((endian == :little) ? "_le" : "_be")
      klass = Registry.instance.lookup(new_name)
    end
  end
  klass
end

.mandatory_parameters(*args) ⇒ Object Also known as: mandatory_parameter

Returns the mandatory parameters used by this class. Any given args are appended to the parameters list. The parameters for a class will include the parameters of its ancestors.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/bindata/base.rb', line 35

def mandatory_parameters(*args)
  unless defined? @mandatory_parameters
    @mandatory_parameters = []
    ancestors[1..-1].each do |parent|
      if parent.respond_to?(:mandatory_parameters)
        @mandatory_parameters.concat(parent.mandatory_parameters)
      end
    end
  end
  if not args.empty?
    args.each { |arg| @mandatory_parameters << arg.to_sym }
    @mandatory_parameters.uniq!
  end
  @mandatory_parameters
end

.mutually_exclusive_parameters(*args) ⇒ Object

Returns the pairs of mutually exclusive parameters used by this class. Any given args are appended to the parameters list. The parameters for a class will include the parameters of its ancestors.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/bindata/base.rb', line 94

def mutually_exclusive_parameters(*args)
  unless defined? @mutually_exclusive_parameters
    @mutually_exclusive_parameters = []
    ancestors[1..-1].each do |parent|
      if parent.respond_to?(:mutually_exclusive_parameters)
        @mutually_exclusive_parameters.concat(parent.mutually_exclusive_parameters)
      end
    end
  end
  if not args.empty?
    @mutually_exclusive_parameters << [args[0].to_sym, args[1].to_sym]
  end
  @mutually_exclusive_parameters
end

.optional_parameters(*args) ⇒ Object Also known as: optional_parameter

Returns the optional parameters used by this class. Any given args are appended to the parameters list. The parameters for a class will include the parameters of its ancestors.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/bindata/base.rb', line 55

def optional_parameters(*args)
  unless defined? @optional_parameters
    @optional_parameters = []
    ancestors[1..-1].each do |parent|
      if parent.respond_to?(:optional_parameters)
        @optional_parameters.concat(parent.optional_parameters)
      end
    end
  end
  if not args.empty?
    args.each { |arg| @optional_parameters << arg.to_sym }
    @optional_parameters.uniq!
  end
  @optional_parameters
end

.read(io) ⇒ Object

Instantiates this class and reads from io. For single value objects just the value is returned, otherwise the newly created data object is returned.



146
147
148
149
150
# File 'lib/bindata/base.rb', line 146

def read(io)
  data = self.new
  data.read(io)
  data.single_value? ? data.value : data
end

.register(name, klass) ⇒ Object

Registers the mapping of name to klass.



153
154
155
# File 'lib/bindata/base.rb', line 153

def register(name, klass)
  Registry.instance.register(name, klass)
end

.sanitize_parameters(params, *args) ⇒ Object

Returns a sanitized params that is of the form expected by #initialize.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/bindata/base.rb', line 116

def sanitize_parameters(params, *args)
  params = params.dup

  # add default parameters
  default_parameters.each do |k,v|
    params[k] = v unless params.has_key?(k)
  end

  # ensure mandatory parameters exist
  mandatory_parameters.each do |prm|
    if not params.has_key?(prm)
      raise ArgumentError, "parameter ':#{prm}' must be specified " +
                           "in #{self}"
    end
  end

  # ensure mutual exclusion
  mutually_exclusive_parameters.each do |param1, param2|
    if params.has_key?(param1) and params.has_key?(param2)
      raise ArgumentError, "params #{param1} and #{param2} " +
                           "are mutually exclusive"
    end
  end

  params
end

Instance Method Details

#clearObject

Resets the internal state to that of a newly created object.

Raises:

  • (NotImplementedError)


316
317
318
# File 'lib/bindata/base.rb', line 316

def clear
  raise NotImplementedError
end

#do_read(io) ⇒ Object

Reads the value for this data from io.



218
219
220
221
222
# File 'lib/bindata/base.rb', line 218

def do_read(io)
  clear
  check_offset(io)
  _do_read(io) if eval_param(:readwrite) != false
end

#done_readObject

To be called after calling #do_read.

Raises:

  • (NotImplementedError)


326
327
328
# File 'lib/bindata/base.rb', line 326

def done_read
  raise NotImplementedError
end

#field_namesObject

Returns a list of the names of all fields accessible through this object.

Raises:

  • (NotImplementedError)


353
354
355
# File 'lib/bindata/base.rb', line 353

def field_names
  raise NotImplementedError
end

#inspectObject

Return a human readable representation of this object.



243
244
245
# File 'lib/bindata/base.rb', line 243

def inspect
  snapshot.inspect
end

#num_bytes(what = nil) ⇒ Object

Returns the number of bytes it will take to write this data.



238
239
240
# File 'lib/bindata/base.rb', line 238

def num_bytes(what = nil)
  (eval_param(:readwrite) != false) ? _num_bytes(what) : 0
end

#read(io) ⇒ Object

Reads data into this data object by calling #do_read then #done_read.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/bindata/base.rb', line 200

def read(io)
  # wrap strings in a StringIO
  io = StringIO.new(io) if io.respond_to?(:to_str)

  # remove previous method to prevent warnings
  class << io
    remove_method(:bindata_mark) if method_defined?(:bindata_mark)
  end

  # remember the current position in the IO object
  io.instance_eval "def bindata_mark; #{io.pos}; end"

  do_read(io)
  done_read
  self
end

#single_value?Boolean

Returns whether this data object contains a single value. Single value data objects respond to #value and #value=.

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


347
348
349
# File 'lib/bindata/base.rb', line 347

def single_value?
  raise NotImplementedError
end

#snapshotObject

Returns a snapshot of this data object.

Raises:

  • (NotImplementedError)


341
342
343
# File 'lib/bindata/base.rb', line 341

def snapshot
  raise NotImplementedError
end

#to_sObject

Returns the string representation of this data object.



230
231
232
233
234
235
# File 'lib/bindata/base.rb', line 230

def to_s
  io = StringIO.new
  write(io)
  io.rewind
  io.read
end

#write(io) ⇒ Object

Writes the value for this data to io.



225
226
227
# File 'lib/bindata/base.rb', line 225

def write(io)
  _write(io) if eval_param(:readwrite) != false
end