Reading a signed byte in network byte order

Discussion in 'Ruby' started by Robert Evans, Nov 14, 2005.

  1. Robert Evans

    Robert Evans Guest

    Hi,

    In trying to read 4 bytes as a signed integer from an IO in big-
    endian order, is there already a utility to do this in Ruby? I notice
    unpack has lots of combinations already, but seemingly not one for
    this. Maybe I am just missing it?

    Thanks,
    Bob Evans
     
    Robert Evans, Nov 14, 2005
    #1
    1. Advertising

  2. Robert Evans

    Robert Evans Guest

    Solution Comments on Re: Reading a signed byte in network byte order

    Hi,

    So, I came up with a solution, and would be grateful for Ruby style
    tips. It seemed a lot harder to do than it needed to be.

    Any help appreciated.

    Thanks,
    Bob

    The Tests:

    class ByteReaderTest < Test::Unit::TestCase

    def test_read_signed4_negative
    negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
    assert_equal -231451016, signed4(StringIO.new(negative))
    end

    def test_read_signed4_positive
    positive = "" << 0x02 << 0x34 << 0x56 << 0x78
    assert_equal 36984440, signed4(StringIO.new(positive))
    end

    end

    The Solution:

    def signed4(file)
    bits = (file.read(4).unpack('B32'))[0]
    if bits[0..0].eql? "0" # sign bit is positive
    bits.to_i(2)
    else # sign bit is negative
    compute_negative_number_from(bits)
    end
    end

    def compute_negative_number_from(bits)
    twos_complement = flip(bits).join.to_i(2) + 1
    -1 * twos_complement
    end

    def flip(bits)
    bits.scan(/\w/).collect { |bit| (bit.eql?("1")) ? "0" : "1" }
    end


    On Nov 14, 2005, at 3:07 PM, Robert Evans wrote:

    > Hi,
    >
    > In trying to read 4 bytes as a signed integer from an IO in big-
    > endian order, is there already a utility to do this in Ruby? I
    > notice unpack has lots of combinations already, but seemingly not
    > one for this. Maybe I am just missing it?
    >
    > Thanks,
    > Bob Evans
    >
     
    Robert Evans, Nov 15, 2005
    #2
    1. Advertising

  3. Re: Solution Comments on Re: Reading a signed byte in network byte order

    Hi Robert,

    I'm probably missing something here, but how about this? It works for
    your tests at least.

    def signed4(file)
    file.read(4).reverse.unpack('l')[0]
    end

    Cheers,
    Dave

    On 11/16/05, Robert Evans <> wrote:
    > Hi,
    >
    > So, I came up with a solution, and would be grateful for Ruby style
    > tips. It seemed a lot harder to do than it needed to be.
    >
    > Any help appreciated.
    >
    > Thanks,
    > Bob
    >
    > The Tests:
    >
    > class ByteReaderTest < Test::Unit::TestCase
    >
    > def test_read_signed4_negative
    > negative =3D "" << 0xF2 << 0x34 << 0x56 << 0x78
    > assert_equal -231451016, signed4(StringIO.new(negative))
    > end
    >
    > def test_read_signed4_positive
    > positive =3D "" << 0x02 << 0x34 << 0x56 << 0x78
    > assert_equal 36984440, signed4(StringIO.new(positive))
    > end
    >
    > end
    >
    > The Solution:
    >
    > def signed4(file)
    > bits =3D (file.read(4).unpack('B32'))[0]
    > if bits[0..0].eql? "0" # sign bit is positive
    > bits.to_i(2)
    > else # sign bit is negative
    > compute_negative_number_from(bits)
    > end
    > end
    >
    > def compute_negative_number_from(bits)
    > twos_complement =3D flip(bits).join.to_i(2) + 1
    > -1 * twos_complement
    > end
    >
    > def flip(bits)
    > bits.scan(/\w/).collect { |bit| (bit.eql?("1")) ? "0" : "1" }
    > end
    >
    >
    > On Nov 14, 2005, at 3:07 PM, Robert Evans wrote:
    >
    > > Hi,
    > >
    > > In trying to read 4 bytes as a signed integer from an IO in big-
    > > endian order, is there already a utility to do this in Ruby? I
    > > notice unpack has lots of combinations already, but seemingly not
    > > one for this. Maybe I am just missing it?
    > >
    > > Thanks,
    > > Bob Evans
    > >

    >
    >
    >
     
    David Balmain, Nov 15, 2005
    #3
  4. Robert Evans

    Ara.T.Howard Guest

    Re: Solution Comments on Re: Reading a signed byte in network byteorder

    On Wed, 16 Nov 2005, Robert Evans wrote:

    > Hi,
    >
    > So, I came up with a solution, and would be grateful for Ruby style tips. It
    > seemed a lot harder to do than it needed to be.
    >
    > Any help appreciated.
    >
    > Thanks,
    > Bob
    >
    > The Tests:
    >
    > class ByteReaderTest < Test::Unit::TestCase
    >
    > def test_read_signed4_negative
    > negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
    > assert_equal -231451016, signed4(StringIO.new(negative))
    > end
    >
    > def test_read_signed4_positive
    > positive = "" << 0x02 << 0x34 << 0x56 << 0x78
    > assert_equal 36984440, signed4(StringIO.new(positive))
    > end
    >
    > end
    >
    > The Solution:
    >
    > def signed4(file)
    > bits = (file.read(4).unpack('B32'))[0]
    > if bits[0..0].eql? "0" # sign bit is positive
    > bits.to_i(2)
    > else # sign bit is negative
    > compute_negative_number_from(bits)
    > end
    > end
    >
    > def compute_negative_number_from(bits)
    > twos_complement = flip(bits).join.to_i(2) + 1
    > -1 * twos_complement
    > end
    >
    > def flip(bits)
    > bits.scan(/\w/).collect { |bit| (bit.eql?("1")) ? "0" : "1" }
    > end


    harp:~ > cat a.rb
    require "test/unit"
    class ByteReaderTest < Test::Unit::TestCase
    MAX_POS = 2 ** 31 -1
    BIG_ENDIAN = [42].pack('N') == [42].pack('i')
    def test_read_signed4_positive
    positive = [0x02, 0x34, 0x56, 0x78].pack "c*"
    assert_equal 36984440, signed4(positive)
    end
    def test_read_signed4_negative
    negative = [0xF2, 0x34, 0x56, 0x78].pack "c*"
    assert_equal -231451016, signed4(negative)
    end
    def signed4 buf
    buf = buf.read 4 if buf.respond_to? "read"
    raise RangeError unless buf.size == 4
    n = buf.unpack('N').first
    if n > MAX_POS
    (BIG_ENDIAN ? buf : buf.reverse).unpack('l').first
    else
    n
    end
    end
    end


    harp:~ > ruby a.rb
    Loaded suite a
    Started
    ..
    Finished in 0.000583 seconds.

    2 tests, 2 assertions, 0 failures, 0 errors


    -a
    --
    ===============================================================================
    | ara [dot] t [dot] howard [at] gmail [dot] com
    | all happiness comes from the desire for others to be happy. all misery
    | comes from the desire for oneself to be happy.
    | -- bodhicaryavatara
    ===============================================================================
     
    Ara.T.Howard, Nov 15, 2005
    #4
  5. Robert Evans

    Robert Evans Guest

    Re: Solution Comments on Re: Reading a signed byte in network byte order

    Hi David,

    The only thing I worried about that was according to the PickAxe v.2
    book, I is in native order. I thought that would be a problem since I
    am running this on unix boxes and windows boxes. Is that a true concern?

    Thanks for your reply,
    Bob


    On Nov 15, 2005, at 9:40 AM, David Balmain wrote:

    > Hi Robert,
    >
    > I'm probably missing something here, but how about this? It works for
    > your tests at least.
    >
    > def signed4(file)
    > file.read(4).reverse.unpack('l')[0]
    > end
    >
    > Cheers,
    > Dave
    >
    > On 11/16/05, Robert Evans <> wrote:
    >> Hi,
    >>
    >> So, I came up with a solution, and would be grateful for Ruby style
    >> tips. It seemed a lot harder to do than it needed to be.
    >>
    >> Any help appreciated.
    >>
    >> Thanks,
    >> Bob
    >>
    >> The Tests:
    >>
    >> class ByteReaderTest < Test::Unit::TestCase
    >>
    >> def test_read_signed4_negative
    >> negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
    >> assert_equal -231451016, signed4(StringIO.new(negative))
    >> end
    >>
    >> def test_read_signed4_positive
    >> positive = "" << 0x02 << 0x34 << 0x56 << 0x78
    >> assert_equal 36984440, signed4(StringIO.new(positive))
    >> end
    >>
    >> end
    >>
    >> The Solution:
    >>
    >> def signed4(file)
    >> bits = (file.read(4).unpack('B32'))[0]
    >> if bits[0..0].eql? "0" # sign bit is positive
    >> bits.to_i(2)
    >> else # sign bit is negative
    >> compute_negative_number_from(bits)
    >> end
    >> end
    >>
    >> def compute_negative_number_from(bits)
    >> twos_complement = flip(bits).join.to_i(2) + 1
    >> -1 * twos_complement
    >> end
    >>
    >> def flip(bits)
    >> bits.scan(/\w/).collect { |bit| (bit.eql?("1")) ? "0" : "1" }
    >> end
    >>
    >>
    >> On Nov 14, 2005, at 3:07 PM, Robert Evans wrote:
    >>
    >>> Hi,
    >>>
    >>> In trying to read 4 bytes as a signed integer from an IO in big-
    >>> endian order, is there already a utility to do this in Ruby? I
    >>> notice unpack has lots of combinations already, but seemingly not
    >>> one for this. Maybe I am just missing it?
    >>>
    >>> Thanks,
    >>> Bob Evans
    >>>

    >>
    >>
    >>

    >
     
    Robert Evans, Nov 15, 2005
    #5
  6. Robert Evans

    Robert Evans Guest

    Re: Solution Comments on Re: Reading a signed byte in network byte order

    Sweet. That's a very slick answer. Thanks.

    Bob


    On Nov 15, 2005, at 9:56 AM, Ara.T.Howard wrote:

    > On Wed, 16 Nov 2005, Robert Evans wrote:
    >
    >> Hi,
    >>
    >> So, I came up with a solution, and would be grateful for Ruby
    >> style tips. It seemed a lot harder to do than it needed to be.
    >>
    >> Any help appreciated.
    >>
    >> Thanks,
    >> Bob
    >>
    >> The Tests:
    >>
    >> class ByteReaderTest < Test::Unit::TestCase
    >>
    >> def test_read_signed4_negative
    >> negative = "" << 0xF2 << 0x34 << 0x56 << 0x78
    >> assert_equal -231451016, signed4(StringIO.new(negative))
    >> end
    >>
    >> def test_read_signed4_positive
    >> positive = "" << 0x02 << 0x34 << 0x56 << 0x78
    >> assert_equal 36984440, signed4(StringIO.new(positive))
    >> end
    >>
    >> end
    >>
    >> The Solution:
    >>
    >> def signed4(file)
    >> bits = (file.read(4).unpack('B32'))[0]
    >> if bits[0..0].eql? "0" # sign bit is positive
    >> bits.to_i(2)
    >> else # sign bit is negative
    >> compute_negative_number_from(bits)
    >> end
    >> end
    >>
    >> def compute_negative_number_from(bits)
    >> twos_complement = flip(bits).join.to_i(2) + 1
    >> -1 * twos_complement
    >> end
    >>
    >> def flip(bits)
    >> bits.scan(/\w/).collect { |bit| (bit.eql?("1")) ? "0" : "1" }
    >> end

    >
    > harp:~ > cat a.rb
    > require "test/unit"
    > class ByteReaderTest < Test::Unit::TestCase
    > MAX_POS = 2 ** 31 -1
    > BIG_ENDIAN = [42].pack('N') == [42].pack('i')
    > def test_read_signed4_positive
    > positive = [0x02, 0x34, 0x56, 0x78].pack "c*"
    > assert_equal 36984440, signed4(positive)
    > end
    > def test_read_signed4_negative
    > negative = [0xF2, 0x34, 0x56, 0x78].pack "c*"
    > assert_equal -231451016, signed4(negative)
    > end
    > def signed4 buf
    > buf = buf.read 4 if buf.respond_to? "read"
    > raise RangeError unless buf.size == 4
    > n = buf.unpack('N').first
    > if n > MAX_POS
    > (BIG_ENDIAN ? buf : buf.reverse).unpack('l').first
    > else
    > n
    > end
    > end
    > end
    >
    >
    > harp:~ > ruby a.rb
    > Loaded suite a
    > Started
    > ..
    > Finished in 0.000583 seconds.
    >
    > 2 tests, 2 assertions, 0 failures, 0 errors
    >
    >
    > -a
    > --
    > ======================================================================
    > =========
    > | ara [dot] t [dot] howard [at] gmail [dot] com
    > | all happiness comes from the desire for others to be happy. all
    > misery
    > | comes from the desire for oneself to be happy.
    > | -- bodhicaryavatara
    > ======================================================================
    > =========
    >
    >
     
    Robert Evans, Nov 15, 2005
    #6
  7. Robert Evans wrote:
    > Hi,
    >
    > In trying to read 4 bytes as a signed integer from an IO in big- endian
    > order, is there already a utility to do this in Ruby? I notice unpack
    > has lots of combinations already, but seemingly not one for this. Maybe
    > I am just missing it?
    >
    > Thanks,
    > Bob Evans


    You can unpack with "N" and then use the following to interpret that
    positive Integer as a signed number in 32 bit two's complement
    representation and convert it to a positive or negative Integer:

    length = 32 # bits
    max = 2**length-1
    mid = 2**(length-1)
    to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}

    For example:

    irb(main):012:0> to_signed[4294967295]
    => -1

    This is a snippet from the implementation of my bit-struct lib (see
    RAA). A BitStruct is basically a string with some extra accessors and
    convenience methods. Currently it handles fields that are either
    multiple bytes or 1-7 bits within a byte. (Eventually, longer odd-size
    bit fields would be nice.) Also supports fields for: fixed length char
    strings, null-terminated strings, hex octets, decimal octets, floats,
    nested BitStructs, and "rest"--the rest of the string after defined
    fields. It's *really* useful for playing with net protocols in pure
    ruby. (Someday, I'll probably write a C extension for efficiency.)

    Example:

    require 'bit-struct'

    class C < BitStruct
    signed :foo, 32, "Something signed"
    unsigned :bar, 32, "Something UNsigned"
    end

    c = C.new

    c.foo = -12345678
    c.bar = 12345678

    puts "-"*40
    p c

    puts "-"*40
    p c.to_s

    puts "-"*40
    puts c.inspect_detailed

    puts "-"*40
    p c.to_h

    puts "-"*40
    puts C.describe

    __END__

    ----------------------------------------
    #<C foo=-12345678, bar=12345678>
    ----------------------------------------
    "\377C\236\262\000\274aN"
    ----------------------------------------
    C:
    Something signed = -12345678
    Something UNsigned = 12345678
    ----------------------------------------
    {:bar=>12345678, :foo=>-12345678}
    ----------------------------------------
    byte: type name [size] description
    ----------------------------------------------------------------------
    @0: signed foo [ 32b] Something signed
    @4: unsigned bar [ 32b] Something UNsigned


    --
    vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
     
    Joel VanderWerf, Nov 15, 2005
    #7
  8. Oh, yeah, I forgot to show that you can use a BitStruct to parse strings
    according to your defined format:

    Joel VanderWerf wrote:
    > require 'bit-struct'
    >
    > class C < BitStruct
    > signed :foo, 32, "Something signed"
    > unsigned :bar, 32, "Something UNsigned"
    > end


    c = C.new(socket.recv(...))

    p c.foo

    and so on.

    --
    vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
     
    Joel VanderWerf, Nov 15, 2005
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Luke Skywalker

    Network Byte Order ...

    Luke Skywalker, Nov 11, 2003, in forum: Java
    Replies:
    12
    Views:
    9,234
    Steve Horsley
    Nov 14, 2003
  2. Jesse Engle

    network byte order

    Jesse Engle, Feb 27, 2004, in forum: C Programming
    Replies:
    2
    Views:
    539
    Dan Pop
    Mar 1, 2004
  3. DaBeef
    Replies:
    0
    Views:
    354
    DaBeef
    Nov 2, 2005
  4. Evan
    Replies:
    4
    Views:
    804
    Mark Tolonen
    Mar 5, 2009
  5. Replies:
    11
    Views:
    396
Loading...

Share This Page