diff --git a/mrblib/hash.rb b/mrblib/hash.rb index 328890880..897523c14 100644 --- a/mrblib/hash.rb +++ b/mrblib/hash.rb @@ -10,32 +10,6 @@ class Hash include Enumerable - ## - # call-seq: - # hash.eql? object -> true or false - # - # Returns true if hash and other are - # both hashes with the same content compared by eql?. - # - def eql?(hash) - return true if self.equal?(hash) - unless Hash === hash - return false - end - return false if self.size != hash.size - - # Check for recursive calls to prevent stack overflow - # Use the same mechanism as inspect_recursive_p but for eql? method - if __eql_recursive_p? - return false - end - - self.each do |k,v| - return false unless hash.key?(k) - return false unless self[k].eql?(hash[k]) - end - return true - end ## # call-seq: diff --git a/src/hash.c b/src/hash.c index 3dfff35d3..b22bd2232 100644 --- a/src/hash.c +++ b/src/hash.c @@ -2217,6 +2217,49 @@ mrb_hash_equal(mrb_state *mrb, mrb_value hash) return mrb_true_value(); } +/* + * call-seq: + * hash.eql?(other) -> true or false + * + * Returns true if hash and other are both hashes with the same content + * compared by eql?. + */ +static mrb_value +mrb_hash_eql(mrb_state *mrb, mrb_value hash) +{ + mrb_value hash2 = mrb_get_arg1(mrb); + + if (mrb_obj_equal(mrb, hash, hash2)) return mrb_true_value(); + if (!mrb_hash_p(hash2)) { + return mrb_false_value(); + } + if (mrb_hash_size(mrb, hash) != mrb_hash_size(mrb, hash2)) { + return mrb_false_value(); + } + + struct RHash *h1 = mrb_hash_ptr(hash); + struct RHash *h2 = mrb_hash_ptr(hash2); + + H_EACH(h1, entry) { + mrb_value val2; + mrb_bool found; + + H_CHECK_MODIFIED(mrb, h1) { + found = h_get(mrb, h2, entry->key, &val2); + } + if (!found) { + return mrb_false_value(); + } + H_CHECK_MODIFIED(mrb, h1) { + if (!mrb_eql(mrb, entry->val, val2)) { + return mrb_false_value(); + } + } + } + + return mrb_true_value(); +} + void mrb_init_hash(mrb_state *mrb) { @@ -2234,6 +2277,7 @@ mrb_init_hash(mrb_state *mrb) mrb_define_method_id(mrb, h, MRB_SYM(default_proc), mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */ mrb_define_method_id(mrb, h, MRB_SYM_E(default_proc), mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */ mrb_define_method_id(mrb, h, MRB_SYM(__delete), mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */ + mrb_define_method_id(mrb, h, MRB_SYM_Q(eql), mrb_hash_eql, MRB_ARGS_REQ(1)); /* Hash#eql? */ mrb_define_method_id(mrb, h, MRB_SYM_Q(empty), mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(has_key), mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(has_value), mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */