class BinData::Struct

:byte_align.
[:byte_align] This field’s rel_offset must be a multiple of
calls to #read, #write, #num_bytes or #snapshot.
if false, this object will not be included in any
[:onlyif] Used to indicate a data object is optional.
Fields may have have extra parameters as listed below:
== Field Parameters
a match is found.
unrecognised, then each prefix is applied until
:search_prefix
Allows abbreviated type names. If a type is
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
that field is anonymous and behaves as a hidden field.
when instantiating it. If name is “” or nil, then
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::Record
require ‘bindata’
A Struct is an ordered collection of named data objects.

def [](key)

def [](key)
  find_obj_for_name(key)
end

def []=(key, value)

def []=(key, value)
  find_obj_for_name(key)&.assign(value)
end

def as_stringified_hash(val)

def as_stringified_hash(val)
  if BinData::Struct === val
    val
  elsif val.nil?
    {}
  else
    hash = Snapshot.new
    val.each_pair { |k, v| hash[k] = v }
    hash
  end
end

def assign(val)

def assign(val)
  clear
  assign_fields(val)
end

def assign_fields(val)

def assign_fields(val)
  src = as_stringified_hash(val)
  @field_names.compact.each do |name|
    obj = find_obj_for_name(name)
    if obj && src.key?(name)
      obj.assign(src[name])
    end
  end
end

def base_field_name(name)

def base_field_name(name)
  name.to_s.sub(/(=|\?)\z/, "").to_sym
end

def clear # :nodoc:

:nodoc:
def clear # :nodoc:
  @field_objs.each { |f| f.nil? || f.clear }
end

def clear? # :nodoc:

:nodoc:
def clear? # :nodoc:
  @field_objs.all? { |f| f.nil? || f.clear? }
end

def debug_name_of(child) # :nodoc:

:nodoc:
def debug_name_of(child) # :nodoc:
  field_name = @field_names[find_index_of(child)]
  "#{debug_name}.#{field_name}"
end

def define_field_accessors

def define_field_accessors
  get_parameter(:fields).each_with_index do |field, i|
    name = field.name_as_sym
    define_field_accessors_for(name, i) if name
  end
end

def define_field_accessors_for(name, index)

def define_field_accessors_for(name, index)
  define_singleton_method(name) do
    instantiate_obj_at(index) if @field_objs[index].nil?
    @field_objs[index]
  end
  define_singleton_method("#{name}=") do |*vals|
    instantiate_obj_at(index) if @field_objs[index].nil?
    @field_objs[index].assign(*vals)
  end
  define_singleton_method("#{name}?") do
    instantiate_obj_at(index) if @field_objs[index].nil?
    include_obj?(@field_objs[index])
  end
end

def do_num_bytes # :nodoc:

:nodoc:
def do_num_bytes # :nodoc:
  instantiate_all_objs
  sum_num_bytes_for_all_fields
end

def do_read(io) # :nodoc:

:nodoc:
def do_read(io) # :nodoc:
  instantiate_all_objs
  @field_objs.each { |f| f.do_read(io) if include_obj_for_io?(f) }
end

def do_write(io) # :nodoc:

:nodoc:
def do_write(io) # :nodoc:
  instantiate_all_objs
  @field_objs.each { |f| f.do_write(io) if include_obj_for_io?(f) }
end

def each_pair(include_all = false)

+include_all+ is true.
Does not include anonymous or hidden fields unless

Calls the given block for each field_name-field_obj pair.
def each_pair(include_all = false)
  instantiate_all_objs
  pairs = @field_names.zip(@field_objs).select do |name, _obj|
    name || include_all
  end
  if block_given?
    pairs.each { |el| yield(el) }
  else
    pairs.each
  end
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)
  if include_hidden
    @field_names.compact
  else
    hidden = get_parameter(:hide) || []
    @field_names.compact - hidden
  end
end

def find_index_of(obj)

def find_index_of(obj)
  @field_objs.index { |el| el.equal?(obj) }
end

def find_obj_for_name(name)

def find_obj_for_name(name)
  index = @field_names.index(base_field_name(name))
  if index
    instantiate_obj_at(index)
    @field_objs[index]
  end
end

def include_obj?(obj)

def include_obj?(obj)
  !obj.has_parameter?(:onlyif) || obj.eval_parameter(:onlyif)
end

def include_obj_for_io?(obj)

def include_obj_for_io?(obj)
  # Used by #do_read and #do_write, to ensure the stream is passed to
  # DelayedIO objects for delayed processing.
  include_obj?(obj) || DelayedIO === obj
end

def initialize_instance

def initialize_instance
  @field_objs = []
end

def initialize_shared_instance

def initialize_shared_instance
  fields = get_parameter(:fields)
  @field_names = fields.field_names.freeze
  extend ByteAlignPlugin if fields.any_field_has_parameter?(:byte_align)
  define_field_accessors
  super
end

def instantiate_all_objs

def instantiate_all_objs
  @field_names.each_index { |i| instantiate_obj_at(i) }
end

def instantiate_obj_at(index)

def instantiate_obj_at(index)
  if @field_objs[index].nil?
    field = get_parameter(:fields)[index]
    @field_objs[index] = field.instantiate(nil, self)
  end
end

def key?(key)

def key?(key)
  @field_names.index(base_field_name(key))
end

def offset_of(child) # :nodoc:

:nodoc:
def offset_of(child) # :nodoc:
  instantiate_all_objs
  sum = sum_num_bytes_below_index(find_index_of(child))
  child.bit_aligned? ? sum.floor : sum.ceil
end

def snapshot

def snapshot
  snapshot = Snapshot.new
  field_names.each do |name|
    obj = find_obj_for_name(name)
    snapshot[name] = obj.snapshot if include_obj?(obj)
  end
  snapshot
end

def sum_num_bytes_below_index(index)

def sum_num_bytes_below_index(index)
  (0...index).inject(0) do |sum, i|
    obj = @field_objs[i]
    if include_obj?(obj)
      nbytes = obj.do_num_bytes
      (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
    else
      sum
    end
  end
end

def sum_num_bytes_for_all_fields

def sum_num_bytes_for_all_fields
  sum_num_bytes_below_index(@field_objs.length)
end