class BitGirder::Io::BinaryConverter
Public Class Methods
# 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
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
# File lib/bitgirder/io.rb, line 513 def initialize( *argv ) super( *argv ) @plat_order = BinaryConverter.detect_platform_order end
Public Instance Methods
# File lib/bitgirder/io.rb, line 721 def read_bigdec( s ) read_bigdec_with_info( s )[ 0 ] end
# 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
# File lib/bitgirder/io.rb, line 648 def read_bignum( s ) read_bignum_with_info( s )[ 0 ] end
# 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
# File lib/bitgirder/io.rb, line 608 def read_float32( s ) impl_read_float( s, :s, false ) end
# File lib/bitgirder/io.rb, line 613 def read_float64( s ) impl_read_float( s, :s, true ) end
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
# 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
# File lib/bitgirder/io.rb, line 589 def write_float32( f ) impl_write_float( f, :f, false ) end
# File lib/bitgirder/io.rb, line 594 def write_float64( d ) impl_write_float( d, :d, true ) end
Private Instance Methods
# 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
# 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
# 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
# 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
# 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
# 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
# File lib/bitgirder/io.rb, line 521 def is_plat_order? @order == @plat_order end