Skip to content

Commit

Permalink
(1.0) Fix for JRUBY-1005, pack support for Q/g
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.codehaus.org/jruby/branches/jruby-1_0@4777 961051c9-f516-0410-bf72-c9f7e237a7b7
  • Loading branch information
headius committed Oct 28, 2007
1 parent 8844e8e commit ddc21e0
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 5 deletions.
55 changes: 55 additions & 0 deletions src/org/jruby/util/Pack.java
Expand Up @@ -45,6 +45,7 @@

import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBignum;
import org.jruby.RubyFloat;
import org.jruby.RubyKernel;
import org.jruby.RubyNumeric;
Expand All @@ -66,6 +67,27 @@ public class Pack {
private static final int[] b64_xtable = new int[256];
private static final Converter[] converters = new Converter[256];

private static final BigInteger QUAD_MIN = BigInteger.valueOf(Long.MIN_VALUE);
private static final BigInteger QUAD_MAX = new BigInteger("ffffffffffffffff", 16);

/*
* convert into longs, returning unsigned 64-bit values as signed longs
* ( num2long raises a RangeError on values > Long.MAX_VALUE )
*/
private static long num2quad(IRubyObject arg) {
if (arg == arg.getRuntime().getNil()) {
return 0L;
}
else if (arg instanceof RubyBignum) {
BigInteger big = ((RubyBignum)arg).getValue();
if (big.compareTo(QUAD_MIN) < 0 || big.compareTo(QUAD_MAX) > 0) {
throw arg.getRuntime().newRangeError("bignum too big to convert into `quad int'");
}
return big.longValue();
}
return RubyNumeric.num2long(arg);
}

static {
hex_table = ByteList.plain("0123456789ABCDEF");
uu_table =
Expand Down Expand Up @@ -193,6 +215,7 @@ public void encode(Ruby runtime, IRubyObject o, StringBuffer result){
converters['I'] = tmp; // unsigned int, native
converters['L'] = tmp; // unsigned long (bugs?)
converters['N'] = tmp; // long, network

tmp = new Converter(4) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeIntBigEndian(enc));
Expand All @@ -205,6 +228,30 @@ public void encode(Ruby runtime, IRubyObject o, StringBuffer result){
};
converters['l'] = tmp; // long, native
converters['i'] = tmp; // int, native

tmp = new Converter(8) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
long l = decodeLongLittleEndian(enc);
return RubyBignum.newBignum(runtime,
BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
}
public void encode(Ruby runtime, IRubyObject o, StringBuffer result){
long l = num2quad(o);
encodeLongBigEndian(result, l);
}
};
converters['Q'] = tmp;

tmp = new Converter(8) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return RubyBignum.newBignum(runtime, decodeLongLittleEndian(enc));
}
public void encode(Ruby runtime, IRubyObject o, StringBuffer result){
long l = num2quad(o);
encodeLongBigEndian(result, l);
}
};
converters['q'] = tmp;
}

/**
Expand Down Expand Up @@ -1224,6 +1271,14 @@ private static final StringBuffer grow(StringBuffer i2Grow, String iPads, int iL
* <td valign="top">Pointer to a null-terminated string</td>
* </tr>
* <tr>
* <td valign="top">Q</td>
* <td valign="top">Unsigned 64-bit number</td>
* </tr>
* <tr>
* <td valign="top">q</td>
* <td valign="top">64-bit number</td>
* </tr>
* <tr>
* <td valign="top">S</td>
* <td valign="top">Unsigned short</td>
* </tr>
Expand Down
56 changes: 51 additions & 5 deletions test/test_pack.rb
@@ -1,6 +1,20 @@
require 'test/unit'

class TestPack < Test::Unit::TestCase
def setup
@char_array = %w/alpha beta gamma/
@int_array = [-1, 0, 1, 128]
@float_array = [-1.5, 0.0, 1.5, 128.5]
@bignum1 = 2**63
end

def teardown
@char_array = nil
@int_array = nil
@float_array = nil
@bignum1 = nil
end

def test_pack_w
assert_equal( "\005", [5].pack('w'))
assert_equal( "\203t", [500].pack('w'))
Expand All @@ -11,10 +25,42 @@ def test_pack_w
end

def test_pack_M
assert_equal("alpha=\n", %w/alpha beta gamma/.pack("M")) # ok
assert_equal("-1=\n", [-1, 0, 1, 128].pack("M"))
assert_equal("1=\n", [1].pack("M"))
assert_equal("-1.5=\n", [-1.5, 0.0, 1.5, 128.5].pack("M"))
assert_equal("9223372036854775808=\n", [9223372036854775808].pack("M"))
assert_equal("alpha=\n", %w/alpha beta gamma/.pack("M")) # ok
assert_equal("-1=\n", [-1, 0, 1, 128].pack("M"))
assert_equal("1=\n", [1].pack("M"))
assert_equal("-1.5=\n", [-1.5, 0.0, 1.5, 128.5].pack("M"))
assert_equal("9223372036854775808=\n", [9223372036854775808].pack("M"))
end

def endian(data, n=4)
[1].pack('I') == [1].pack('N') ? data.gsub(/.{#{n}}/){ |s| s.reverse } : data
end

def test_pack_q
assert_equal(endian("\000\000\000\000\000\000\000\000", 8), [0].pack("q"))
assert_equal(endian("\001\000\000\000\000\000\000\000", 8), [1].pack("q"))
assert_equal(endian("\377\377\377\377\377\377\377\377", 8), [-1].pack("q"))
assert_equal(endian("\377\377\377\377\377\377\377\377", 8), @int_array.pack("q"))
assert_equal(endian("\377\377\377\377\377\377\377\377", 8), @float_array.pack("q"))
assert_equal(endian("\000\000\000\000\000\000\000\200", 8), [@bignum1].pack("q"))
end

def test_pack_q_expected_errors
assert_raises(TypeError){ @char_array.pack("q") }
assert_raises(RangeError){ [(2**128)].pack("q") }
end

def test_pack_Q
assert_equal(endian("\000\000\000\000\000\000\000\000", 8), [0].pack("Q"))
assert_equal(endian("\001\000\000\000\000\000\000\000", 8), [1].pack("Q"))
assert_equal(endian("\377\377\377\377\377\377\377\377", 8), [-1].pack("Q"))
assert_equal(endian("\377\377\377\377\377\377\377\377", 8), @int_array.pack("Q"))
assert_equal(endian("\377\377\377\377\377\377\377\377", 8), @float_array.pack("Q"))
assert_equal(endian("\000\000\000\000\000\000\000\200", 8), [@bignum1].pack("Q"))
end

def test_pack_Q_expected_errors
assert_raises(TypeError){ @char_array.pack("Q") }
assert_raises(RangeError){ [(2**128)].pack("Q") }
end
end

0 comments on commit ddc21e0

Please sign in to comment.