From 55c3f7b6440b3c49e39941876b9f07e913501675 Mon Sep 17 00:00:00 2001 From: shyouhei Date: Wed, 15 Aug 2007 21:37:41 +0000 Subject: * bignum.c (rb_big_lshift, rb_big_rshift): separated functions to get rid of infinite recursion. fixed calculation in edge cases. [ruby-dev:31244] * numeric.c (rb_fix_lshift, rb_fix_rshift): ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8_5@13022 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- bignum.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 7 deletions(-) (limited to 'bignum.c') diff --git a/bignum.c b/bignum.c index 489ae77b2e..17c05c0ceb 100644 --- a/bignum.c +++ b/bignum.c @@ -1509,6 +1509,20 @@ rb_big_remainder(x, y) return bignorm(z); } +static VALUE big_lshift _((VALUE, unsigned int)); +static VALUE big_rshift _((VALUE, unsigned int)); + +static VALUE big_shift(x, n) + VALUE x; + int n; +{ + if (n < 0) + return big_lshift(x, (unsigned int)n); + else if (n > 0) + return big_rshift(x, (unsigned int)n); + return x; +} + /* * call-seq: * big.divmod(numeric) => array @@ -1812,7 +1826,15 @@ rb_big_xor(xx, yy) return bignorm(z); } -static VALUE rb_big_rshift _((VALUE,VALUE)); +static VALUE +check_shiftdown(VALUE y, VALUE x) +{ + if (!RBIGNUM(x)->len) return INT2FIX(0); + if (RBIGNUM(y)->len > SIZEOF_LONG / SIZEOF_BDIGITS) { + return RBIGNUM(x)->sign ? INT2FIX(0) : INT2FIX(-1); + } + return Qnil; +} /* * call-seq: @@ -1824,16 +1846,46 @@ static VALUE rb_big_rshift _((VALUE,VALUE)); VALUE rb_big_lshift(x, y) VALUE x, y; +{ + int shift, neg = 0; + + for (;;) { + if (FIXNUM_P(y)) { + shift = FIX2INT(y); + if (shift < 0) { + neg = 1; + shift = -shift; + } + break; + } + else if (TYPE(y) == T_BIGNUM) { + if (!RBIGNUM(y)->sign) { + VALUE t = check_shiftdown(y, x); + if (!NIL_P(t)) return t; + neg = 1; + } + shift = big2ulong(y, "long", Qtrue); + break; + } + y = rb_to_int(y); + } + + if (neg) return big_rshift(x, shift); + return big_lshift(x, shift); +} + +static VALUE +big_lshift(x, shift) + VALUE x; + unsigned int shift; { BDIGIT *xds, *zds; - int shift = NUM2INT(y); int s1 = shift/BITSPERDIG; int s2 = shift%BITSPERDIG; VALUE z; BDIGIT_DBL num = 0; long len, i; - if (shift < 0) return rb_big_rshift(x, INT2FIX(-shift)); len = RBIGNUM(x)->len; z = bignew(len+s1+1, RBIGNUM(x)->sign); zds = BDIGITS(z); @@ -1857,12 +1909,46 @@ rb_big_lshift(x, y) * Shifts big right _numeric_ positions (left if _numeric_ is negative). */ -static VALUE +VALUE rb_big_rshift(x, y) VALUE x, y; +{ + int shift; + int neg = 0; + + for (;;) { + if (FIXNUM_P(y)) { + shift = FIX2INT(y); + if (shift < 0) { + neg = 1; + shift = -shift; + } + break; + } + else if (TYPE(y) == T_BIGNUM) { + if (RBIGNUM(y)->sign) { + VALUE t = check_shiftdown(y, x); + if (!NIL_P(t)) return t; + } + else { + neg = 1; + } + shift = big2ulong(y, "long", Qtrue); + break; + } + y = rb_to_int(y); + } + + if (neg) return big_lshift(x, shift); + return big_rshift(x, shift); +} + +static VALUE +big_rshift(x, shift) + VALUE x; + unsigned int shift; { BDIGIT *xds, *zds; - int shift = NUM2INT(y); long s1 = shift/BITSPERDIG; long s2 = shift%BITSPERDIG; VALUE z; @@ -1870,8 +1956,6 @@ rb_big_rshift(x, y) long i, j; volatile VALUE save_x; - if (shift < 0) return rb_big_lshift(x, INT2FIX(-shift)); - if (s1 > RBIGNUM(x)->len) { if (RBIGNUM(x)->sign) return INT2FIX(0); -- cgit v1.2.3