aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--benchmark/hash_dup.yml8
-rw-r--r--hash.c76
2 files changed, 29 insertions, 55 deletions
diff --git a/benchmark/hash_dup.yml b/benchmark/hash_dup.yml
new file mode 100644
index 0000000000..65f521ec94
--- /dev/null
+++ b/benchmark/hash_dup.yml
@@ -0,0 +1,8 @@
+prelude: |
+ small_hash = { a: 1 }
+ larger_hash = 20.times.map { |i| [('a'.ord + i).chr.to_sym, i] }.to_h
+
+benchmark:
+ dup_small: small_hash.dup
+ dup_larger: larger_hash.dup
+loop_count: 10000
diff --git a/hash.c b/hash.c
index b73ade7c01..705ec31252 100644
--- a/hash.c
+++ b/hash.c
@@ -2808,49 +2808,6 @@ rb_hash_aset(VALUE hash, VALUE key, VALUE val)
return val;
}
-static int
-replace_i(VALUE key, VALUE val, VALUE hash)
-{
- rb_hash_aset(hash, key, val);
-
- return ST_CONTINUE;
-}
-
-/* :nodoc: */
-static VALUE
-rb_hash_initialize_copy(VALUE hash, VALUE hash2)
-{
- rb_hash_modify_check(hash);
- hash2 = to_hash(hash2);
-
- Check_Type(hash2, T_HASH);
-
- if (hash == hash2) return hash;
-
- if (RHASH_AR_TABLE_P(hash2)) {
- if (RHASH_AR_TABLE_P(hash)) ar_free_and_clear_table(hash);
- ar_copy(hash, hash2);
- if (RHASH_AR_TABLE_SIZE(hash))
- rb_hash_rehash(hash);
- }
- else if (RHASH_ST_TABLE_P(hash2)) {
- if (RHASH_ST_TABLE_P(hash)) st_free_table(RHASH_ST_TABLE(hash));
- RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(hash2)));
- if (RHASH_ST_TABLE(hash)->num_entries)
- rb_hash_rehash(hash);
- }
- else if (RHASH_AR_TABLE_P(hash)) {
- ar_clear(hash);
- }
- else if (RHASH_ST_TABLE_P(hash)) {
- st_clear(RHASH_ST_TABLE(hash));
- }
-
- COPY_DEFAULT(hash, hash2);
-
- return hash;
-}
-
/*
* call-seq:
* hsh.replace(other_hash) -> hsh
@@ -2868,26 +2825,35 @@ rb_hash_replace(VALUE hash, VALUE hash2)
{
rb_hash_modify_check(hash);
if (hash == hash2) return hash;
+ if (RHASH_ITER_LEV(hash) > 0) {
+ rb_raise(rb_eRuntimeError, "can't replace hash during iteration");
+ }
hash2 = to_hash(hash2);
COPY_DEFAULT(hash, hash2);
- rb_hash_clear(hash);
-
if (RHASH_AR_TABLE_P(hash)) {
- if (RHASH_AR_TABLE_P(hash2)) {
- ar_copy(hash, hash2);
- }
- else {
- goto st_to_st;
- }
+ if (RHASH_AR_TABLE_P(hash2)) {
+ ar_clear(hash);
+ }
+ else {
+ ar_free_and_clear_table(hash);
+ RHASH_ST_TABLE_SET(hash, st_init_table_with_size(RHASH_TYPE(hash2), RHASH_SIZE(hash2)));
+ }
}
else {
- if (RHASH_AR_TABLE_P(hash2)) ar_force_convert_table(hash2, __FILE__, __LINE__);
- st_to_st:
+ if (RHASH_AR_TABLE_P(hash2)) {
+ st_free_table(RHASH_ST_TABLE(hash));
+ RHASH_ST_CLEAR(hash);
+ }
+ else {
+ st_clear(RHASH_ST_TABLE(hash));
RHASH_TBL_RAW(hash)->type = RHASH_ST_TABLE(hash2)->type;
- rb_hash_foreach(hash2, replace_i, hash);
+ }
}
+ rb_hash_foreach(hash2, rb_hash_rehash_i, (VALUE)hash);
+
+ rb_gc_writebarrier_remember(hash);
return hash;
}
@@ -6143,7 +6109,7 @@ Init_Hash(void)
rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);
rb_define_singleton_method(rb_cHash, "try_convert", rb_hash_s_try_convert, 1);
rb_define_method(rb_cHash, "initialize", rb_hash_initialize, -1);
- rb_define_method(rb_cHash, "initialize_copy", rb_hash_initialize_copy, 1);
+ rb_define_method(rb_cHash, "initialize_copy", rb_hash_replace, 1);
rb_define_method(rb_cHash, "rehash", rb_hash_rehash, 0);
rb_define_method(rb_cHash, "to_hash", rb_hash_to_hash, 0);