summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hash.c30
-rw-r--r--internal/hash.h1
-rw-r--r--test/ruby/test_hash.rb10
3 files changed, 37 insertions, 4 deletions
diff --git a/hash.c b/hash.c
index 9b6d9b9aea..80ed64527d 100644
--- a/hash.c
+++ b/hash.c
@@ -2906,14 +2906,38 @@ rb_hash_replace(VALUE hash, VALUE hash2)
else {
RHASH_ST_CLEAR(hash);
}
- hash_copy(hash, hash2);
+
+ if (RHASH_AR_TABLE_P(hash2)) {
+ if (RHASH_AR_TABLE_P(hash)) {
+ ar_copy(hash, hash2);
+ }
+ else {
+ st_table *tab = RHASH_ST_TABLE(hash);
+ rb_st_init_existing_table_with_size(tab, &objhash, RHASH_AR_TABLE_SIZE(hash2));
+
+ int bound = RHASH_AR_TABLE_BOUND(hash2);
+ for (int i = 0; i < bound; i++) {
+ if (ar_cleared_entry(hash2, i)) continue;
+
+ ar_table_pair *pair = RHASH_AR_TABLE_REF(hash2, i);
+ st_add_direct(tab, pair->key, pair->val);
+ RB_OBJ_WRITTEN(hash, Qundef, pair->key);
+ RB_OBJ_WRITTEN(hash, Qundef, pair->val);
+ }
+ }
+ }
+ else {
+ HASH_ASSERT(sizeof(st_table) <= sizeof(ar_table));
+
+ RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(hash2)));
+ rb_gc_writebarrier_remember(hash);
+ }
+
if (RHASH_EMPTY_P(hash2) && RHASH_ST_TABLE_P(hash2)) {
/* ident hash */
hash_st_table_init(hash, RHASH_TYPE(hash2), 0);
}
- rb_gc_writebarrier_remember(hash);
-
return hash;
}
diff --git a/internal/hash.h b/internal/hash.h
index 248a53429d..3874abeaa5 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -179,7 +179,6 @@ static inline void
RHASH_ST_CLEAR(VALUE h)
{
memset(RHASH_ST_TABLE(h), 0, sizeof(st_table));
- FL_UNSET_RAW(h, RHASH_ST_TABLE_FLAG);
}
static inline unsigned
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index 8b6065146c..d9d1ca7dde 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -850,6 +850,16 @@ class TestHash < Test::Unit::TestCase
assert(true)
end
+ def test_replace_st_with_ar
+ # ST hash
+ h1 = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9 }
+ # AR hash
+ h2 = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7 }
+ # Replace ST hash with AR hash
+ h1.replace(h2)
+ assert_equal(h2, h1)
+ end
+
def test_shift
h = @h.dup