module BitGirder::Io

Constants

ORDER_BIG_ENDIAN
ORDER_LITTLE_ENDIAN

Private Class Methods

as_encoded( str, enc ) click to toggle source
# File lib/bitgirder/io.rb, line 36
def as_encoded( str, enc )
    
    not_nil( str, :str )
    not_nil( enc, :enc )

    str.encoding == enc ? str : str.encode( enc )
end
as_json( obj ) click to toggle source
# File lib/bitgirder/io.rb, line 220
def as_json( obj ); JSON.generate( not_nil( obj, :obj ) ); end
as_read_src( obj ) { |obj| ... } click to toggle source
# File lib/bitgirder/io.rb, line 207
def as_read_src( obj )
    
    not_nil( obj, :obj )

    case obj
        when IO, Tempfile then yield( obj )
        when String then File.open( file_exists( obj ) ) { |io| yield( io ) }
        else raise TypeError, "Unkown read src: #{obj.class}"
    end
end
as_write_dest( obj ) { |obj| ... } click to toggle source
# File lib/bitgirder/io.rb, line 194
def as_write_dest( obj )
    
    not_nil( obj, :obj )

    case obj
        when IO, Tempfile then yield( obj )
        when String then File.open( obj, "w" ) { |io| yield( io ) }
        else raise TypeError, "Unknown write dest: #{obj.class}"
    end
end
can_connect?( *argv ) click to toggle source
# File lib/bitgirder/io.rb, line 387
def can_connect?( *argv )
    
    raise "Need at least a port" if argv.empty?

    host = argv.first.is_a?( String ) ? argv.shift : "127.0.0.1"

    raise "Need a port" if argv.empty?

    unless ( port = argv.shift ).is_a?( Integer )
        raise TypeError, "Invalid host or port value: #{port.class}" 
    end

    begin
        TCPSocket::new( host, port ).close
        true
    rescue Errno::ECONNREFUSED, Errno::ECONNRESET
        false
    end
end
debug_kill( sig, pid, opts = {} ) click to toggle source
# File lib/bitgirder/io.rb, line 359
def debug_kill( sig, pid, opts = {} )
    
    not_nil( opts, "opts" )
    
    msg = "Sending #{sig} to #{pid}"
    msg += " (#{opts[ :name ]})" if opts.key?( :name )

    BitGirderLogger.get_logger.code( msg )
    Process.kill( sig, pid )
end
debug_wait2( opts ) click to toggle source
# File lib/bitgirder/io.rb, line 335
def debug_wait2( opts )
    
    not_nil( opts, "opts" )
    
    pid = has_key( opts, :pid )

    name = opts[ :name ]
    name_str = name ? "#{name} (pid #{pid})" : pid.to_s
    code( "Waiting on #{name_str}" )

    pid, status = Process::wait2( pid )
    msg = "Process #{pid} exited with status #{status.exitstatus}"

    if status.success?
        code( msg )
    else
        opts[ :check_status ] ? ( raise msg ) : warn( msg )
    end

    [ pid, status ]
end
digest_file( opts ) click to toggle source
# File lib/bitgirder/io.rb, line 71
def digest_file( opts )
    
    dig_cls, file = has_keys( opts, :digest_type, :file )
    buf_sz = opts[ :buffer_size ] || 50 * ( 1 << 10 )

    # Set this now so we can fail before reading file if need be
    out_func = case ot = opts[ :output_type ]
        when nil, :binary then lambda { |dig| dig }
        when :base64 then lambda { |dig| strict_encode64( dig ) }
        else raise "Unhandled output type: #{ot}"
    end

    dig = dig_cls.new

    File.open( file ) do |io|
        
        buf = ""
        while buf = io.read( buf_sz, buf ) 
            dig.update( buf ) 
        end
    end

    out_func.call( dig.digest )
end
dump_json( obj, file ) click to toggle source
# File lib/bitgirder/io.rb, line 228
def dump_json( obj, file )
 
    not_nil( obj, :obj )
    not_nil( file, :file )

    json = as_json( obj )

    as_write_dest( file ) { |io| io.print( json ) }
end
dump_yaml( obj, dest = nil ) click to toggle source
# File lib/bitgirder/io.rb, line 248
def dump_yaml( obj, dest = nil )
    
    not_nil( obj, :obj )

    if dest
        as_write_dest( dest ) { |io| YAML.dump( obj, io ) }
    else
        YAML.dump( obj )
    end
end
enc_utf8() click to toggle source

Lazily load and assert presence of utf8 encoding

# File lib/bitgirder/io.rb, line 22
def enc_utf8

    @@enc_utf8 ||= 
       ( Encoding.find( "utf-8" ) or raise "No utf-8 encoding found (?!)" )
end
ensure_dir( d ) click to toggle source

Effectively enables mdkir_p as an inline function to enable statements like:

some_dir = ensure_dir( some_dir )
# File lib/bitgirder/io.rb, line 285
def ensure_dir( d )

    fu().mkdir_p( d ) unless File.exist?( d )
    d
end
ensure_dirs( *dirs ) click to toggle source
# File lib/bitgirder/io.rb, line 301
def ensure_dirs( *dirs )
    dirs.each { |dir| ensure_dir( dir ) }
end
ensure_parent( file ) click to toggle source

Ensures that the directory referred to as dirname( file ) exists, and returns file itself (not the parent). Fails if dirname does not return anything meaningful for file.

# File lib/bitgirder/io.rb, line 310
def ensure_parent( file )
    
    parent = File.dirname( file )
    raise "No parent exists for #{file}" unless parent
    
    ensure_dir( parent )

    file
end
ensure_wiped( d ) click to toggle source
# File lib/bitgirder/io.rb, line 293
def ensure_wiped( d )
    
    fu().rm_rf( d )
    ensure_dir( d )
end
file_exists( d ) click to toggle source
# File lib/bitgirder/io.rb, line 122
def file_exists( d )
    
    raise "File or directory #{d} does not exist" unless File.exist?( d )
    d
end
first_line( file ) click to toggle source
# File lib/bitgirder/io.rb, line 166
def first_line( file )
    File.open( file ) { |io| io.readline.chomp }
end
fsize( obj ) click to toggle source
# File lib/bitgirder/io.rb, line 142
def fsize( obj )
    
    stat =
        case obj
        when File, Tempfile then File.stat( obj.path )
        when String then File.stat( obj )
        else raise TypeError, "Unhandled type for fsize: #{obj.class}"
        end
    
    stat.size
end
fu() click to toggle source
# File lib/bitgirder/io.rb, line 30
def fu()
    BitGirderLogger.get_logger.is_debug? ? FileUtils::Verbose : FileUtils
end
int_to_byte_array( i ) click to toggle source

Returns i as a little-endian 2’s complement byte array; Algorithm is from stackoverflow.com/questions/5284369/ruby-return-byte-array-containing-twos-complement-representation-of-bignum-fi (with some cosmetic differences, including the return val’s endianness).

Though not stated there, it’s worth noting that the reason for the end condition test of the 7th (sign) bit is to avoid stopping prematurely on inputs such as 0xff00, dropping the sign information from the result

# File lib/bitgirder/io.rb, line 106
def int_to_byte_array( i )
    
    not_nil( i, :i )

    res = []

    begin
        res << ( i & 0xff )
        i >>= 8
    end until ( i == 0 || i == -1 ) && ( res[ -1 ][ 7 ] == i[ 7 ] )

    res
end
is_executable( file ) click to toggle source
# File lib/bitgirder/io.rb, line 270
def is_executable( file )
 
    file_exists( file )
    raise "Not an executable: #{file}" unless File.executable?( file )

    file
end
load_json( file ) click to toggle source
# File lib/bitgirder/io.rb, line 240
def load_json( file )
 
    not_nil( file, :file )
    as_read_src( file ) { |io| parse_json( slurp( io ) ) }
end
load_yaml( src ) click to toggle source
# File lib/bitgirder/io.rb, line 261
def load_yaml( src )
    
    not_nil( src, :src )

    as_read_src( src ) { |io| YAML.load( io ) }
end
mktmpdir( *argv, &blk ) click to toggle source
# File lib/bitgirder/io.rb, line 136
def mktmpdir( *argv, &blk )
    Dir.mktmpdir( *argv, &blk )
end
open_tempfile( basename = nil, *rest, &blk ) click to toggle source
# File lib/bitgirder/io.rb, line 130
def open_tempfile( basename = nil, *rest, &blk )
    
    basename ||= [ "bg-tmp-", ".tmp" ]
    Tempfile::open( basename, *rest, &blk )
end
parse_json( str ) click to toggle source
# File lib/bitgirder/io.rb, line 224
def parse_json( str ); JSON.parse( not_nil( str, :str ) ); end
read_full( io, len, buf = nil ) click to toggle source
# File lib/bitgirder/io.rb, line 372
def read_full( io, len, buf = nil )
    
    args = [ len ]
    args << buf if buf
    buf = io.read( *args )

    if ( sz = buf == nil ? 0 : buf.bytesize ) < len
        raise EOFError.new( "EOF after #{sz} bytes (wanted #{len})" )
    end

    buf
end
slurp( io, blk_sz = 4096 ) click to toggle source
# File lib/bitgirder/io.rb, line 183
def slurp( io, blk_sz = 4096 )
    
    case io
        when IO, Tempfile then slurp_io( io, blk_sz )
        when String then File.open( io ) { |io2| slurp_io( io2, blk_sz ) }
        else raise "Unknown slurp target: #{io} (#{io.class})"
    end
end
slurp_io( io, blk_sz = 4096 ) click to toggle source
# File lib/bitgirder/io.rb, line 172
def slurp_io( io, blk_sz = 4096 )
    
    not_nil( io, :io )

    while s = io.read( blk_sz ); res = res ? res << s : s; end
    
    res
end
strict_decode64( str ) click to toggle source

Despite the name, this method only enforces strictness when running in a ruby >= 1.9 for now, though we may backport and handcode integrity checks at some point for other rubies

# File lib/bitgirder/io.rb, line 60
def strict_decode64( str )
    
    if RUBY_VERSION >= "1.9"
        Base64.strict_decode64( str )
    else
        Base64.decode64( str )
    end
end
strict_encode64( str ) click to toggle source
# File lib/bitgirder/io.rb, line 46
def strict_encode64( str )

    if RUBY_VERSION >= "1.9"
        Base64.strict_encode64( str )
    else
        Base64.encode64( str ).split( /\n/ ).join( "" )
    end
end
which( cmd, fail_on_miss = false ) click to toggle source
# File lib/bitgirder/io.rb, line 322
def which( cmd, fail_on_miss = false )
    
    not_nil( cmd, "cmd" )

    if ( f = `which #{cmd}`.strip ) and f.empty? 
        raise "Cannot find command #{cmd.inspect} in path" if fail_on_miss
    else
        f
    end
end
write_file( text, file ) click to toggle source
# File lib/bitgirder/io.rb, line 158
def write_file( text, file )
 
    fu().mkdir_p( File.dirname( file ) )
    File.open( file, "w" ) { |io| io.print text }
end

Private Instance Methods

as_encoded( str, enc ) click to toggle source
# File lib/bitgirder/io.rb, line 36
def as_encoded( str, enc )
    
    not_nil( str, :str )
    not_nil( enc, :enc )

    str.encoding == enc ? str : str.encode( enc )
end
as_json( obj ) click to toggle source
# File lib/bitgirder/io.rb, line 220
def as_json( obj ); JSON.generate( not_nil( obj, :obj ) ); end
as_read_src( obj ) { |obj| ... } click to toggle source
# File lib/bitgirder/io.rb, line 207
def as_read_src( obj )
    
    not_nil( obj, :obj )

    case obj
        when IO, Tempfile then yield( obj )
        when String then File.open( file_exists( obj ) ) { |io| yield( io ) }
        else raise TypeError, "Unkown read src: #{obj.class}"
    end
end
as_write_dest( obj ) { |obj| ... } click to toggle source
# File lib/bitgirder/io.rb, line 194
def as_write_dest( obj )
    
    not_nil( obj, :obj )

    case obj
        when IO, Tempfile then yield( obj )
        when String then File.open( obj, "w" ) { |io| yield( io ) }
        else raise TypeError, "Unknown write dest: #{obj.class}"
    end
end
can_connect?( *argv ) click to toggle source
# File lib/bitgirder/io.rb, line 387
def can_connect?( *argv )
    
    raise "Need at least a port" if argv.empty?

    host = argv.first.is_a?( String ) ? argv.shift : "127.0.0.1"

    raise "Need a port" if argv.empty?

    unless ( port = argv.shift ).is_a?( Integer )
        raise TypeError, "Invalid host or port value: #{port.class}" 
    end

    begin
        TCPSocket::new( host, port ).close
        true
    rescue Errno::ECONNREFUSED, Errno::ECONNRESET
        false
    end
end
debug_kill( sig, pid, opts = {} ) click to toggle source
# File lib/bitgirder/io.rb, line 359
def debug_kill( sig, pid, opts = {} )
    
    not_nil( opts, "opts" )
    
    msg = "Sending #{sig} to #{pid}"
    msg += " (#{opts[ :name ]})" if opts.key?( :name )

    BitGirderLogger.get_logger.code( msg )
    Process.kill( sig, pid )
end
debug_wait2( opts ) click to toggle source
# File lib/bitgirder/io.rb, line 335
def debug_wait2( opts )
    
    not_nil( opts, "opts" )
    
    pid = has_key( opts, :pid )

    name = opts[ :name ]
    name_str = name ? "#{name} (pid #{pid})" : pid.to_s
    code( "Waiting on #{name_str}" )

    pid, status = Process::wait2( pid )
    msg = "Process #{pid} exited with status #{status.exitstatus}"

    if status.success?
        code( msg )
    else
        opts[ :check_status ] ? ( raise msg ) : warn( msg )
    end

    [ pid, status ]
end
digest_file( opts ) click to toggle source
# File lib/bitgirder/io.rb, line 71
def digest_file( opts )
    
    dig_cls, file = has_keys( opts, :digest_type, :file )
    buf_sz = opts[ :buffer_size ] || 50 * ( 1 << 10 )

    # Set this now so we can fail before reading file if need be
    out_func = case ot = opts[ :output_type ]
        when nil, :binary then lambda { |dig| dig }
        when :base64 then lambda { |dig| strict_encode64( dig ) }
        else raise "Unhandled output type: #{ot}"
    end

    dig = dig_cls.new

    File.open( file ) do |io|
        
        buf = ""
        while buf = io.read( buf_sz, buf ) 
            dig.update( buf ) 
        end
    end

    out_func.call( dig.digest )
end
dump_json( obj, file ) click to toggle source
# File lib/bitgirder/io.rb, line 228
def dump_json( obj, file )
 
    not_nil( obj, :obj )
    not_nil( file, :file )

    json = as_json( obj )

    as_write_dest( file ) { |io| io.print( json ) }
end
dump_yaml( obj, dest = nil ) click to toggle source
# File lib/bitgirder/io.rb, line 248
def dump_yaml( obj, dest = nil )
    
    not_nil( obj, :obj )

    if dest
        as_write_dest( dest ) { |io| YAML.dump( obj, io ) }
    else
        YAML.dump( obj )
    end
end
enc_utf8() click to toggle source

Lazily load and assert presence of utf8 encoding

# File lib/bitgirder/io.rb, line 22
def enc_utf8

    @@enc_utf8 ||= 
       ( Encoding.find( "utf-8" ) or raise "No utf-8 encoding found (?!)" )
end
ensure_dir( d ) click to toggle source

Effectively enables mdkir_p as an inline function to enable statements like:

some_dir = ensure_dir( some_dir )
# File lib/bitgirder/io.rb, line 285
def ensure_dir( d )

    fu().mkdir_p( d ) unless File.exist?( d )
    d
end
ensure_dirs( *dirs ) click to toggle source
# File lib/bitgirder/io.rb, line 301
def ensure_dirs( *dirs )
    dirs.each { |dir| ensure_dir( dir ) }
end
ensure_parent( file ) click to toggle source

Ensures that the directory referred to as dirname( file ) exists, and returns file itself (not the parent). Fails if dirname does not return anything meaningful for file.

# File lib/bitgirder/io.rb, line 310
def ensure_parent( file )
    
    parent = File.dirname( file )
    raise "No parent exists for #{file}" unless parent
    
    ensure_dir( parent )

    file
end
ensure_wiped( d ) click to toggle source
# File lib/bitgirder/io.rb, line 293
def ensure_wiped( d )
    
    fu().rm_rf( d )
    ensure_dir( d )
end
file_exists( d ) click to toggle source
# File lib/bitgirder/io.rb, line 122
def file_exists( d )
    
    raise "File or directory #{d} does not exist" unless File.exist?( d )
    d
end
first_line( file ) click to toggle source
# File lib/bitgirder/io.rb, line 166
def first_line( file )
    File.open( file ) { |io| io.readline.chomp }
end
fsize( obj ) click to toggle source
# File lib/bitgirder/io.rb, line 142
def fsize( obj )
    
    stat =
        case obj
        when File, Tempfile then File.stat( obj.path )
        when String then File.stat( obj )
        else raise TypeError, "Unhandled type for fsize: #{obj.class}"
        end
    
    stat.size
end
fu() click to toggle source
# File lib/bitgirder/io.rb, line 30
def fu()
    BitGirderLogger.get_logger.is_debug? ? FileUtils::Verbose : FileUtils
end
int_to_byte_array( i ) click to toggle source

Returns i as a little-endian 2’s complement byte array; Algorithm is from stackoverflow.com/questions/5284369/ruby-return-byte-array-containing-twos-complement-representation-of-bignum-fi (with some cosmetic differences, including the return val’s endianness).

Though not stated there, it’s worth noting that the reason for the end condition test of the 7th (sign) bit is to avoid stopping prematurely on inputs such as 0xff00, dropping the sign information from the result

# File lib/bitgirder/io.rb, line 106
def int_to_byte_array( i )
    
    not_nil( i, :i )

    res = []

    begin
        res << ( i & 0xff )
        i >>= 8
    end until ( i == 0 || i == -1 ) && ( res[ -1 ][ 7 ] == i[ 7 ] )

    res
end
is_executable( file ) click to toggle source
# File lib/bitgirder/io.rb, line 270
def is_executable( file )
 
    file_exists( file )
    raise "Not an executable: #{file}" unless File.executable?( file )

    file
end
load_json( file ) click to toggle source
# File lib/bitgirder/io.rb, line 240
def load_json( file )
 
    not_nil( file, :file )
    as_read_src( file ) { |io| parse_json( slurp( io ) ) }
end
load_yaml( src ) click to toggle source
# File lib/bitgirder/io.rb, line 261
def load_yaml( src )
    
    not_nil( src, :src )

    as_read_src( src ) { |io| YAML.load( io ) }
end
mktmpdir( *argv, &blk ) click to toggle source
# File lib/bitgirder/io.rb, line 136
def mktmpdir( *argv, &blk )
    Dir.mktmpdir( *argv, &blk )
end
open_tempfile( basename = nil, *rest, &blk ) click to toggle source
# File lib/bitgirder/io.rb, line 130
def open_tempfile( basename = nil, *rest, &blk )
    
    basename ||= [ "bg-tmp-", ".tmp" ]
    Tempfile::open( basename, *rest, &blk )
end
parse_json( str ) click to toggle source
# File lib/bitgirder/io.rb, line 224
def parse_json( str ); JSON.parse( not_nil( str, :str ) ); end
read_full( io, len, buf = nil ) click to toggle source
# File lib/bitgirder/io.rb, line 372
def read_full( io, len, buf = nil )
    
    args = [ len ]
    args << buf if buf
    buf = io.read( *args )

    if ( sz = buf == nil ? 0 : buf.bytesize ) < len
        raise EOFError.new( "EOF after #{sz} bytes (wanted #{len})" )
    end

    buf
end
slurp( io, blk_sz = 4096 ) click to toggle source
# File lib/bitgirder/io.rb, line 183
def slurp( io, blk_sz = 4096 )
    
    case io
        when IO, Tempfile then slurp_io( io, blk_sz )
        when String then File.open( io ) { |io2| slurp_io( io2, blk_sz ) }
        else raise "Unknown slurp target: #{io} (#{io.class})"
    end
end
slurp_io( io, blk_sz = 4096 ) click to toggle source
# File lib/bitgirder/io.rb, line 172
def slurp_io( io, blk_sz = 4096 )
    
    not_nil( io, :io )

    while s = io.read( blk_sz ); res = res ? res << s : s; end
    
    res
end
strict_decode64( str ) click to toggle source

Despite the name, this method only enforces strictness when running in a ruby >= 1.9 for now, though we may backport and handcode integrity checks at some point for other rubies

# File lib/bitgirder/io.rb, line 60
def strict_decode64( str )
    
    if RUBY_VERSION >= "1.9"
        Base64.strict_decode64( str )
    else
        Base64.decode64( str )
    end
end
strict_encode64( str ) click to toggle source
# File lib/bitgirder/io.rb, line 46
def strict_encode64( str )

    if RUBY_VERSION >= "1.9"
        Base64.strict_encode64( str )
    else
        Base64.encode64( str ).split( /\n/ ).join( "" )
    end
end
which( cmd, fail_on_miss = false ) click to toggle source
# File lib/bitgirder/io.rb, line 322
def which( cmd, fail_on_miss = false )
    
    not_nil( cmd, "cmd" )

    if ( f = `which #{cmd}`.strip ) and f.empty? 
        raise "Cannot find command #{cmd.inspect} in path" if fail_on_miss
    else
        f
    end
end
write_file( text, file ) click to toggle source
# File lib/bitgirder/io.rb, line 158
def write_file( text, file )
 
    fu().mkdir_p( File.dirname( file ) )
    File.open( file, "w" ) { |io| io.print text }
end