Byte strings

It appears that in Ruby2.0 it's impossible to treat strings as mutable byte arrays. Is there any replacement?

FWIW, the string is initialized with:
v = ["00" * len].pack("H*")
and trouble is caused at:
             q,r = q.divmod(128)
             p("q = #{q}, r = #{r}")
             v[i] = r
Where I am told:
"q = 8, r = 0"
test.rb:54:in `[]=': no implicit conversion of Fixnum into String (TypeError)

a test modification to see if 0 was being handled specially:
             v[i] = r unless r == 0
yielded:
"q = 8, r = 0"
"q = 0, r = 8"
test.rb:54:in `[]=': no implicit conversion of Fixnum into String (TypeError)

even though 0x08 is a valid ASCII code (backspace).

In more detail, the code was:
     def i2key(int, len = 18)
         if not len.is_a?(Fixnum) then
             raise TypeError, "len not a Fixnum, == #{len.inspect}"
         end #if
         if len < 2 then
             raise ArgumentError, "len too small, == #{len}"
         end #if
         if not int.is_a?(Integer) then
             raise TypeError, "int not an Integer, == #{int.inspect}"
         end #if
         if int < 0 then
             raise ArgumentError, "int too small, == #{int}"
         end #if
         v = ["00" * len].pack("H*")
         i = len
         q = int
         while (q > 0 and i > 0)
             i = i - 1
             q,r = q.divmod(128)
             p("q = #{q}, r = #{r}")
             v[i] = r unless r == 0
         end #while
         return v
     end #i2key

···

--
Charles Hixson

Have you considered using String#getbyte(index) and String#setbyte(index, integer)?

ratdog:~ mike$ pry
[1] pry(main)> str = ['00' * 10].pack('H*')
=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
[2] pry(main)> str.setbyte(5, 8)
=> 8
[3] pry(main)> str.getbyte(5)
=> 8
[4] pry(main)> str
=> "\x00\x00\x00\x00\x00\b\x00\x00\x00\x00"
[5] pry(main)> RUBY_VERSION
=> "2.0.0"

Hope this helps,

Mike

···

On 2013-09-08, at 3:28 AM, Charles Hixson <charleshixsn@earthlink.net> wrote:

It appears that in Ruby2.0 it's impossible to treat strings as mutable byte arrays. Is there any replacement?

FWIW, the string is initialized with:
v = ["00" * len].pack("H*")
and trouble is caused at:
           q,r = q.divmod(128)
           p("q = #{q}, r = #{r}")
           v[i] = r
Where I am told:
"q = 8, r = 0"
test.rb:54:in `=': no implicit conversion of Fixnum into String (TypeError)

a test modification to see if 0 was being handled specially:
           v[i] = r unless r == 0
yielded:
"q = 8, r = 0"
"q = 0, r = 8"
test.rb:54:in `=': no implicit conversion of Fixnum into String (TypeError)

even though 0x08 is a valid ASCII code (backspace).

In more detail, the code was:
   def i2key(int, len = 18)
       if not len.is_a?(Fixnum) then
           raise TypeError, "len not a Fixnum, == #{len.inspect}"
       end #if
       if len < 2 then
           raise ArgumentError, "len too small, == #{len}"
       end #if
       if not int.is_a?(Integer) then
           raise TypeError, "int not an Integer, == #{int.inspect}"
       end #if
       if int < 0 then
           raise ArgumentError, "int too small, == #{int}"
       end #if
       v = ["00" * len].pack("H*")
       i = len
       q = int
       while (q > 0 and i > 0)
           i = i - 1
           q,r = q.divmod(128)
           p("q = #{q}, r = #{r}")
           v[i] = r unless r == 0
       end #while
       return v
   end #i2key

--

Mike Stok <mike@stok.ca>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.

I just glanced at this, but it looks like you are trying to do this....

p RUBY_VERSION

v = "abcdef"
v[2] = 5 # try 5.to_s ?
p v

Harry

Sorry, but I want to set the ASCII char whose value is 5. A 64 bit Fixnum can be held in 9 bytes if you can code it mod 128. Using standard string translation can take over twice as many. (I didn't calculate it exactly, but lots more. Since the key value is to determine the sort order, I can't use the standard pack("w") routine, either. That produces a string without leading zeros, which won't sort correctly.

···

On 09/07/2013 08:46 PM, Harry Kakueki wrote:

I just glanced at this, but it looks like you are trying to do this....

p RUBY_VERSION

v = "abcdef"
v[2] = 5 # try 5.to_s ?
p v

Harry

--
Charles Hixson

Thanks. Those were the routines I needed to know about. I can't really test it until I write the inverse function, but inspection of the results for:
             #v[i] = r unless r == 0
             v.setbyte(i, r)
looks quite good. Checking individual numbers gives the expected values.
1
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
2
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
3
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03"
4
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04"
8
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b"
16
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10"

···

On 09/07/2013 08:31 PM, Mike Stok wrote:

On 2013-09-08, at 3:28 AM, Charles Hixson <charleshixsn@earthlink.net> wrote:

It appears that in Ruby2.0 it's impossible to treat strings as mutable byte arrays. Is there any replacement?

FWIW, the string is initialized with:
v = ["00" * len].pack("H*")
and trouble is caused at:
            q,r = q.divmod(128)
            p("q = #{q}, r = #{r}")
            v[i] = r
Where I am told:
"q = 8, r = 0"
test.rb:54:in `=': no implicit conversion of Fixnum into String (TypeError)

a test modification to see if 0 was being handled specially:
            v[i] = r unless r == 0
yielded:
"q = 8, r = 0"
"q = 0, r = 8"
test.rb:54:in `=': no implicit conversion of Fixnum into String (TypeError)

even though 0x08 is a valid ASCII code (backspace).

In more detail, the code was:
    def i2key(int, len = 18)
        if not len.is_a?(Fixnum) then
            raise TypeError, "len not a Fixnum, == #{len.inspect}"
        end #if
        if len < 2 then
            raise ArgumentError, "len too small, == #{len}"
        end #if
        if not int.is_a?(Integer) then
            raise TypeError, "int not an Integer, == #{int.inspect}"
        end #if
        if int < 0 then
            raise ArgumentError, "int too small, == #{int}"
        end #if
        v = ["00" * len].pack("H*")
        i = len
        q = int
        while (q > 0 and i > 0)
            i = i - 1
            q,r = q.divmod(128)
            p("q = #{q}, r = #{r}")
            v[i] = r unless r == 0
        end #while
        return v
    end #i2key

Have you considered using String#getbyte(index) and String#setbyte(index, integer)?

http://ruby-doc.org/core-2.0.0/String.html#method-i-getbyte
http://ruby-doc.org/core-2.0.0/String.html#method-i-setbyte

ratdog:~ mike$ pry
[1] pry(main)> str = ['00' * 10].pack('H*')
=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
[2] pry(main)> str.setbyte(5, 8)
=> 8
[3] pry(main)> str.getbyte(5)
=> 8
[4] pry(main)> str
=> "\x00\x00\x00\x00\x00\b\x00\x00\x00\x00"
[5] pry(main)> RUBY_VERSION
=> "2.0.0"

Hope this helps,

Mike

--
Charles Hixson

v[i] = r unless r == 0

This line looked odd to me when I glanced through.
In this line, if r is a Fixnum then you are trying to put a Fixnum into a
String v[i].
If r is a String then when will r == 0 ?
I did not read all of your code, I just looked at the error location first.

Harry