class BinData::Struct

nested data objects.
endian of any numerics in this struct, or in any
:endian
Either :little or :big. This specifies the default
by name.
in #snapshot or #field_names but are still accessible
from the outside world. Hidden fields don’t appear
:hide
A list of the names of fields that are to be hidden
when instantiating it.
optional hash of parameters to pass to this field
type. Name is the name of this field. Params is an
params]. Type is a symbol representing a registered
Each element of the array is of the form [type, name,
:fields

An array specifying the fields for this struct.
an object. These params are:
Parameters may be provided at initialisation to control the behaviour of
== Parameters
obj.field_names =># [“b”, “s”]
[:tuple, :s] ])
[:int16le, :b],
:fields => [ [:int32le, :a],
obj = BinData::Struct.new(:hide => :a,
end
int8 :z
int8 :y
int8 :x
class Tuple < BinData::MultiValue
require ‘bindata’
A Struct is an ordered collection of named data objects.

def _do_num_bytes(name)

to write all fields.
by +name+. If +name+ is nil then returns the number of bytes required
Returns the number of bytes it will take to write the field represented
def _do_num_bytes(name)
  if name.nil?
    instantiate_all
    (@field_objs.inject(0) { |sum, f| sum + f.do_num_bytes }).ceil
  else
    obj = find_obj_for_name(name.to_s)
    obj.nil? ? 0 : obj.do_num_bytes
  end
end

def _do_read(io)

Reads the values for all fields in this object from +io+.
def _do_read(io)
  instantiate_all
  @field_objs.each { |f| f.do_read(io) }
end

def _do_write(io)

Writes the values for all fields in this object to +io+.
def _do_write(io)
  instantiate_all
  @field_objs.each { |f| f.do_write(io) }
end

def _snapshot

Returns a snapshot of this struct as a hash.
def _snapshot
  hash = Snapshot.new
  field_names.each do |name|
    ss = find_obj_for_name(name).snapshot
    hash[name] = ss unless ss.nil?
  end
  hash
end

def clear(name = nil)

is given, clears all fields in the struct.
Clears the field represented by +name+. If no +name+
def clear(name = nil)
  if name.nil?
    @field_objs.each { |f| f.clear unless f.nil? }
  else
    obj = find_obj_for_name(name.to_s)
    obj.clear unless obj.nil?
  end
end

def clear?(name = nil)

is given, returns whether all fields are clear.
Returns if the field represented by +name+ is clear?. If no +name+
def clear?(name = nil)
  if name.nil?
    @field_objs.each do |f|
      return false unless f.nil? or f.clear?
    end
    true
  else
    obj = find_obj_for_name(name.to_s)
    obj.nil? ? true : obj.clear?
  end
end

def done_read

To be called after calling #read.
def done_read
  @field_objs.each { |f| f.done_read unless f.nil? }
end

def field_names(include_hidden = false)

in the listing.
object. +include_hidden+ specifies whether to include hidden names
Returns a list of the names of all fields accessible through this
def field_names(include_hidden = false)
  # collect field names
  names = []
  hidden = no_eval_param(:hide)
  @field_names.each do |name|
    if include_hidden or not hidden.include?(name)
      names << name
    end
  end
  names
end

def find_obj_for_name(name)

Returns the data object that stores values for +name+.
def find_obj_for_name(name)
  idx = @field_names.index(name)
  if idx
    instantiate_obj(idx)
    @field_objs[idx].obj
  else
    nil
  end
end

def inherited(subclass) #:nodoc:

:nodoc:

### DEPRECATION HACK to warn about inheriting from BinData::Struct
def inherited(subclass) #:nodoc:
  if subclass != MultiValue
    # warn about deprecated method - remove before releasing 1.0
    fail "error: inheriting from BinData::Struct has been deprecated. Inherit from BinData::MultiValue instead."
  end
end

def initialize(params = {}, parent = nil)

Creates a new Struct.
def initialize(params = {}, parent = nil)
  super(params, parent)
  # extract field names but don't instantiate the fields
  @field_names = no_eval_param(:fields).collect { |k, n, p| n }
  @field_objs  = []
end

def instantiate_all

Instantiates all fields.
def instantiate_all
  @field_names.each_with_index { |name, i| instantiate_obj(i) }
end

def instantiate_obj(idx)

Instantiates the field object at position +idx+.
def instantiate_obj(idx)
  if @field_objs[idx].nil?
    fklass, fname, fparams = no_eval_param(:fields)[idx]
    @field_objs[idx] = fklass.new(fparams, self)
  end
end

def method_missing(symbol, *args, &block)

def method_missing(symbol, *args, &block)
  name = symbol.id2name
  is_writer = (name[-1, 1] == "=")
  name.chomp!("=")
  # find the object that is responsible for name
  if (obj = find_obj_for_name(name))
    # pass on the request
    if obj.single_value? and is_writer
      obj.value = *args
    elsif obj.single_value?
      obj.value
    else
      obj
    end
  else
    super
  end
end

def offset_of(field)

def offset_of(field)
  idx = @field_names.index(field.to_s)
  if idx
    instantiate_all
    offset = 0
    (0...idx).each do |i|
      this_offset = @field_objs[i].do_num_bytes
      if ::Float === offset and ::Integer === this_offset
        offset = offset.ceil
      end
      offset += this_offset
    end
    offset
  else
    nil
  end
end

def respond_to?(symbol, include_private = false)

def respond_to?(symbol, include_private = false)
  orig_respond_to?(symbol, include_private) ||
    field_names(true).include?(symbol.id2name.chomp("="))
end

def sanitize_parameters!(sanitizer, params)

Ensures that +params+ is of the form expected by #initialize.
def sanitize_parameters!(sanitizer, params)
  # possibly override endian
  endian = params[:endian]
  if endian != nil
    unless [:little, :big].include?(endian)
      raise ArgumentError, "unknown value for endian '#{endian}'"
    end
    params[:endian] = endian
  end
  if params.has_key?(:fields)
    sanitizer.with_endian(endian) do
      # ensure names of fields are strings and that params is sanitized
      all_fields = params[:fields].collect do |ftype, fname, fparams|
        fname = fname.to_s
        klass = sanitizer.lookup_klass(ftype)
        sanitized_fparams = sanitizer.sanitize_params(klass, fparams)
        [klass, fname, sanitized_fparams]
      end
      params[:fields] = all_fields
    end
    # now params are sanitized, check that parameter names are okay
    field_names = []
    instance_methods = self.instance_methods
    reserved_names = RESERVED
    params[:fields].each do |fklass, fname, fparams|
      # check that name doesn't shadow an existing method
      if instance_methods.include?(fname)
        raise NameError.new("Rename field '#{fname}' in #{self}, " +
                            "as it shadows an existing method.", fname)
      end
      # check that name isn't reserved
      if reserved_names.include?(fname)
        raise NameError.new("Rename field '#{fname}' in #{self}, " +
                            "as it is a reserved name.", fname)
      end
      # check for multiple definitions
      if field_names.include?(fname)
        raise NameError.new("field '#{fname}' in #{self}, " +
                            "is defined multiple times.", fname)
      end
      field_names << fname
    end
    # collect all hidden names that correspond to a field name
    hide = []
    if params.has_key?(:hide)
      hidden = (params[:hide] || []).collect { |h| h.to_s }
      all_field_names = params[:fields].collect { |k,n,p| n }
      hide = hidden & all_field_names
    end
    params[:hide] = hide
  end
  super(sanitizer, params)
end

def single_value?

value data objects respond to #value and #value=.
Returns whether this data object contains a single value. Single
def single_value?
  return false
end