lib/bindata/int.rb in bindata-2.0.0 vs lib/bindata/int.rb in bindata-2.1.0
- old
+ new
@@ -4,12 +4,11 @@
# Defines a number of classes that contain an integer. The integer
# is defined by endian, signedness and number of bytes.
module Int #:nodoc: all
class << self
- def define_class(nbits, endian, signed)
- name = class_name(nbits, endian, signed)
+ def define_class(name, nbits, endian, signed)
unless BinData.const_defined?(name)
BinData.module_eval <<-END
class #{name} < BinData::BasePrimitive
Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
end
@@ -17,17 +16,10 @@
end
BinData.const_get(name)
end
- def class_name(nbits, endian, signed)
- endian_str = (endian == :big) ? "be" : "le"
- base = (signed == :signed) ? "Int" : "Uint"
-
- "#{base}#{nbits}#{endian_str}"
- end
-
def define_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)
@@ -46,18 +38,15 @@
0
end
def value_to_binary_string(val)
#{create_clamp_code(nbits, signed)}
- #{create_int2uint_code(nbits) if signed == :signed}
- #{create_to_binary_s_code(nbits, endian)}
+ #{create_to_binary_s_code(nbits, endian, signed)}
end
def read_and_return_value(io)
- val = #{create_read_code(nbits, endian)}
- #{create_uint2int_code(nbits) if signed == :signed}
- val
+ #{create_read_code(nbits, endian, signed)}
end
END
end
#-------------
@@ -66,84 +55,120 @@
def create_clamp_code(nbits, signed)
if signed == :signed
max = (1 << (nbits - 1)) - 1
min = -(max + 1)
else
- min = 0
max = (1 << nbits) - 1
+ min = 0
end
"val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
end
- def create_int2uint_code(nbits)
- "val = val & #{(1 << nbits) - 1}"
+ def create_read_code(nbits, endian, signed)
+ unpack_str = create_read_unpack_code(nbits, endian, signed)
+ assemble_str = create_read_assemble_code(nbits, endian, signed)
+
+ read_str = "(#{unpack_str} ; #{assemble_str})"
+
+ if need_conversion_code?(nbits, signed)
+ "val = #{read_str} ; #{create_uint2int_code(nbits)}"
+ else
+ read_str
+ end
end
- def create_uint2int_code(nbits)
- "val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
+ def create_read_unpack_code(nbits, endian, signed)
+ nbytes = nbits / 8
+
+ "ints = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits, endian, signed)}')"
end
- def create_read_code(nbits, endian)
+ def create_read_assemble_code(nbits, endian, signed)
bits_per_word = bytes_per_word(nbits) * 8
nwords = nbits / bits_per_word
- nbytes = nbits / 8
idx = (0 ... nwords).to_a
idx.reverse! if (endian == :big)
parts = (0 ... nwords).collect do |i|
if i.zero?
- "a.at(#{idx[i]})"
+ "ints.at(#{idx[i]})"
else
- "(a.at(#{idx[i]}) << #{bits_per_word * i})"
+ "(ints.at(#{idx[i]}) << #{bits_per_word * i})"
end
end
- unpack_str = "a = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits, endian)}')"
assemble_str = parts.join(" + ")
-
- "(#{unpack_str}; #{assemble_str})"
end
- def create_to_binary_s_code(nbits, endian)
+ def create_to_binary_s_code(nbits, endian, signed)
# special case 8bit integers for speed
- return "val.chr" if nbits == 8
+ return "(val & 0xff).chr" if nbits == 8
bits_per_word = bytes_per_word(nbits) * 8
nwords = nbits / bits_per_word
mask = (1 << bits_per_word) - 1
vals = (0 ... nwords).collect do |i|
- i.zero? ? "val" : "(val >> #{bits_per_word * i})"
+ i.zero? ? "val" : "val >> #{bits_per_word * i}"
end
vals.reverse! if (endian == :big)
- parts = (0 ... nwords).collect { |i| "#{vals[i]} & #{mask}" }
- array_str = "[" + parts.join(", ") + "]"
+ array_str = "[" + vals.collect { |val| "#{val} & #{mask}" }.join(", ") + "]" # TODO: "& mask" is needed to work around jruby bug
+ pack_str = "#{array_str}.pack('#{pack_directive(nbits, endian, signed)}')"
- "#{array_str}.pack('#{pack_directive(nbits, endian)}')"
+ if need_conversion_code?(nbits, signed)
+ "#{create_int2uint_code(nbits)} ; #{pack_str}"
+ else
+ pack_str
+ end
end
+ def create_int2uint_code(nbits)
+ "val &= #{(1 << nbits) - 1}"
+ end
+
+ def create_uint2int_code(nbits)
+ "(val >= #{1 << (nbits - 1)}) ? val - #{1 << nbits} : val"
+ end
+
def bytes_per_word(nbits)
- (nbits % 32).zero? ? 4 : (nbits % 16).zero? ? 2 : 1
+ (nbits % 64).zero? ? 8 :
+ (nbits % 32).zero? ? 4 :
+ (nbits % 16).zero? ? 2 :
+ 1
end
- def pack_directive(nbits, endian)
+ def pack_directive(nbits, endian, signed)
bits_per_word = bytes_per_word(nbits) * 8
nwords = nbits / bits_per_word
- if (nbits % 32).zero?
- d = (endian == :big) ? 'N' : 'V'
+ if (nbits % 64).zero?
+ d = (endian == :big) ? 'Q>' : 'Q<'
+ elsif (nbits % 32).zero?
+ d = (endian == :big) ? 'L>' : 'L<'
elsif (nbits % 16).zero?
- d = (endian == :big) ? 'n' : 'v'
+ d = (endian == :big) ? 'S>' : 'S<'
else
d = 'C'
end
- d * nwords
+ if pack_directive_signed?(nbits, signed)
+ (d * nwords).downcase
+ else
+ d * nwords
+ end
end
+
+ def need_conversion_code?(nbits, signed)
+ signed == :signed and not pack_directive_signed?(nbits, signed)
+ end
+
+ def pack_directive_signed?(nbits, signed)
+ signed == :signed and [64, 32, 16, 8].include?(nbits)
+ end
end
end
# Unsigned 1 byte integer.
@@ -158,20 +183,20 @@
# Create classes on demand
module IntFactory
def const_missing(name)
mappings = {
- /^Uint(\d+)be$/ => [:big, :unsigned],
+ /^Uint(\d+)be$/ => [:big, :unsigned],
/^Uint(\d+)le$/ => [:little, :unsigned],
- /^Int(\d+)be$/ => [:big, :signed],
- /^Int(\d+)le$/ => [:little, :signed],
+ /^Int(\d+)be$/ => [:big, :signed],
+ /^Int(\d+)le$/ => [:little, :signed],
}
mappings.each_pair do |regex, args|
if regex =~ name.to_s
nbits = $1.to_i
if (nbits % 8).zero?
- return Int.define_class(nbits, *args)
+ return Int.define_class(name, nbits, *args)
end
end
end
super