On 6/4/05, Brian Candler <B.Candler / pobox.com> wrote:
> I'm sure there ought to be a Ruby function to do this, but I've been
> scratching my head whilst going through the Pickaxe book :-)
> 
> I want to encode/decode a positive number to/from a variable-length
> big-endian binary string.
> 
> Originally the best I could come up with was:
> 
>   str = "\001\002\003"
>   val = 0
>   str.each_byte { |b| val = (val << 8) | b }
>   p val
>   # => 66051
> 
>   val = 1234
>   str = ""
>   while (val > 0)
>     str = (val & 0xff).chr + str
>     val >>= 8
>   end
>   p str
>   # => "\004\322"
> 
> pack/unpack seem only to work for fixed lengths, e.g. 2 or 4 bytes.
> 
> Is there a faster or simpler way of doing this in Ruby?
> 
> Then I discovered I can go via hex:
> 
>   p "\001\002\003".unpack("H*")[0].hex
>   # => 66051
> 
>   str = 1234.to_s(16)
>   str = "0#{str}" if str.length % 2 != 0
>   val = [str].pack("H*")
>   p val
>   # => "\004\322"
> 
> That's still pretty nasty. Any better offers?

You're on the right track... It looks like you're just doing too much
work. how about:

  [int.to_s(16)].pack('H*')

to pack it, and:

  string.unpack('H*').first.to_i(16)

to unpack?

Also, if you aren't tied to this exact format, there's the
BER-compressed integer option in #pack/#unpack, which handles
variable-length integers in a nice way:

  [12345678901234567890].pack('w')
    ==>"\201\253\252\252\261\316\330\374\225R"
  [1].pack('w')
    ==>"\001"

cheers,
Mark