summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--bignum.c85
-rw-r--r--test/-ext-/bignum/test_pack.rb5
3 files changed, 66 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index 018c4b511c..9fb3c63ef3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Thu Jun 20 22:02:46 2013 Tanaka Akira <akr@fsij.org>
+
+ * bignum.c (bary_unpack_internal): Return -2 when negative overflow.
+ (bary_unpack): Set the overflowed bit if an extra BDIGIT exists.
+ (rb_integer_unpack): Set the overflowed bit.
+
Thu Jun 20 21:17:19 2013 Koichi Sasada <ko1@atdot.net>
* gc.c (rgengc_rememberset_mark): record
diff --git a/bignum.c b/bignum.c
index 0d50618db2..cba044250b 100644
--- a/bignum.c
+++ b/bignum.c
@@ -1259,26 +1259,26 @@ integer_unpack_push_bits(int data, int numbits, BDIGIT_DBL *ddp, int *numbits_in
static int
bary_unpack_internal(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwords, size_t wordsize, size_t nails, int flags, int nlp_bits)
{
- int sign = (flags & INTEGER_PACK_NEGATIVE) ? -1 : 1;
+ int sign;
- const unsigned char *buf = words;
+ if (num_bdigits != 0) {
+ const unsigned char *buf = words;
- BDIGIT *dp;
- BDIGIT *de;
+ BDIGIT *dp;
+ BDIGIT *de;
- int word_num_partialbits;
- size_t word_num_fullbytes;
+ int word_num_partialbits;
+ size_t word_num_fullbytes;
- ssize_t word_step;
- size_t byte_start;
- int byte_step;
+ ssize_t word_step;
+ size_t byte_start;
+ int byte_step;
- size_t word_start, word_last;
- const unsigned char *wordp, *last_wordp;
- BDIGIT_DBL dd;
- int numbits_in_dd;
+ size_t word_start, word_last;
+ const unsigned char *wordp, *last_wordp;
+ BDIGIT_DBL dd;
+ int numbits_in_dd;
- if (num_bdigits) {
dp = bdigits;
de = dp + num_bdigits;
@@ -1322,20 +1322,34 @@ bary_unpack_internal(BDIGIT *bdigits, size_t num_bdigits, const void *words, siz
#undef PUSH_BITS
}
- if (flags & INTEGER_PACK_2COMP) {
- if (num_bdigits == 0) {
- if (flags & INTEGER_PACK_NEGATIVE)
+ if (!(flags & INTEGER_PACK_2COMP)) {
+ sign = (flags & INTEGER_PACK_NEGATIVE) ? -1 : 1;
+ }
+ else {
+ if (nlp_bits) {
+ if ((flags & INTEGER_PACK_NEGATIVE) ||
+ (bdigits[num_bdigits-1] >> (BITSPERDIG - nlp_bits - 1))) {
+ bdigits[num_bdigits-1] |= (~(BDIGIT)0) << (BITSPERDIG - nlp_bits);
sign = -1;
- else
- sign = 0;
+ }
+ else {
+ sign = 1;
+ }
}
- else if ((flags & INTEGER_PACK_NEGATIVE) ||
- (num_bdigits != 0 &&
- (bdigits[num_bdigits-1] >> (BITSPERDIG - nlp_bits - 1)))) {
- if (nlp_bits)
- bdigits[num_bdigits-1] |= (~(BDIGIT)0) << (BITSPERDIG - nlp_bits);
+ else {
+ if (flags & INTEGER_PACK_NEGATIVE) {
+ sign = bary_zero_p(bdigits, num_bdigits) ? -2 : -1;
+ }
+ else {
+ if (num_bdigits != 0 &&
+ (bdigits[num_bdigits-1] >> (BITSPERDIG - 1)))
+ sign = -1;
+ else
+ sign = 1;
+ }
+ }
+ if (sign == -1 && num_bdigits != 0) {
bary_2comp(bdigits, num_bdigits);
- sign = -1;
}
}
@@ -1347,6 +1361,7 @@ bary_unpack(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwo
{
size_t num_bdigits0;
int nlp_bits;
+ int sign;
validate_integer_pack_format(numwords, wordsize, nails, flags,
INTEGER_PACK_MSWORD_FIRST|
@@ -1362,7 +1377,14 @@ bary_unpack(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwo
assert(num_bdigits0 <= num_bdigits);
- bary_unpack_internal(bdigits, num_bdigits, words, numwords, wordsize, nails, flags, nlp_bits);
+ sign = bary_unpack_internal(bdigits, num_bdigits0, words, numwords, wordsize, nails, flags, nlp_bits);
+
+ if (num_bdigits0 < num_bdigits) {
+ MEMZERO(bdigits + num_bdigits0, BDIGIT, num_bdigits - num_bdigits0);
+ if (sign == -2) {
+ bdigits[num_bdigits0] = 1;
+ }
+ }
}
/*
@@ -1429,16 +1451,19 @@ rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t na
num_bdigits = integer_unpack_num_bdigits(numwords, wordsize, nails, &nlp_bits);
- if (LONG_MAX < num_bdigits)
+ if (LONG_MAX-1 < num_bdigits)
rb_raise(rb_eArgError, "too big to unpack as an integer");
val = bignew((long)num_bdigits, 0);
ds = BDIGITS(val);
sign = bary_unpack_internal(ds, num_bdigits, words, numwords, wordsize, nails, flags, nlp_bits);
- if ((flags & INTEGER_PACK_2COMP) && num_bdigits == 0 && sign < 0) {
- rb_big_resize(val, 1);
- ds[0] = 1;
+ if (sign == -2) {
+ rb_big_resize(val, (long)num_bdigits+1);
+ BDIGITS(val)[num_bdigits] = 1;
}
+ if ((flags & INTEGER_PACK_FORCE_BIGNUM) && sign != 0 &&
+ bary_zero_p(BDIGITS(val), RBIGNUM_LEN(val)))
+ sign = 0;
RBIGNUM_SET_SIGN(val, 0 <= sign);
if (flags & INTEGER_PACK_FORCE_BIGNUM)
diff --git a/test/-ext-/bignum/test_pack.rb b/test/-ext-/bignum/test_pack.rb
index 093c9bd212..c2c19a32a2 100644
--- a/test/-ext-/bignum/test_pack.rb
+++ b/test/-ext-/bignum/test_pack.rb
@@ -194,5 +194,10 @@ class TestBignum < Test::Unit::TestCase
assert_equal( -1, Integer.test_unpack("\xFF", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
end
+ def test_unpack2comp_negative_zero
+ 0.upto(100) {|n|
+ assert_equal(-(256**n), Integer.test_unpack("\x00"*n, n, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ }
+ end
end
end