lib/bindata/bits.rb in bindata-1.8.0 vs lib/bindata/bits.rb in bindata-1.8.1
- old
+ new
@@ -4,78 +4,102 @@
# Defines a number of classes that contain a bit based integer.
# The integer is defined by endian and number of bits.
module BitField #:nodoc: all
class << self
- def define_class(nbits, endian)
- name = "Bit#{nbits}"
+ def define_class(nbits, endian, signed = :unsigned)
+ name = ((signed == :signed ) ? "Sbit" : "Bit") + nbits.to_s
name << "le" if endian == :little
unless BinData.const_defined?(name)
BinData.module_eval <<-END
class #{name} < BinData::BasePrimitive
- BitField.define_methods(self, #{nbits}, :#{endian})
+ BitField.define_methods(self, #{nbits}, :#{endian}, :#{signed})
end
END
end
BinData.const_get(name)
end
- def define_methods(bit_class, nbits, endian)
+ def define_methods(bit_class, nbits, endian, signed)
bit_class.module_eval <<-END
def assign(val)
- #{create_clamp_code(nbits)}
+ #{create_clamp_code(nbits, signed)}
super(val)
end
def do_write(io)
- io.writebits(_value, #{nbits}, :#{endian})
+ val = _value
+ #{create_int2uint_code(nbits) if signed == :signed}
+ io.writebits(val, #{nbits}, :#{endian})
end
def do_num_bytes
#{nbits / 8.0}
end
#---------------
private
def read_and_return_value(io)
- io.readbits(#{nbits}, :#{endian})
+ val = io.readbits(#{nbits}, :#{endian})
+ #{create_uint2int_code(nbits) if signed == :signed}
+ val
end
def sensible_default
0
end
END
end
- def create_clamp_code(nbits)
- min = 0
- max = (1 << nbits) - 1
+ def create_clamp_code(nbits, signed)
+ if nbits == 1 and signed == :signed
+ raise "signed bitfield must have more than one bit"
+ end
+
+ if signed == :signed
+ max = (1 << (nbits - 1)) - 1
+ min = -(max + 1)
+ else
+ min = 0
+ max = (1 << nbits) - 1
+ end
+
clamp = "(val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
if nbits == 1
# allow single bits to be used as booleans
clamp = "(val == true) ? 1 : (not val) ? 0 : #{clamp}"
end
"val = #{clamp}"
end
+
+ def create_int2uint_code(nbits)
+ "val = val & #{(1 << nbits) - 1}"
+ end
+
+ def create_uint2int_code(nbits)
+ "val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
+ end
end
end
# Create classes on demand
module BitFieldFactory
def const_missing(name)
mappings = {
/^Bit(\d+)$/ => :big,
- /^Bit(\d+)le$/ => :little
+ /^Bit(\d+)le$/ => :little,
+ /^Sbit(\d+)$/ => [:big, :signed],
+ /^Sbit(\d+)le$/ => [:little, :signed]
}
- mappings.each_pair do |regex, endian|
+ mappings.each_pair do |regex, args|
if regex =~ name.to_s
nbits = $1.to_i
- return BitField.define_class(nbits, endian)
+ return BitField.define_class(nbits, *args)
end
end
super(name)
end