* 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
This commit is contained in:
akr 2013-06-20 13:05:27 +00:00
Родитель bf017de1f3
Коммит 6ea1aee76e
3 изменённых файлов: 67 добавлений и 31 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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)

Просмотреть файл

@ -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