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.

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.



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
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/bindata/base.rb', line 116

def initialize(params = {}, env = nil)
  # all known parameters
  mandatory = self.class.mandatory_parameters
  optional = self.class.optional_parameters

  # default :readwrite param to true if unspecified
  if not params.has_key?(:readwrite)
    params = params.dup
    params[:readwrite] = true
  end

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

  # partition parameters into known and extra parameters
  @params = {}
  extra   = {}
  params.each do |k,v|
    k = k.to_sym
    raise ArgumentError, "parameter :#{k} is nil in #{self}" if v.nil?
    if mandatory.include?(k) or optional.include?(k)
      @params[k] = v.freeze
    else
      extra[k] = v.freeze
    end
  end

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

Class Method Details

.lookup(name) ⇒ Object

Returns the class matching a previously registered name.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/bindata/base.rb', line 89

def lookup(name)
  klass = Registry.instance.lookup(name)
  if klass.nil?
    # lookup failed so attempt endian lookup
    if self.respond_to?(:endian) and self.endian != nil
      name = name.to_s
      if /^u?int\d\d?$/ =~ name
        new_name = name + ((self.endian == :little) ? "le" : "be")
        klass = Registry.instance.lookup(new_name)
      elsif ["float", "double"].include?(name)
        new_name = name + ((self.endian == :little) ? "_le" : "_be")
        klass = Registry.instance.lookup(new_name)
      end
    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.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/bindata/base.rb', line 29

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

.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.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/bindata/base.rb', line 49

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

.parametersObject

Returns both the mandatory and optional parameters used by this class.



67
68
69
70
71
# File 'lib/bindata/base.rb', line 67

def parameters
  # warn about deprecated method - remove before releasing 1.0
  warn "warning: #parameters is deprecated."
  (mandatory_parameters + optional_parameters).uniq
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.



76
77
78
79
80
# File 'lib/bindata/base.rb', line 76

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.



83
84
85
# File 'lib/bindata/base.rb', line 83

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

Instance Method Details

#accepted_parametersObject

Returns a list of parameters that are accepted by this object



170
171
172
# File 'lib/bindata/base.rb', line 170

def accepted_parameters
  (self.class.mandatory_parameters + self.class.optional_parameters).uniq
end

#clearObject

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

Raises:

  • (NotImplementedError)


273
274
275
# File 'lib/bindata/base.rb', line 273

def clear
  raise NotImplementedError
end

#do_read(io) ⇒ Object

Reads the value for this data from io.



190
191
192
193
194
# File 'lib/bindata/base.rb', line 190

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)


283
284
285
# File 'lib/bindata/base.rb', line 283

def done_read
  raise NotImplementedError
end

#field_namesObject

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

Raises:

  • (NotImplementedError)


304
305
306
# File 'lib/bindata/base.rb', line 304

def field_names
  raise NotImplementedError
end

#inspectObject

Return a human readable representation of this object.



213
214
215
# File 'lib/bindata/base.rb', line 213

def inspect
  snapshot.inspect
end

#klass_lookup(name) ⇒ Object

Returns the class matching a previously registered name.



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/bindata/base.rb', line 155

def klass_lookup(name)
  @cache ||= {}
  klass = @cache[name]
  if klass.nil?
    klass = self.class.lookup(name)
    if klass.nil? and @env.parent_data_object != nil
      # lookup failed so retry in the context of the parent data object
      klass = @env.parent_data_object.klass_lookup(name)
    end
    @cache[name] = klass
  end
  klass
end

#num_bytes(what = nil) ⇒ Object

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



202
203
204
# File 'lib/bindata/base.rb', line 202

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

#read(io) ⇒ Object

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



175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/bindata/base.rb', line 175

def read(io)
  # remove previous method to prevent warnings
  class << io
    undef_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)


208
209
210
# File 'lib/bindata/base.rb', line 208

def single_value?
  respond_to? :value
end

#snapshotObject

Returns a snapshot of this data object.

Raises:

  • (NotImplementedError)


298
299
300
# File 'lib/bindata/base.rb', line 298

def snapshot
  raise NotImplementedError
end

#write(io) ⇒ Object

Writes the value for this data to io.



197
198
199
# File 'lib/bindata/base.rb', line 197

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