summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-02-26 18:51:02 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-02-26 18:51:02 +0000
commit8c9024763f6340d0fc64e21dd6ee052af8c6748b (patch)
treef1ad29586da4aec8a4b8b3fd265e6c456812afef
parent069d271eb5b237b2274c382c5da011b43b0fa1d9 (diff)
* pack.c: fix q and Q for big endian environments which have no
8 bytes integer type. (pack_pack): use rb_big_pack. (pack_unpack): use rb_big_unpack. * include/ruby/intern.h (rb_big_pack): declared. (rb_big_unpack): ditto. * bignum.c (rb_big_pack): new function. (rb_big_unpack): ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26771 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog13
-rw-r--r--bignum.c83
-rw-r--r--include/ruby/intern.h2
-rw-r--r--pack.c65
4 files changed, 140 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index 0f0d424737..535f747d00 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+Sat Feb 27 03:48:18 2010 Tanaka Akira <akr@fsij.org>
+
+ * pack.c: fix q and Q for big endian environments which have no
+ 8 bytes integer type.
+ (pack_pack): use rb_big_pack.
+ (pack_unpack): use rb_big_unpack.
+
+ * include/ruby/intern.h (rb_big_pack): declared.
+ (rb_big_unpack): ditto.
+
+ * bignum.c (rb_big_pack): new function.
+ (rb_big_unpack): ditto.
+
Fri Feb 26 21:36:51 2010 Tanaka Akira <akr@fsij.org>
* bignum.c: fix rb_quad_pack and rb_quad_unpack for environments
diff --git a/bignum.c b/bignum.c
index de1d11f954..451ed5a8e0 100644
--- a/bignum.c
+++ b/bignum.c
@@ -304,6 +304,89 @@ rb_int2inum(SIGNED_VALUE n)
return rb_int2big(n);
}
+void
+rb_big_pack(VALUE val, unsigned long *buf, long num_longs)
+{
+ val = rb_to_int(val);
+ if (num_longs == 0)
+ return;
+ if (FIXNUM_P(val)) {
+ long i;
+ long tmp = FIX2LONG(val);
+ buf[0] = (unsigned long)tmp;
+ tmp = tmp < 0 ? ~0L : 0;
+ for (i = 1; i < num_longs; i++)
+ buf[i] = (unsigned long)tmp;
+ return;
+ }
+ else {
+ long len = RBIGNUM_LEN(val);
+ BDIGIT *ds = BDIGITS(val), *dend = ds + len;
+ long i, j;
+ for (i = 0; i < num_longs && ds < dend; i++) {
+ unsigned long l = 0;
+ for (j = 0; j < SIZEOF_LONG/SIZEOF_BDIGITS && ds < dend; j++, ds++) {
+ l |= ((unsigned long)*ds << (j * SIZEOF_BDIGITS * CHAR_BIT));
+ }
+ buf[i] = l;
+ }
+ for (; i < num_longs; i++)
+ buf[i] = 0;
+ if (RBIGNUM_NEGATIVE_P(val)) {
+ for (i = 0; i < num_longs; i++) {
+ buf[i] = ~buf[i];
+ }
+ for (i = 0; i < num_longs; i++) {
+ buf[i]++;
+ if (buf[i] != 0)
+ return;
+ }
+ }
+ }
+}
+
+VALUE
+rb_big_unpack(unsigned long *buf, long num_longs)
+{
+ while (2 <= num_longs) {
+ if (buf[num_longs-1] == 0 && (long)buf[num_longs-2] >= 0)
+ num_longs--;
+ else if (buf[num_longs-1] == ~0UL && (long)buf[num_longs-2] < 0)
+ num_longs--;
+ else
+ break;
+ }
+ if (num_longs == 0)
+ return INT2FIX(0);
+ else if (num_longs == 1)
+ return LONG2NUM((long)buf[0]);
+ else {
+ VALUE big;
+ BDIGIT *ds;
+ long len = num_longs * (SIZEOF_LONG/SIZEOF_BDIGITS);
+ long i;
+ big = bignew(len, 1);
+ ds = BDIGITS(big);
+ for (i = 0; i < num_longs; i++) {
+ unsigned long d = buf[i];
+#if SIZEOF_LONG == SIZEOF_BDIGITS
+ *ds++ = d;
+#else
+ int j;
+ for (j = 0; j < SIZEOF_LONG/SIZEOF_BDIGITS; j++) {
+ *ds++ = BIGLO(d);
+ d = BIGDN(d);
+ }
+#endif
+ }
+ if ((long)buf[num_longs-1] < 0) {
+ get2comp(big);
+ RBIGNUM_SET_SIGN(big, 0);
+ }
+ return bignorm(big);
+ }
+}
+
#define QUAD_SIZE 8
#if SIZEOF_LONG_LONG == QUAD_SIZE && SIZEOF_BDIGITS*2 == SIZEOF_LONG_LONG
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 7872ca8007..ebbdd713b2 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -103,6 +103,8 @@ unsigned LONG_LONG rb_big2ull(VALUE);
#endif /* HAVE_LONG_LONG */
void rb_quad_pack(char*,VALUE);
VALUE rb_quad_unpack(const char*,int);
+void rb_big_pack(VALUE val, unsigned long *buf, long num_longs);
+VALUE rb_big_unpack(unsigned long *buf, long num_longs);
int rb_uv_to_utf8(char[6],unsigned long);
VALUE rb_dbl2big(double);
double rb_big2dbl(VALUE);
diff --git a/pack.c b/pack.c
index cfaec60354..552350c864 100644
--- a/pack.c
+++ b/pack.c
@@ -267,6 +267,7 @@ num2i32(VALUE x)
}
#define QUAD_SIZE 8
+/* #define FORCE_BIG_PACK */
static const char toofew[] = "too few arguments";
static void encodes(VALUE,const char*,long,int,int);
@@ -724,6 +725,7 @@ pack_pack(VALUE ary, VALUE fmt)
#endif
#if SIZEOF_INT != SIZEOF_LONG
+#if !defined(FORCE_BIG_PACK) || SIZEOF_LONG != QUAD_SIZE
case SIZEOF_LONG:
while (len-- > 0) {
long l;
@@ -735,15 +737,24 @@ pack_pack(VALUE ary, VALUE fmt)
}
break;
#endif
+#endif
-#if SIZEOF_LONG != QUAD_SIZE
+#if SIZEOF_LONG != QUAD_SIZE || defined(FORCE_BIG_PACK)
case QUAD_SIZE:
while (len-- > 0) {
- char tmp[QUAD_SIZE];
+ unsigned long tmp[QUAD_SIZE/SIZEOF_LONG];
from = NEXTFROM;
- rb_quad_pack(tmp, from);
- rb_str_buf_cat(res, (char*)&tmp, QUAD_SIZE);
+ rb_big_pack(from, tmp, QUAD_SIZE/SIZEOF_LONG);
+ if (BIGENDIAN_P()) {
+ int i;
+ for (i = 0; i < QUAD_SIZE/SIZEOF_LONG/2; i++) {
+ unsigned long t = tmp[i];
+ tmp[i] = tmp[QUAD_SIZE/SIZEOF_LONG-i-1];
+ tmp[QUAD_SIZE/SIZEOF_LONG-i-1] = t;
+ }
+ }
+ rb_str_buf_cat(res, (char*)tmp, QUAD_SIZE);
}
break;
#endif
@@ -1593,6 +1604,7 @@ pack_unpack(VALUE str, VALUE fmt)
#endif
#if SIZEOF_INT != SIZEOF_LONG
+#if !defined(FORCE_BIG_PACK) || SIZEOF_LONG != QUAD_SIZE
case SIZEOF_LONG:
if (signed_p) {
PACK_LENGTH_ADJUST_SIZE(sizeof(long));
@@ -1618,8 +1630,10 @@ pack_unpack(VALUE str, VALUE fmt)
}
break;
#endif
+#endif
#if defined(HAVE_LONG_LONG) && SIZEOF_LONG != SIZEOF_LONG_LONG
+#if !defined(FORCE_BIG_PACK) || SIZEOF_LONG_LONG != QUAD_SIZE
case SIZEOF_LONG_LONG:
if (signed_p) {
PACK_LENGTH_ADJUST_SIZE(sizeof(LONG_LONG));
@@ -1645,29 +1659,34 @@ pack_unpack(VALUE str, VALUE fmt)
}
break;
#endif
+#endif
-#if SIZEOF_LONG != QUAD_SIZE && (!defined(HAVE_LONG_LONG) || SIZEOF_LONG_LONG != QUAD_SIZE)
+#if (SIZEOF_LONG != QUAD_SIZE && (!defined(HAVE_LONG_LONG) || SIZEOF_LONG_LONG != QUAD_SIZE)) || defined(FORCE_BIG_PACK)
case QUAD_SIZE:
if (bigendian_p != BIGENDIAN_P())
rb_bug("unexpected endian for unpack");
- if (signed_p) {
- PACK_LENGTH_ADJUST_SIZE(QUAD_SIZE);
- while (len-- > 0) {
- char *tmp = (char*)s;
- s += QUAD_SIZE;
- UNPACK_PUSH(rb_quad_unpack(tmp, 1));
- }
- PACK_ITEM_ADJUST();
- }
- else {
- PACK_LENGTH_ADJUST_SIZE(QUAD_SIZE);
- while (len-- > 0) {
- char *tmp = (char*)s;
- s += QUAD_SIZE;
- UNPACK_PUSH(rb_quad_unpack(tmp, 0));
- }
- PACK_ITEM_ADJUST();
- }
+ PACK_LENGTH_ADJUST_SIZE(QUAD_SIZE);
+ while (len-- > 0) {
+ unsigned long tmp[QUAD_SIZE/SIZEOF_LONG+1];
+ memcpy(tmp, s, QUAD_SIZE);
+ if (BIGENDIAN_P()) {
+ int i;
+ for (i = 0; i < (QUAD_SIZE/SIZEOF_LONG)/2; i++) {
+ unsigned long t = tmp[i];
+ tmp[i] = tmp[(QUAD_SIZE/SIZEOF_LONG)-i-1];
+ tmp[(QUAD_SIZE/SIZEOF_LONG)-i-1] = t;
+ }
+ }
+ s += QUAD_SIZE;
+ if (signed_p) {
+ UNPACK_PUSH(rb_big_unpack(tmp, QUAD_SIZE/SIZEOF_LONG));
+ }
+ else {
+ tmp[QUAD_SIZE/SIZEOF_LONG] = 0;
+ UNPACK_PUSH(rb_big_unpack(tmp, QUAD_SIZE/SIZEOF_LONG+1));
+ }
+ }
+ PACK_ITEM_ADJUST();
break;
#endif