1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* array.c (rb_ary_equal, rb_ary_eql, rb_ary_hash, rb_ary_cmp):

Make Array#eql?, #hash, #== and #<=> use rb_exec_recursive() and
  handle recursive data properly.

* hash.c (hash_equal, rb_hash_hash): Make Hash#eql?, #hash and #==
  use rb_exec_recursive() and handle recursive data properly.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@16081 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
knu 2008-04-19 11:11:25 +00:00
parent 932ac5d35d
commit 80627a281e
4 changed files with 189 additions and 37 deletions

View file

@ -1,3 +1,12 @@
Sat Apr 19 20:05:39 2008 Akinori MUSHA <knu@iDaemons.org>
* array.c (rb_ary_equal, rb_ary_eql, rb_ary_hash, rb_ary_cmp):
Make Array#eql?, #hash, #== and #<=> use rb_exec_recursive() and
handle recursive data properly.
* hash.c (hash_equal, rb_hash_hash): Make Hash#eql?, #hash and #==
use rb_exec_recursive() and handle recursive data properly.
Sat Apr 19 19:26:09 2008 Akinori MUSHA <knu@iDaemons.org> Sat Apr 19 19:26:09 2008 Akinori MUSHA <knu@iDaemons.org>
* intern.h, eval.c (rb_exec_recursive): New internal function to * intern.h, eval.c (rb_exec_recursive): New internal function to

13
NEWS
View file

@ -27,6 +27,13 @@ with all sufficient information, see the ChangeLog file.
Takes an optional argument that determines the level of recursion Takes an optional argument that determines the level of recursion
to flatten. to flatten.
* Array#eql?
* Array#hash
* Array#==
* Array#<=>
Handle recursive data properly.
* Array#index * Array#index
* Array#rindex * Array#rindex
@ -122,6 +129,12 @@ with all sufficient information, see the ChangeLog file.
New alias to #inject. New alias to #inject.
* Hash#eql?
* Hash#hash
* Hash#==
Handle recursive data properly.
* Hash#delete_if * Hash#delete_if
* Hash#each * Hash#each
* Hash#each_key * Hash#each_key

111
array.c
View file

@ -2595,6 +2595,22 @@ rb_ary_rassoc(ary, value)
return Qnil; return Qnil;
} }
static VALUE recursive_equal _((VALUE, VALUE, int));
static VALUE
recursive_equal(ary1, ary2, recur)
VALUE ary1, ary2;
int recur;
{
long i;
if (recur) return Qfalse;
for (i=0; i<RARRAY(ary1)->len; i++) {
if (!rb_equal(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
return Qfalse;
}
return Qtrue;
}
/* /*
* call-seq: * call-seq:
* array == other_array -> bool * array == other_array -> bool
@ -2613,8 +2629,6 @@ static VALUE
rb_ary_equal(ary1, ary2) rb_ary_equal(ary1, ary2)
VALUE ary1, ary2; VALUE ary1, ary2;
{ {
long i;
if (ary1 == ary2) return Qtrue; if (ary1 == ary2) return Qtrue;
if (TYPE(ary2) != T_ARRAY) { if (TYPE(ary2) != T_ARRAY) {
if (!rb_respond_to(ary2, rb_intern("to_ary"))) { if (!rb_respond_to(ary2, rb_intern("to_ary"))) {
@ -2623,8 +2637,20 @@ rb_ary_equal(ary1, ary2)
return rb_equal(ary2, ary1); return rb_equal(ary2, ary1);
} }
if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse; if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
return rb_exec_recursive(recursive_equal, ary1, ary2);
}
static VALUE recursive_eql _((VALUE, VALUE, int));
static VALUE
recursive_eql(ary1, ary2, recur)
VALUE ary1, ary2;
int recur;
{
long i;
if (recur) return Qfalse;
for (i=0; i<RARRAY(ary1)->len; i++) { for (i=0; i<RARRAY(ary1)->len; i++) {
if (!rb_equal(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i))) if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
return Qfalse; return Qfalse;
} }
return Qtrue; return Qtrue;
@ -2642,16 +2668,33 @@ static VALUE
rb_ary_eql(ary1, ary2) rb_ary_eql(ary1, ary2)
VALUE ary1, ary2; VALUE ary1, ary2;
{ {
long i;
if (ary1 == ary2) return Qtrue; if (ary1 == ary2) return Qtrue;
if (TYPE(ary2) != T_ARRAY) return Qfalse; if (TYPE(ary2) != T_ARRAY) return Qfalse;
if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse; if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
for (i=0; i<RARRAY(ary1)->len; i++) { return rb_exec_recursive(recursive_eql, ary1, ary2);
if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i))) }
return Qfalse;
static VALUE recursive_hash _((VALUE, VALUE, int));
static VALUE
recursive_hash(ary, dummy, recur)
VALUE ary;
VALUE dummy;
int recur;
{
long i, h;
VALUE n;
if (recur) {
return LONG2FIX(0);
} }
return Qtrue;
h = RARRAY(ary)->len;
for (i=0; i<RARRAY(ary)->len; i++) {
h = (h << 1) | (h<0 ? 1 : 0);
n = rb_hash(RARRAY(ary)->ptr[i]);
h ^= NUM2LONG(n);
}
return LONG2FIX(h);
} }
/* /*
@ -2666,16 +2709,7 @@ static VALUE
rb_ary_hash(ary) rb_ary_hash(ary)
VALUE ary; VALUE ary;
{ {
long i, h; return rb_exec_recursive(recursive_hash, ary, 0);
VALUE n;
h = RARRAY(ary)->len;
for (i=0; i<RARRAY(ary)->len; i++) {
h = (h << 1) | (h<0 ? 1 : 0);
n = rb_hash(RARRAY(ary)->ptr[i]);
h ^= NUM2LONG(n);
}
return LONG2FIX(h);
} }
/* /*
@ -2707,6 +2741,29 @@ rb_ary_includes(ary, item)
} }
static VALUE recursive_cmp _((VALUE, VALUE, int));
static VALUE
recursive_cmp(ary1, ary2, recur)
VALUE ary1;
VALUE ary2;
int recur;
{
long i, len;
if (recur) return Qnil;
len = RARRAY(ary1)->len;
if (len > RARRAY(ary2)->len) {
len = RARRAY(ary2)->len;
}
for (i=0; i<len; i++) {
VALUE v = rb_funcall(rb_ary_elt(ary1, i), id_cmp, 1, rb_ary_elt(ary2, i));
if (v != INT2FIX(0)) {
return v;
}
}
return Qundef;
}
/* /*
* call-seq: * call-seq:
* array <=> other_array -> -1, 0, +1 * array <=> other_array -> -1, 0, +1
@ -2731,19 +2788,13 @@ VALUE
rb_ary_cmp(ary1, ary2) rb_ary_cmp(ary1, ary2)
VALUE ary1, ary2; VALUE ary1, ary2;
{ {
long i, len; long len;
VALUE v;
ary2 = to_ary(ary2); ary2 = to_ary(ary2);
len = RARRAY(ary1)->len; if (ary1 == ary2) return INT2FIX(0);
if (len > RARRAY(ary2)->len) { v = rb_exec_recursive(recursive_cmp, ary1, ary2);
len = RARRAY(ary2)->len; if (v != Qundef) return v;
}
for (i=0; i<len; i++) {
VALUE v = rb_funcall(rb_ary_elt(ary1, i), id_cmp, 1, rb_ary_elt(ary2, i));
if (v != INT2FIX(0)) {
return v;
}
}
len = RARRAY(ary1)->len - RARRAY(ary2)->len; len = RARRAY(ary1)->len - RARRAY(ary2)->len;
if (len == 0) return INT2FIX(0); if (len == 0) return INT2FIX(0);
if (len > 0) return INT2FIX(1); if (len > 0) return INT2FIX(1);

93
hash.c
View file

@ -1501,11 +1501,13 @@ rb_hash_has_value(hash, val)
struct equal_data { struct equal_data {
int result; int result;
st_table *tbl; st_table *tbl;
int eql;
}; };
static int static int
equal_i(key, val1, data) eql_i(key, val1, data)
VALUE key, val1; VALUE key;
VALUE val1;
struct equal_data *data; struct equal_data *data;
{ {
VALUE val2; VALUE val2;
@ -1515,13 +1517,30 @@ equal_i(key, val1, data)
data->result = Qfalse; data->result = Qfalse;
return ST_STOP; return ST_STOP;
} }
if (!rb_equal(val1, val2)) { if (!(data->eql ? rb_eql(val1, val2) : rb_equal(val1, val2))) {
data->result = Qfalse; data->result = Qfalse;
return ST_STOP; return ST_STOP;
} }
return ST_CONTINUE; return ST_CONTINUE;
} }
static VALUE recursive_eql _((VALUE, VALUE, int));
static VALUE
recursive_eql(hash, dt, recur)
VALUE hash;
VALUE dt;
int recur;
{
struct equal_data *data;
if (recur) return Qfalse;
data = (struct equal_data*)dt;
data->result = Qtrue;
rb_hash_foreach(hash, eql_i, (st_data_t)data);
return data->result;
}
static VALUE static VALUE
hash_equal(hash1, hash2, eql) hash_equal(hash1, hash2, eql)
VALUE hash1, hash2; VALUE hash1, hash2;
@ -1545,10 +1564,8 @@ hash_equal(hash1, hash2, eql)
} }
data.tbl = RHASH(hash2)->tbl; data.tbl = RHASH(hash2)->tbl;
data.result = Qtrue; data.eql = eql;
rb_hash_foreach(hash1, equal_i, (st_data_t)&data); return rb_exec_recursive(recursive_eql, hash1, (VALUE)&data);
return data.result;
} }
/* /*
@ -1577,6 +1594,66 @@ rb_hash_equal(hash1, hash2)
return hash_equal(hash1, hash2, Qfalse); return hash_equal(hash1, hash2, Qfalse);
} }
static int
hash_i(key, val, hval)
VALUE key;
VALUE val;
int *hval;
{
if (key == Qundef) return ST_CONTINUE;
*hval ^= rb_hash(key);
*hval *= 137;
*hval ^= rb_hash(val);
return ST_CONTINUE;
}
static VALUE
recursive_hash(hash, dummy, recur)
VALUE hash;
VALUE dummy;
int recur;
{
int hval;
if (recur) {
return LONG2FIX(0);
}
hval = RHASH(hash)->tbl->num_entries;
rb_hash_foreach(hash, hash_i, (st_data_t)&hval);
return INT2FIX(hval);
}
/*
* call-seq:
* array.hash -> fixnum
*
* Compute a hash-code for this array. Two arrays with the same content
* will have the same hash code (and will compare using <code>eql?</code>).
*/
static VALUE
rb_hash_hash(hash)
VALUE hash;
{
return rb_exec_recursive(recursive_hash, hash, 0);
}
/*
* call-seq:
* hash.eql?(other) -> true or false
*
* Returns <code>true</code> if <i>hash</i> and <i>other</i> are
* both hashes with the same content.
*/
static VALUE
rb_hash_eql(hash1, hash2)
VALUE hash1, hash2;
{
return hash_equal(hash1, hash2, Qtrue);
}
static int static int
rb_hash_invert_i(key, value, hash) rb_hash_invert_i(key, value, hash)
VALUE key, value; VALUE key, value;
@ -2545,6 +2622,8 @@ Init_Hash()
rb_define_method(rb_cHash,"==", rb_hash_equal, 1); rb_define_method(rb_cHash,"==", rb_hash_equal, 1);
rb_define_method(rb_cHash,"[]", rb_hash_aref, 1); rb_define_method(rb_cHash,"[]", rb_hash_aref, 1);
rb_define_method(rb_cHash,"hash", rb_hash_hash, 0);
rb_define_method(rb_cHash,"eql?", rb_hash_eql, 1);
rb_define_method(rb_cHash,"fetch", rb_hash_fetch, -1); rb_define_method(rb_cHash,"fetch", rb_hash_fetch, -1);
rb_define_method(rb_cHash,"[]=", rb_hash_aset, 2); rb_define_method(rb_cHash,"[]=", rb_hash_aset, 2);
rb_define_method(rb_cHash,"store", rb_hash_aset, 2); rb_define_method(rb_cHash,"store", rb_hash_aset, 2);