module BinData::Integer
def create_clamp_code(min, max)
def create_clamp_code(min, max) "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val" end
def create_int2uint_code(nbits)
def create_int2uint_code(nbits) "val = val & #{(1 << nbits) - 1}" end
def create_int_methods(int_class, nbits, endian)
def create_int_methods(int_class, nbits, endian) max = (1 << (nbits - 1)) - 1 min = -(max + 1) clamp = create_clamp_code(min, max) read = create_read_code(nbits, endian) to_s = create_to_s_code(nbits, endian) int2uint = create_int2uint_code(nbits) uint2int = create_uint2int_code(nbits) define_methods(int_class, nbits / 8, clamp, read, to_s, int2uint, uint2int) end
def create_read_code(nbits, endian)
def create_read_code(nbits, endian) raise "nbits must be divisible by 8" unless (nbits % 8).zero? # determine "word" size and unpack directive if (nbits % 32).zero? bytes_per_word = 4 d = (endian == :big) ? 'N' : 'V' elsif (nbits % 16).zero? bytes_per_word = 2 d = (endian == :big) ? 'n' : 'v' else bytes_per_word = 1 d = 'C' end bits_per_word = bytes_per_word * 8 nwords = nbits / bits_per_word nbytes = nbits / 8 idx = (0 ... nwords).to_a idx.reverse! if (endian == :big) unpack_str = "a = io.readbytes(#{nbytes}).unpack('#{d * nwords}')" parts = (0 ... nwords).collect do |i| i.zero? ? "a.at(#{idx[i]})" : "(a.at(#{idx[i]}) << #{bits_per_word * i})" end assemble_str = parts.join(" + ") "(#{unpack_str}; #{assemble_str})" end
def create_to_s_code(nbits, endian)
def create_to_s_code(nbits, endian) raise "nbits must be divisible by 8" unless (nbits % 8).zero? # special case 8bit integers for speed return "val.chr" if nbits == 8 # determine "word" size and pack directive if (nbits % 32).zero? bytes_per_word = 4 d = (endian == :big) ? 'N' : 'V' elsif (nbits % 16).zero? bytes_per_word = 2 d = (endian == :big) ? 'n' : 'v' else bytes_per_word = 1 d = 'C' end bits_per_word = bytes_per_word * 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})" end vals.reverse! if (endian == :big) parts = (0 ... nwords).collect { |i| "#{vals[i]} & #{mask}" } array_str = "[" + parts.join(", ") + "]" "#{array_str}.pack('#{d * nwords}')" end
def create_uint2int_code(nbits)
def create_uint2int_code(nbits) mask = (1 << (nbits - 1)) - 1 "val = ((val & #{1 << (nbits - 1)}).zero?) ? " + "val & #{mask} : -(((~val) & #{mask}) + 1)" end
def create_uint_methods(int_class, nbits, endian)
def create_uint_methods(int_class, nbits, endian) min = 0 max = (1 << nbits) - 1 clamp = create_clamp_code(min, max) read = create_read_code(nbits, endian) to_s = create_to_s_code(nbits, endian) define_methods(int_class, nbits / 8, clamp, read, to_s) end
def define_class(nbits, endian, signed)
def define_class(nbits, endian, signed) endian_str = (endian == :big) ? "be" : "le" if signed == :signed name = "Int#{nbits}#{endian_str}" creation_method = "create_int_methods" else name = "Uint#{nbits}#{endian_str}" creation_method = "create_uint_methods" end BinData.module_eval <<-END class #{name} < BinData::BasePrimitive register(self.name, self) Integer.#{creation_method}(self, #{nbits}, :#{endian.to_s}) end END end
def define_methods(int_class, nbytes, clamp, read, to_s,
def define_methods(int_class, nbytes, clamp, read, to_s, int2uint = nil, uint2int = nil) int_class.module_eval <<-END #--------------- private def _assign(val) #{clamp} super(val) end def _do_num_bytes(ignored) #{nbytes} end def sensible_default 0 end def value_to_binary_string(val) #{clamp} #{int2uint unless int2uint.nil?} #{to_s} end def read_and_return_value(io) val = #{read} #{uint2int unless uint2int.nil?} end END end