Class: BinData::SingleValue

Inherits:
Single show all
Defined in:
lib/bindata/single_value.rb

Overview

A SingleValue is a declarative way to define a new BinData data type. The data type must contain a single value only. For new data types that contain multiple values see BinData::MultiValue.

To define a new data type, set fields as if for MultiValue and add a #get and #set method to extract / convert the data between the fields and the #value of the object.

require 'bindata'

class PascalString < BinData::SingleValue
  uint8  :len,  :value => lambda { data.length }
  string :data, :read_length => :len

  def get
    self.data
  end

  def set(v)
    self.data = v
  end
end

ps = PascalString.new(:initial_value => "hello")
ps.to_s #=> "\005hello"
ps.read("\003abcde")
ps.value #=> "abc"

# Unsigned 24 bit big endian integer
class Uint24be < BinData::SingleValue
  uint8 :byte1
  uint8 :byte2
  uint8 :byte3

  def get
    (self.byte1 << 16) | (self.byte2 << 8) | self.byte3
  end

  def set(v)
    v = 0 if v < 0
    v = 0xffffff if v > 0xffffff

    self.byte1 = (v >> 16) & 0xff
    self.byte2 = (v >>  8) & 0xff
    self.byte3 =  v        & 0xff
  end
end

u24 = Uint24be.new
u24.read("\x12\x34\x56")
"0x%x" % u24.value #=> 0x123456

Parameters

SingleValue objects accept all the parameters that BinData::Single do.

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Single

#_do_read, #_num_bytes, #_write, all_possible_field_names, #clear, #clear?, #done_read, #field_names, #single_value?, #snapshot, #value, #value=

Methods inherited from Base

accepted_parameters, #clear, default_parameters, #do_read, #done_read, #field_names, #inspect, lookup, mandatory_parameters, mutually_exclusive_parameters, #num_bytes, optional_parameters, read, #read, register, #single_value?, #snapshot, #to_s, #write

Constructor Details

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

Returns a new instance of SingleValue.



150
151
152
153
154
# File 'lib/bindata/single_value.rb', line 150

def initialize(params = {}, env = nil)
  super(params, env)

  @struct = BinData::Struct.new(param(:struct_params), create_env)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args, &block) ⇒ Object

Forward method calls to the internal struct.



157
158
159
160
161
162
163
# File 'lib/bindata/single_value.rb', line 157

def method_missing(symbol, *args, &block)
  if @struct.respond_to?(symbol)
    @struct.__send__(symbol, *args, &block)
  else
    super
  end
end

Class Method Details

.endian(endian = nil) ⇒ Object

Returns or sets the endianess of numerics used in this stucture. Endianess is applied to the fields of this structure. Valid values are :little and :big.



73
74
75
76
77
78
79
80
81
# File 'lib/bindata/single_value.rb', line 73

def endian(endian = nil)
  @endian ||= nil
  if [:little, :big].include?(endian)
    @endian = endian
  elsif endian != nil
    raise ArgumentError, "unknown value for endian '#{endian}'"
  end
  @endian
end

.fieldsObject

Returns all stored fields. Should only be called by #sanitize_parameters



85
86
87
# File 'lib/bindata/single_value.rb', line 85

def fields
  @fields || []
end

.inherited(subclass) ⇒ Object

Register the names of all subclasses of this class.



66
67
68
# File 'lib/bindata/single_value.rb', line 66

def inherited(subclass) #:nodoc:
  register(subclass.name, subclass)
end

.method_missing(symbol, *args) ⇒ Object

Used to define fields for the internal structure.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/bindata/single_value.rb', line 90

def method_missing(symbol, *args)
  name, params = args

  type = symbol
  name = (name.nil? or name == "") ? nil : name.to_s
  params ||= {}

  # note that fields are stored in an instance variable not a class var
  @fields ||= []

  # check that type is known
  if lookup(type, endian).nil?
    raise TypeError, "unknown type '#{type}' for #{self}", caller
  end

  # check that name is okay
  if name != nil
    # check for duplicate names
    @fields.each do |t, n, p|
      if n == name
        raise SyntaxError, "duplicate field '#{name}' in #{self}", caller
      end
    end

    # check that name doesn't shadow an existing method
    if self.instance_methods.include?(name)
      raise NameError.new("", name),
            "field '#{name}' shadows an existing method", caller
    end
  end

  # remember this field.  These fields will be recalled upon creating
  # an instance of this class
  @fields.push([type, name, params])
end

.sanitize_parameters(params, endian = nil) ⇒ Object

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



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/bindata/single_value.rb', line 128

def sanitize_parameters(params, endian = nil)
  params = params.dup

  # possibly override endian
  endian = self.endian || endian

  hash = {}
  hash[:fields] = self.fields

  unless endian.nil?
    hash[:endian] = endian
  end
  
  params[:struct_params] = hash

  super(params, endian)
end