diff --git a/hash.c b/hash.c index a74db258e4..a779c6f218 100644 --- a/hash.c +++ b/hash.c @@ -1482,7 +1482,9 @@ hash_copy(VALUE ret, VALUE hash) else { HASH_ASSERT(sizeof(st_table) <= sizeof(ar_table)); - RHASH_ST_TABLE_SET(ret, st_copy(RHASH_ST_TABLE(hash))); + RHASH_SET_ST_FLAG(ret); + st_replace(RHASH_ST_TABLE(ret), RHASH_ST_TABLE(hash)); + rb_gc_writebarrier_remember(ret); } return ret; @@ -1776,7 +1778,8 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass) } else { hash = hash_alloc(klass); - hash_copy(hash, tmp); + if (!RHASH_EMPTY_P(tmp)) + hash_copy(hash, tmp); return hash; } } diff --git a/include/ruby/st.h b/include/ruby/st.h index 0d42283364..ba69c066c9 100644 --- a/include/ruby/st.h +++ b/include/ruby/st.h @@ -162,6 +162,8 @@ void rb_st_cleanup_safe(st_table *, st_data_t); #define st_cleanup_safe rb_st_cleanup_safe void rb_st_clear(st_table *); #define st_clear rb_st_clear +st_table *rb_st_replace(st_table *new_tab, st_table *old_tab); +#define st_replace rb_st_replace st_table *rb_st_copy(st_table *); #define st_copy rb_st_copy CONSTFUNC(int rb_st_numcmp(st_data_t, st_data_t)); diff --git a/parser_st.c b/parser_st.c index b2df6cbad2..5d3148bc0d 100644 --- a/parser_st.c +++ b/parser_st.c @@ -109,6 +109,8 @@ nonempty_memcpy(void *dest, const void *src, size_t n) #define st_add_direct rb_parser_st_add_direct #undef st_insert2 #define st_insert2 rb_parser_st_insert2 +#undef st_replace +#define st_replace rb_parser_st_replace #undef st_copy #define st_copy rb_parser_st_copy #undef st_delete_safe diff --git a/parser_st.h b/parser_st.h index 85db3b5847..877b1e9051 100644 --- a/parser_st.h +++ b/parser_st.h @@ -137,6 +137,7 @@ void rb_parser_st_add_direct(parser_st_table *, parser_st_data_t, parser_st_data void rb_parser_st_free_table(parser_st_table *); void rb_parser_st_cleanup_safe(parser_st_table *, parser_st_data_t); void rb_parser_st_clear(parser_st_table *); +parser_st_table *rb_parser_st_replace(parser_st_table *, parser_st_table *); parser_st_table *rb_parser_st_copy(parser_st_table *); CONSTFUNC(int rb_parser_st_numcmp(parser_st_data_t, parser_st_data_t)); CONSTFUNC(parser_st_index_t rb_parser_st_numhash(parser_st_data_t)); diff --git a/st.c b/st.c index 7d44171e97..119a6964b4 100644 --- a/st.c +++ b/st.c @@ -1228,6 +1228,36 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value, return 1; } +/* Create a copy of old_tab into new_tab. */ +st_table * +st_replace(st_table *new_tab, st_table *old_tab) +{ + *new_tab = *old_tab; + if (old_tab->bins == NULL) + new_tab->bins = NULL; + else { + new_tab->bins = (st_index_t *) malloc(bins_size(old_tab)); +#ifndef RUBY + if (new_tab->bins == NULL) { + return NULL; + } +#endif + } + new_tab->entries = (st_table_entry *) malloc(get_allocated_entries(old_tab) + * sizeof(st_table_entry)); +#ifndef RUBY + if (new_tab->entries == NULL) { + return NULL; + } +#endif + MEMCPY(new_tab->entries, old_tab->entries, st_table_entry, + get_allocated_entries(old_tab)); + if (old_tab->bins != NULL) + MEMCPY(new_tab->bins, old_tab->bins, char, bins_size(old_tab)); + + return new_tab; +} + /* Create and return a copy of table OLD_TAB. */ st_table * st_copy(st_table *old_tab) @@ -1239,30 +1269,12 @@ st_copy(st_table *old_tab) if (new_tab == NULL) return NULL; #endif - *new_tab = *old_tab; - if (old_tab->bins == NULL) - new_tab->bins = NULL; - else { - new_tab->bins = (st_index_t *) malloc(bins_size(old_tab)); -#ifndef RUBY - if (new_tab->bins == NULL) { - free(new_tab); - return NULL; - } -#endif - } - new_tab->entries = (st_table_entry *) malloc(get_allocated_entries(old_tab) - * sizeof(st_table_entry)); -#ifndef RUBY - if (new_tab->entries == NULL) { + + if (st_replace(new_tab, old_tab) == NULL) { st_free_table(new_tab); return NULL; } -#endif - MEMCPY(new_tab->entries, old_tab->entries, st_table_entry, - get_allocated_entries(old_tab)); - if (old_tab->bins != NULL) - MEMCPY(new_tab->bins, old_tab->bins, char, bins_size(old_tab)); + return new_tab; }