require'bindata/base_primitive'moduleBinData# Defines a number of classes that contain an integer. The integer# is defined by endian, signedness and number of bytes.moduleInt#:nodoc: allclass<<selfdefdefine_class(nbits,endian,signed)name=class_name(nbits,endian,signed)unlessBinData.const_defined?(name)BinData.module_eval<<-END
class #{name} < BinData::BasePrimitive
register_self
Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
end
ENDendBinData.const_get(name)enddefclass_name(nbits,endian,signed)endian_str=(endian==:big)?"be":"le"base=(signed==:signed)?"Int":"Uint""#{base}#{nbits}#{endian_str}"enddefdefine_methods(int_class,nbits,endian,signed)raise"nbits must be divisible by 8"unless(nbits%8).zero?int_class.module_eval<<-END
def assign(val)
#{create_clamp_code(nbits,signed)}
super(val)
end
def do_num_bytes
#{nbits/8}
end
#---------------
private
def sensible_default
0
end
def value_to_binary_string(val)
#{create_clamp_code(nbits,signed)}#{create_int2uint_code(nbits)ifsigned==:signed}#{create_to_binary_s_code(nbits,endian)}
end
def read_and_return_value(io)
val = #{create_read_code(nbits,endian)}#{create_uint2int_code(nbits)ifsigned==:signed}
end
ENDend#-------------privatedefcreate_clamp_code(nbits,signed)ifsigned==:signedmax=(1<<(nbits-1))-1min=-(max+1)elsemin=0max=(1<<nbits)-1end"val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"enddefcreate_int2uint_code(nbits)"val = val & #{(1<<nbits)-1}"enddefcreate_uint2int_code(nbits)mask=(1<<(nbits-1))-1"val = ((val & #{1<<(nbits-1)}).zero?) ? "+"val & #{mask} : -(((~val) & #{mask}) + 1)"enddefcreate_read_code(nbits,endian)bits_per_word=bytes_per_word(nbits)*8nwords=nbits/bits_per_wordnbytes=nbits/8idx=(0...nwords).to_aidx.reverse!if(endian==:big)parts=(0...nwords).collectdo|i|ifi.zero?"a.at(#{idx[i]})"else"(a.at(#{idx[i]}) << #{bits_per_word*i})"endendunpack_str="a = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits,endian)}')"assemble_str=parts.join(" + ")"(#{unpack_str}; #{assemble_str})"enddefcreate_to_binary_s_code(nbits,endian)# special case 8bit integers for speedreturn"val.chr"ifnbits==8bits_per_word=bytes_per_word(nbits)*8nwords=nbits/bits_per_wordmask=(1<<bits_per_word)-1vals=(0...nwords).collectdo|i|i.zero??"val":"(val >> #{bits_per_word*i})"endvals.reverse!if(endian==:big)parts=(0...nwords).collect{|i|"#{vals[i]} & #{mask}"}array_str="["+parts.join(", ")+"]""#{array_str}.pack('#{pack_directive(nbits,endian)}')"enddefbytes_per_word(nbits)(nbits%32).zero??4:(nbits%16).zero??2:1enddefpack_directive(nbits,endian)bits_per_word=bytes_per_word(nbits)*8nwords=nbits/bits_per_wordif(nbits%32).zero?d=(endian==:big)?'N':'V'elsif(nbits%16).zero?d=(endian==:big)?'n':'v'elsed='C'endd*nwordsendendend# Unsigned 1 byte integer.classUint8<BinData::BasePrimitiveregister_selfInt.define_methods(self,8,:little,:unsigned)end# Signed 1 byte integer.classInt8<BinData::BasePrimitiveregister_selfInt.define_methods(self,8,:little,:signed)end# Create classes on demandclass<<selfalias_method:const_missing_without_int,:const_missingdefconst_missing_with_int(name)name=name.to_smappings={/^Uint(\d+)be$/=>[:big,:unsigned],/^Uint(\d+)le$/=>[:little,:unsigned],/^Int(\d+)be$/=>[:big,:signed],/^Int(\d+)le$/=>[:little,:signed],}mappings.each_pairdo|regex,args|ifregex=~namenbits=$1.to_iif(nbits%8).zero?returnInt.define_class(nbits,*args)endendendconst_missing_without_int(name)endalias_method:const_missing,:const_missing_with_intendend