# = BinReadable # # This mixin solely depends on method read(n), which must be # defined in the class/module where you mix in this module. # # == Todo # # * The name of this module sucks. # # == Authors # # * Michael Neumann # # == Copying # # Copyright (c) 2003 Michael Neumann # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # = BinReadable # # This mixin solely depends on method read(n), which must be # defined in the class/module where you mix in this module. module BinReadable #-- # TODO Would like to get the core functionality this provides into the # System module and then change BinaryReader to depend on that instead. #++ module ByteOrder Native = :Native BigEndian = Big = Network = :BigEndian LittleEndian = Little = :LittleEndian # examines the byte order of the underlying machine def byte_order if [0x12345678].pack("L") == "\x12\x34\x56\x78" BigEndian else LittleEndian end end alias_method :byteorder, :byte_order def little_endian? byte_order == LittleEndian end def big_endian? byte_order == BigEndian end alias_method :little?, :little_endian? alias_method :big?, :big_endian? alias_method :network?, :big_endian? module_function :byte_order, :byteorder module_function :little_endian?, :little? module_function :big_endian?, :big?, :network? end # default is native byte-order def byte_order @byte_order || ByteOrder::Native end def byte_order=(new_byteorder) @byte_order = new_byteorder end alias byteorder byte_order alias byteorder= byte_order= # == 8 bit # no byteorder for 8 bit! def read_word8 ru(1, 'C') end def read_int8 ru(1, 'c') end # == 16 bit # === Unsigned def read_word16_native ru(2, 'S') end def read_word16_little ru(2, 'v') end def read_word16_big ru(2, 'n') end # === Signed def read_int16_native ru(2, 's') end def read_int16_little # swap bytes if native=big (but we want little) ru_swap(2, 's', ByteOrder::Big) end def read_int16_big # swap bytes if native=little (but we want big) ru_swap(2, 's', ByteOrder::Little) end # == 32 bit # === Unsigned def read_word32_native ru(4, 'L') end def read_word32_little ru(4, 'V') end def read_word32_big ru(4, 'N') end # === Signed def read_int32_native ru(4, 'l') end def read_int32_little # swap bytes if native=big (but we want little) ru_swap(4, 'l', ByteOrder::Big) end def read_int32_big # swap bytes if native=little (but we want big) ru_swap(4, 'l', ByteOrder::Little) end # == Aliases alias read_uint8 read_word8 # add some short-cut functions %w(word16 int16 word32 int32).each do |typ| eval %{ alias read_#{typ}_network read_#{typ}_big def read_#{typ}(byte_order = nil) case byte_order || @byte_order when ByteOrder::Native then read_#{typ}_native when ByteOrder::Little then read_#{typ}_little when ByteOrder::Network then read_#{typ}_network else raise ArgumentError end end } end {:word16 => :uint16, :word32 => :uint32}.each do |old, new| ['', '_native', '_little', '_big', '_network'].each do |bo| eval %{ alias read_#{new}#{bo} read_#{old}#{bo} } end end def read_cstring str = "" while (c=readn(1)) != "\0" str << c end str end # read exactly n characters, otherwise raise an exception. def readn(n) str = read(n) raise "couldn't read #{n} characters" if str.nil? or str.size != n str end private # shortcut method for readn+unpack def ru(size, template) readn(size).unpack(template).first end # same as method +ru+, but swap bytes if native byteorder == _byteorder_ def ru_swap(size, template, byteorder) str = readn(size) str.reverse! if ByteOrder.byteorder == byteorder str.unpack(template).first end end # Compatability with old version. BinaryReader = BinReadable