From 09ce106ab3341030cc1893ca308532d196658508 Mon Sep 17 00:00:00 2001 From: nobu Date: Thu, 19 Jul 2007 05:38:48 +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/trunk@12814 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- bignum.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 9 deletions(-) (limited to 'bignum.c') diff --git a/bignum.c b/bignum.c index fc4b79883a..a0402d7d2d 100644 --- a/bignum.c +++ b/bignum.c @@ -1529,7 +1529,17 @@ bdigbitsize(BDIGIT x) return size; } -static VALUE rb_big_rshift(VALUE,VALUE); +static VALUE big_lshift(VALUE, unsigned int); +static VALUE big_rshift(VALUE, unsigned int); + +static VALUE big_shift(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: @@ -1558,7 +1568,7 @@ rb_big_quo(VALUE x, VALUE y) ex = (RBIGNUM(bigtrunc(x))->len - 1) * BITSPERDIG; ex += bdigbitsize(BDIGITS(x)[RBIGNUM(x)->len - 1]); ex -= 2 * DBL_BIGDIG * BITSPERDIG; - if (ex) x = rb_big_rshift(x, INT2FIX(ex)); + if (ex) x = big_shift(x, ex); switch (TYPE(y)) { case T_FIXNUM: @@ -1567,7 +1577,7 @@ rb_big_quo(VALUE x, VALUE y) ey = (RBIGNUM(bigtrunc(y))->len - 1) * BITSPERDIG; ey += bdigbitsize(BDIGITS(y)[RBIGNUM(y)->len - 1]); ey -= DBL_BIGDIG * BITSPERDIG; - if (ey) y = rb_big_rshift(y, INT2FIX(ey)); + if (ey) y = big_shift(y, ey); bignum: bigdivrem(x, y, &z, 0); return rb_float_new(ldexp(big2dbl(z), ex - ey)); @@ -1872,6 +1882,16 @@ rb_big_xor(VALUE xx, VALUE yy) return bignorm(z); } +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: * big << numeric => integer @@ -1881,16 +1901,44 @@ rb_big_xor(VALUE xx, VALUE yy) VALUE rb_big_lshift(VALUE x, VALUE 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(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); @@ -1914,11 +1962,43 @@ rb_big_lshift(VALUE x, VALUE y) * Shifts big right _numeric_ positions (left if _numeric_ is negative). */ -static VALUE +VALUE rb_big_rshift(VALUE x, VALUE 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(VALUE x, unsigned int shift) { BDIGIT *xds, *zds; - int shift = NUM2INT(y); long s1 = shift/BITSPERDIG; long s2 = shift%BITSPERDIG; VALUE z; @@ -1926,8 +2006,6 @@ rb_big_rshift(VALUE x, VALUE 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