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 offest doesn’t meet this criteria. A boolean return indicates success or failure. Any other return is compared to the current offset. 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.



112
113
114
115
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
# File 'lib/bindata/base.rb', line 112

def initialize(params = {}, env = nil)
  # default :readwrite param to true if unspecified
  unless params.has_key?(:readwrite)
    params = params.dup
    params[:readwrite] = true
  end

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

  known_params = self.class.parameters

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



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/bindata/base.rb', line 85

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.



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

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



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

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



65
66
67
# File 'lib/bindata/base.rb', line 65

def parameters
  (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.



72
73
74
75
76
# File 'lib/bindata/base.rb', line 72

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.



79
80
81
# File 'lib/bindata/base.rb', line 79

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

Instance Method Details

#do_read(io) ⇒ Object

Reads the value for this data from io.



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

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

#klass_lookup(name) ⇒ Object

Returns the class matching a previously registered name.



149
150
151
152
153
154
155
156
# File 'lib/bindata/base.rb', line 149

def klass_lookup(name)
  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
  klass
end

#num_bytes(what = nil) ⇒ Object

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



180
181
182
# File 'lib/bindata/base.rb', line 180

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.



159
160
161
162
163
164
165
# File 'lib/bindata/base.rb', line 159

def read(io)
  # remember the current position in the IO object
  io.instance_eval "def mark; #{io.pos}; end"

  do_read(io)
  done_read
end

#single_value?Boolean

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

Returns:

  • (Boolean)


186
187
188
# File 'lib/bindata/base.rb', line 186

def single_value?
  respond_to? :value
end

#write(io) ⇒ Object

Writes the value for this data to io.



175
176
177
# File 'lib/bindata/base.rb', line 175

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