require'bindata/base'moduleBinDataclassBaseoptional_parameter:onlyif,:byte_align# Used by Structend# A Struct is an ordered collection of named data objects.## require 'bindata'## class Tuple < BinData::Record# int8 :x# int8 :y# int8 :z# end## obj = BinData::Struct.new(:hide => :a,# :fields => [ [:int32le, :a],# [:int16le, :b],# [:tuple, :s] ])# obj.field_names =># [:b, :s]### == Parameters## Parameters may be provided at initialisation to control the behaviour of# an object. These params are:## <tt>:fields</tt>:: An array specifying the fields for this struct.# Each element of the array is of the form [type, name,# params]. Type is a symbol representing a registered# type. Name is the name of this field. Params is an# optional hash of parameters to pass to this field# when instantiating it. If name is "" or nil, then# that field is anonymous and behaves as a hidden field.# <tt>:hide</tt>:: A list of the names of fields that are to be hidden# from the outside world. Hidden fields don't appear# in #snapshot or #field_names but are still accessible# by name.# <tt>:endian</tt>:: Either :little or :big. This specifies the default# endian of any numerics in this struct, or in any# nested data objects.## == Field Parameters## Fields may have have extra parameters as listed below:## [<tt>:onlyif</tt>] Used to indicate a data object is optional.# if +false+, this object will not be included in any# calls to #read, #write, #num_bytes or #snapshot.# [<tt>:byte_align</tt>] This field's rel_offset must be a multiple of# <tt>:byte_align</tt>.classStruct<BinData::Basearg_processor:structmandatory_parameter:fieldsoptional_parameters:endian,:hide# These reserved words may not be used as field namesRESERVED=Hash[*(Hash.instance_methods+%w{alias and begin break case class def defined do else elsif
end ensure false for if in module next nil not or redo
rescue retry return self super then true undef unless until
when while yield}+%w{array element index value}).collect{|name|name.to_sym}.uniq.collect{|key|[key,true]}.flatten]definitialize_shared_instancefields=get_parameter(:fields)@field_names=fields.field_names.freezeextendByteAlignPluginiffields.any_field_has_parameter?(:byte_align)define_field_accessorssuperenddefinitialize_instance@field_objs=[]enddefclear#:nodoc:@field_objs.each{|f|f.clearunlessf.nil?}enddefclear?#:nodoc:@field_objs.all?{|f|f.nil?orf.clear?}enddefassign(val)clearassign_fields(val)enddefsnapshotsnapshot=Snapshot.newfield_names.eachdo|name|obj=find_obj_for_name(name)snapshot[name]=obj.snapshotifinclude_obj?(obj)endsnapshotend# Returns a list of the names of all fields accessible through this# object. +include_hidden+ specifies whether to include hidden names# in the listing.deffield_names(include_hidden=false)ifinclude_hidden@field_names.compactelsehidden=get_parameter(:hide)||[]@field_names.compact-hiddenendenddefdebug_name_of(child)#:nodoc:field_name=@field_names[find_index_of(child)]"#{debug_name}.#{field_name}"enddefoffset_of(child)#:nodoc:instantiate_all_objssum=sum_num_bytes_below_index(find_index_of(child))child.do_num_bytes.is_a?(Integer)?sum.ceil:sum.floorenddefdo_read(io)#:nodoc:instantiate_all_objs@field_objs.each{|f|f.do_read(io)ifinclude_obj?(f)}enddefdo_write(io)#:nodocinstantiate_all_objs@field_objs.each{|f|f.do_write(io)ifinclude_obj?(f)}enddefdo_num_bytes#:nodoc:instantiate_all_objssum_num_bytes_for_all_fieldsenddef[](key)find_obj_for_name(key)enddef[]=(key,value)obj=find_obj_for_name(key)ifobjobj.assign(value)endenddefhas_key?(key)@field_names.index(base_field_name(key))enddefeach_pair@field_names.compact.eachdo|name|yield[name,find_obj_for_name(name)]endend#---------------privatedefdefine_field_accessorsget_parameter(:fields).each_with_indexdo|field,i|name=field.name_as_symdefine_field_accessors_for(name,i)ifnameendenddefdefine_field_accessors_for(name,index)define_singleton_method(name)doinstantiate_obj_at(index)if@field_objs[index].nil?@field_objs[index]enddefine_singleton_method("#{name}=")do|*vals|instantiate_obj_at(index)if@field_objs[index].nil?@field_objs[index].assign(*vals)enddefine_singleton_method("#{name}?")doinstantiate_obj_at(index)if@field_objs[index].nil?include_obj?(@field_objs[index])endenddeffind_index_of(obj)@field_objs.index{|el|el.equal?(obj)}enddeffind_obj_for_name(name)index=@field_names.index(base_field_name(name))ifindexinstantiate_obj_at(index)@field_objs[index]elsenilendenddefbase_field_name(name)name.to_s.sub(/(=|\?)\z/,"").to_symenddefinstantiate_all_objs@field_names.each_index{|i|instantiate_obj_at(i)}enddefinstantiate_obj_at(index)if@field_objs[index].nil?field=get_parameter(:fields)[index]@field_objs[index]=field.instantiate(nil,self)endenddefassign_fields(val)src=as_stringified_hash(val)@field_names.compact.eachdo|name|obj=find_obj_for_name(name)ifobjandsrc.has_key?(name)obj.assign(src[name])endendenddefas_stringified_hash(val)ifBinData::Struct===valvalelsifval.nil?{}elsehash=Snapshot.newval.each_pair{|k,v|hash[k]=v}hashendenddefsum_num_bytes_for_all_fieldssum_num_bytes_below_index(@field_objs.length)enddefsum_num_bytes_below_index(index)(0...index).inject(0)do|sum,i|obj=@field_objs[i]ifinclude_obj?(obj)nbytes=obj.do_num_bytes(nbytes.is_a?(Integer)?sum.ceil:sum)+nbyteselsesumendendenddefinclude_obj?(obj)notobj.has_parameter?(:onlyif)orobj.eval_parameter(:onlyif)end# A hash that can be accessed via attributes.classSnapshot<::Hash#:nodoc:def[]=(key,value)superunlessvalue.nil?enddefrespond_to?(symbol,include_private=false)has_key?(symbol)||superenddefmethod_missing(symbol,*args)self[symbol]||superendendend# Align fields to a multiple of :byte_alignmoduleByteAlignPlugindefdo_read(io)initial_offset=io.offsetinstantiate_all_objs@field_objs.eachdo|f|ifinclude_obj?(f)ifalign_obj?(f)io.seekbytes(bytes_to_align(f,io.offset-initial_offset))endf.do_read(io)endendenddefdo_write(io)initial_offset=io.offsetinstantiate_all_objs@field_objs.eachdo|f|ifinclude_obj?(f)ifalign_obj?(f)io.writebytes("\x00"*bytes_to_align(f,io.offset-initial_offset))endf.do_write(io)endendenddefsum_num_bytes_below_index(index)sum=0(0...@field_objs.length).eachdo|i|obj=@field_objs[i]ifinclude_obj?(obj)sum=sum.ceil+bytes_to_align(obj,sum.ceil)ifalign_obj?(obj)breakifi>=indexnbytes=obj.do_num_bytessum=(nbytes.is_a?(Integer)?sum.ceil:sum)+nbytesendendsumenddefbytes_to_align(obj,rel_offset)align=obj.eval_parameter(:byte_align)(align-(rel_offset%align))%alignenddefalign_obj?(obj)obj.has_parameter?(:byte_align)endendclassStructArgProcessor<BaseArgProcessordefsanitize_parameters!(obj_class,params)sanitize_endian(params)sanitize_fields(obj_class,params)sanitize_hide(params)end#-------------privatedefsanitize_endian(params)ifparams.needs_sanitizing?(:endian)endian=params.create_sanitized_endian(params[:endian])params[:endian]=endianparams.endian=endian# sync params[:endian] and params.endianendenddefsanitize_fields(obj_class,params)ifparams.needs_sanitizing?(:fields)fields=params[:fields]params[:fields]=params.create_sanitized_fieldsfields.eachdo|ftype,fname,fparams|params[:fields].add_field(ftype,fname,fparams)endfield_names=sanitized_field_names(params[:fields])ensure_field_names_are_valid(obj_class,field_names)endenddefsanitize_hide(params)ifparams.needs_sanitizing?(:hide)andparams.has_parameter?(:fields)field_names=sanitized_field_names(params[:fields])hfield_names=hidden_field_names(params[:hide])params[:hide]=(hfield_names&field_names)endenddefsanitized_field_names(sanitized_fields)sanitized_fields.field_names.compactenddefhidden_field_names(hidden)(hidden||[]).collect{|h|h.to_sym}enddefensure_field_names_are_valid(obj_class,field_names)reserved_names=BinData::Struct::RESERVEDfield_names.eachdo|name|ifobj_class.method_defined?(name)raiseNameError.new("Rename field '#{name}' in #{obj_class}, "+"as it shadows an existing method.",name)endifreserved_names.include?(name)raiseNameError.new("Rename field '#{name}' in #{obj_class}, "+"as it is a reserved name.",name)endiffield_names.count(name)!=1raiseNameError.new("field '#{name}' in #{obj_class}, "+"is defined multiple times.",name)endendendendend