class BitGirder::Io::BinaryConverter

Public Class Methods

detect_platform_order() click to toggle source
# File lib/bitgirder/io.rb, line 725
def self.detect_platform_order

    case test = [ 1 ].pack( 's' )
        when "\x01\x00" then ORDER_LITTLE_ENDIAN
        when "\x00\x01" then ORDER_BIG_ENDIAN
        else raise "Undetected byte order: #{test.inspect}"
    end
end
new( *argv ) click to toggle source

Set @plat_order as an instance variable instead of as a class constant. We could theoretically set plat order as a class constant, but don’t for 2 reasons. One is that, in the event there is a bug in our detection, we don’t want to fail any class requiring this file, since some code may never touch the BinaryConverter class anyway. Two is that we can use reflection during testing to simulate a different platform byte ordering on a test-by-test basis, rather than having to change a global constant

Calls superclass method
# File lib/bitgirder/io.rb, line 513
def initialize( *argv )
    
    super( *argv )

    @plat_order = BinaryConverter.detect_platform_order
end

Public Instance Methods

read_bigdec( s ) click to toggle source
# File lib/bitgirder/io.rb, line 721
def read_bigdec( s )
    read_bigdec_with_info( s )[ 0 ]
end
read_bigdec_with_info( s ) click to toggle source
# File lib/bitgirder/io.rb, line 700
def read_bigdec_with_info( s )
    
    not_nil( s, :s )

    unscaled, info1 = read_bignum_with_info( s )
    unscaled_len = has_key( info1, :total_len )

    scale = read_int32( s[ unscaled_len, 4 ] )

    info2 = { 
        :total_len => unscaled_len + 4, 
        :unscaled_len => unscaled_len, 
        :scale_len => 4
    }

    num_str = unscaled.to_s( 10 ) + "e" + ( scale ).to_s

    [ BigDecimal.new( num_str ), info2 ]
end
read_bignum( s ) click to toggle source
# File lib/bitgirder/io.rb, line 648
def read_bignum( s )
    read_bignum_with_info( s )[ 0 ]
end
read_bignum_with_info( s ) click to toggle source
# File lib/bitgirder/io.rb, line 653
def read_bignum_with_info( s )

    not_nil( s, :s )
    
    if ( len = s.size ) < 4
        raise "Input does not have a size (input len: #{len})"
    elsif len == 4
        raise "Input has no integer data (input len: #{len})"
    end

    sz = read_int32( s[ 0, 4 ] )
    rem = [ len - 4, len - sz ].min

    if ( rem = s.size - 4 ) < sz
        raise "Input specifies size #{sz} but actually is only of " \
               "length #{rem}"
    end

    info = { :total_len => 4 + sz, :hdr_len => 4, :num_len => sz }
    [ inflate_bignum( s[ 4, sz ] ), info ]
end
read_float32( s ) click to toggle source
# File lib/bitgirder/io.rb, line 608
def read_float32( s )
    impl_read_float( s, :s, false )
end
read_float64( s ) click to toggle source
# File lib/bitgirder/io.rb, line 613
def read_float64( s )
    impl_read_float( s, :s, true )
end
write_bigdec( n ) click to toggle source

write_bigdec (read_bigdec does the inverse) interprets ruby BigDecimal first as an unscaled value and a scale as defined in Java’s BigDecimal class, and then serializes them as the concatenation of write_bignum( unscaled ) and write_int32( scale ), with each component’s byte-order determined by @order

# File lib/bitgirder/io.rb, line 681
def write_bigdec( n )
 
    not_nil( n, :n )
    raise "Not a BigDecimal" unless n.is_a?( BigDecimal )

    sign, unscaled, base, exp = n.split
    raise "Unexpected base: #{base}" unless base == 10
    raise "NaN not supported" if sign == 0

    if ( unscaled_i = unscaled.to_i ) == 0
        write_bignum( 0 ) + write_int32( 0 )
    else
        # sci_exp is the exponent we'd get using scientific notation
        sci_exp = -unscaled.size + exp
        write_bignum( unscaled_i * sign ) + write_int32( sci_exp )
    end
end
write_bignum( i ) click to toggle source
# File lib/bitgirder/io.rb, line 618
def write_bignum( i )
    
    not_nil( i, :i )

    arr = Io.int_to_byte_array( i )
    arr.reverse! if @order == ORDER_BIG_ENDIAN

    res = write_int32( arr.size )
    arr.each { |b| res << b.chr }

    res
end
write_float32( f ) click to toggle source
# File lib/bitgirder/io.rb, line 589
def write_float32( f )
    impl_write_float( f, :f, false )
end
write_float64( d ) click to toggle source
# File lib/bitgirder/io.rb, line 594
def write_float64( d )
    impl_write_float( d, :d, true )
end

Private Instance Methods

get_float_format( is64 ) click to toggle source
# File lib/bitgirder/io.rb, line 565
def get_float_format( is64 )
    
    fmt = 
        case @order
            when ORDER_LITTLE_ENDIAN then 'e'
            when ORDER_BIG_ENDIAN then 'g'
            else raise "Unhandled order: #@order"
        end
    
    fmt = fmt.upcase if is64

    fmt
end
impl_read_float( str, nm, is64 ) click to toggle source
# File lib/bitgirder/io.rb, line 599
def impl_read_float( str, nm, is64 )
    
    not_nil( str, nm )

    fmt = get_float_format( is64 )
    str.unpack( fmt )[ 0 ]
end
impl_read_int( str, nm, fmt ) click to toggle source
# File lib/bitgirder/io.rb, line 537
def impl_read_int( str, nm, fmt )

    not_nil( str, nm )

    str = str.reverse unless is_plat_order?
    str.unpack( fmt )[ 0 ]
end
impl_write_float( num, nm, is64 ) click to toggle source
# File lib/bitgirder/io.rb, line 580
def impl_write_float( num, nm, is64 )
    
    not_nil( num, nm )
    
    fmt = get_float_format( is64 )
    [ num ].pack( fmt )
end
impl_write_int( num, nm, fmt ) click to toggle source
# File lib/bitgirder/io.rb, line 526
def impl_write_int( num, nm, fmt )
    
    not_nil( num, nm )
    res = [ num ].pack( fmt )
    
    res.reverse! unless is_plat_order?

    res
end
inflate_bignum( s ) click to toggle source
# File lib/bitgirder/io.rb, line 632
def inflate_bignum( s )
 
    # Ensure we process bytes in big-endian order
    s = s.reverse if @order == ORDER_LITTLE_ENDIAN

    res = s[ 0, 1 ].unpack( 'c' )[ 0 ] # res is now set and correctly signed

    s[ 1 .. -1 ].each_byte do |b|
        res <<= 8
        res += b
    end

    res
end
is_plat_order?() click to toggle source
# File lib/bitgirder/io.rb, line 521
def is_plat_order?
    @order == @plat_order
end