hash.c: implement Hash#eql? in C for better performance

Move Hash#eql? implementation from Ruby to C to improve performance and
consistency with other core methods. The C implementation uses mrb_eql
for value comparison, providing proper eql? semantics.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-07 13:01:34 +09:00
parent 633317a809
commit 96355f33b2
2 changed files with 44 additions and 26 deletions
-26
View File
@@ -10,32 +10,6 @@ class Hash
include Enumerable
##
# call-seq:
# hash.eql? object -> true or false
#
# Returns <code>true</code> if <i>hash</i> and <i>other</i> 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:
+44
View File
@@ -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 */