From 6ea1aee76e2642aa0d532a00e0b971dede02d818 Mon Sep 17 00:00:00 2001 From: akr Date: Thu, 20 Jun 2013 13:05:27 +0000 Subject: [PATCH] * 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41494 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 6 +++ bignum.c | 87 ++++++++++++++++++++++------------ test/-ext-/bignum/test_pack.rb | 5 ++ 3 files changed, 67 insertions(+), 31 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 + + * 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 * 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) - sign = -1; - else - sign = 0; - } - else if ((flags & INTEGER_PACK_NEGATIVE) || - (num_bdigits != 0 && - (bdigits[num_bdigits-1] >> (BITSPERDIG - nlp_bits - 1)))) { - if (nlp_bits) + 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 = 1; + } + } + 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