summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-08-15 21:37:41 +0000
committershyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-08-15 21:37:41 +0000
commit55c3f7b6440b3c49e39941876b9f07e913501675 (patch)
tree6604992e830d019dd81a148c194523c06fa12a5f
parent9f53bcf67c9b880c1f0aed5c6999247556717626 (diff)
* 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
-rw-r--r--ChangeLog8
-rw-r--r--bignum.c98
-rw-r--r--intern.h1
-rw-r--r--numeric.c43
-rw-r--r--version.h2
5 files changed, 132 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index d4bcc30802..760be6e117 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Thu Aug 16 06:32:25 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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.
+
Thu Aug 16 06:26:58 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
* bignum.c (rb_big_pow): refine overflow check. [ruby-dev:31242]
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:
@@ -1825,15 +1847,45 @@ 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);
diff --git a/intern.h b/intern.h
index 97577a7cfd..072997503a 100644
--- a/intern.h
+++ b/intern.h
@@ -113,6 +113,7 @@ VALUE rb_mod_ancestors _((VALUE));
VALUE rb_class_instance_methods _((int, VALUE*, VALUE));
VALUE rb_class_public_instance_methods _((int, VALUE*, VALUE));
VALUE rb_class_protected_instance_methods _((int, VALUE*, VALUE));
+VALUE rb_big_rshift(VALUE, VALUE);
VALUE rb_class_private_instance_methods _((int, VALUE*, VALUE));
VALUE rb_obj_singleton_methods _((int, VALUE*, VALUE));
void rb_define_method_id _((VALUE, ID, VALUE (*)(ANYARGS), int));
diff --git a/numeric.c b/numeric.c
index f4132fd1b7..ded844a4cb 100644
--- a/numeric.c
+++ b/numeric.c
@@ -2462,7 +2462,8 @@ fix_xor(x, y)
return LONG2NUM(val);
}
-static VALUE fix_rshift _((VALUE, VALUE));
+static VALUE fix_lshift _((long, unsigned long));
+static VALUE fix_rshift _((long, unsigned long));
/*
* call-seq:
@@ -2472,18 +2473,28 @@ static VALUE fix_rshift _((VALUE, VALUE));
*/
static VALUE
-fix_lshift(x, y)
+rb_fix_lshift(x, y)
VALUE x, y;
{
long val, width;
val = NUM2LONG(x);
- width = NUM2LONG(y);
+ if (!FIXNUM_P(y))
+ return rb_big_lshift(rb_int2big(val), y);
+ width = FIX2LONG(y);
if (width < 0)
- return fix_rshift(x, LONG2FIX(-width));
+ return fix_rshift(val, (unsigned long)-width);
+ return fix_lshift(val, width);
+}
+
+static VALUE
+fix_lshift(val, width)
+ long val;
+ unsigned long width;
+{
if (width > (sizeof(VALUE)*CHAR_BIT-1)
|| ((unsigned long)val)>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) {
- return rb_big_lshift(rb_int2big(val), y);
+ return rb_big_lshift(rb_int2big(val), ULONG2NUM(width));
}
val = val << width;
return LONG2NUM(val);
@@ -2497,16 +2508,24 @@ fix_lshift(x, y)
*/
static VALUE
-fix_rshift(x, y)
+rb_fix_rshift(x, y)
VALUE x, y;
{
long i, val;
- i = NUM2LONG(y);
- if (i < 0)
- return fix_lshift(x, LONG2FIX(-i));
- if (i == 0) return x;
val = FIX2LONG(x);
+ if (!FIXNUM_P(y))
+ return rb_big_rshift(rb_int2big(val), y);
+ i = FIX2LONG(y);
+ if (i == 0) return x;
+ if (i < 0)
+ return fix_lshift(val, (unsigned long)-i);
+ return fix_rshift(val, i);
+}
+
+static VALUE
+fix_rshift(long val, unsigned long i)
+{
if (i >= sizeof(long)*CHAR_BIT-1) {
if (val < 0) return INT2FIX(-1);
return INT2FIX(0);
@@ -2907,8 +2926,8 @@ Init_Numeric()
rb_define_method(rb_cFixnum, "^", fix_xor, 1);
rb_define_method(rb_cFixnum, "[]", fix_aref, 1);
- rb_define_method(rb_cFixnum, "<<", fix_lshift, 1);
- rb_define_method(rb_cFixnum, ">>", fix_rshift, 1);
+ rb_define_method(rb_cFixnum, "<<", rb_fix_lshift, 1);
+ rb_define_method(rb_cFixnum, ">>", rb_fix_rshift, 1);
rb_define_method(rb_cFixnum, "to_f", fix_to_f, 0);
rb_define_method(rb_cFixnum, "size", fix_size, 0);
diff --git a/version.h b/version.h
index 0a6ccf6b7c..462fb12c5a 100644
--- a/version.h
+++ b/version.h
@@ -2,7 +2,7 @@
#define RUBY_RELEASE_DATE "2007-08-16"
#define RUBY_VERSION_CODE 185
#define RUBY_RELEASE_CODE 20070816
-#define RUBY_PATCHLEVEL 79
+#define RUBY_PATCHLEVEL 80
#define RUBY_VERSION_MAJOR 1
#define RUBY_VERSION_MINOR 8