# -*- coding: binary -*- class BitStruct # Class for signed integers in network order, 1-16 bits, or 8n bits. # Declared with BitStruct.signed. class SignedField < Field # Used in describe. def self.class_name @class_name ||= "signed" end def add_accessors_to(cl, attr = name) # :nodoc: offset_byte = offset / 8 offset_bit = offset % 8 length_bit = offset_bit + length length_byte = (length_bit/8.0).ceil last_byte = offset_byte + length_byte - 1 max = 2**length-1 mid = 2**(length-1) max_unsigned = 2**length to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n} # to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n} divisor = options[:fixed] || options["fixed"] divisor_f = divisor && divisor.to_f # if divisor and not divisor.is_a? Fixnum # raise ArgumentError, "fixed-point divisor must be a fixnum" # end endian = (options[:endian] || options["endian"]).to_s case endian when "native" ctl = length_byte <= 2 ? "s" : "l" if length == 16 or length == 32 to_signed = proc {|n| n} # with pack support, to_signed can be replaced with no-op end when "little" ctl = length_byte <= 2 ? "v" : "V" when "network", "big", "" ctl = length_byte <= 2 ? "n" : "N" else raise ArgumentError, "Unrecognized endian option: #{endian.inspect}" end data_is_big_endian = ([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N")) if length_byte == 1 rest = 8 - length_bit mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord cl.class_eval do if divisor define_method attr do || to_signed[(self[offset_byte] & mask) >> rest] / divisor_f end define_method "#{attr}=" do |val| val = (val * divisor).round self[offset_byte] = (self[offset_byte] & mask2) | ((val<> rest] end define_method "#{attr}=" do |val| self[offset_byte] = (self[offset_byte] & mask2) | ((val< 0 bytes.push val % 256 val = val >> 8 end if bytes.length < length_byte bytes.concat [0] * (length_byte - bytes.length) end bytes.reverse! if data_is_big_endian bytes.pack("C*") end if divisor define_method attr do || to_signed[reader_helper[self[byte_range]] / divisor_f] end define_method "#{attr}=" do |val| self[byte_range] = writer_helper[(val * divisor).round] end else define_method attr do || to_signed[reader_helper[self[byte_range]]] end define_method "#{attr}=" do |val| self[byte_range] = writer_helper[val] end end end end elsif length_byte == 2 # unaligned field that fits within two whole bytes byte_range = offset_byte..last_byte rest = 16 - length_bit mask = ["0"*offset_bit + "1"*length + "0"*rest] mask = mask.pack("B16").unpack(ctl).first mask2 = ["1"*offset_bit + "0"*length + "1"*rest] mask2 = mask2.pack("B16").unpack(ctl).first cl.class_eval do if divisor define_method attr do || to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] / divisor_f end define_method "#{attr}=" do |val| val = (val * divisor).round x = (self[byte_range].unpack(ctl).first & mask2) | ((val<> rest] end define_method "#{attr}=" do |val| x = (self[byte_range].unpack(ctl).first & mask2) | ((val<> rest)] / divisor_f end define_method "#{attr}=" do |val| val = (val * divisor).round bytes = self[byte_range] bytes << 0 x = (bytes.unpack(ctl).first & mask2) | ((val<> rest] end define_method "#{attr}=" do |val| bytes = self[byte_range] bytes << 0 x = (bytes.unpack(ctl).first & mask2) | ((val<