diff --git a/common.mk b/common.mk index 70b66b11f4..fe8c5ed743 100644 --- a/common.mk +++ b/common.mk @@ -16956,6 +16956,7 @@ st.$(OBJEXT): {$(VPATH)}internal/variable.h st.$(OBJEXT): {$(VPATH)}internal/warning_push.h st.$(OBJEXT): {$(VPATH)}internal/xmalloc.h st.$(OBJEXT): {$(VPATH)}missing.h +st.$(OBJEXT): {$(VPATH)}ruby_assert.h st.$(OBJEXT): {$(VPATH)}st.c st.$(OBJEXT): {$(VPATH)}st.h st.$(OBJEXT): {$(VPATH)}subst.h diff --git a/st.c b/st.c index b921b1bf3b..50bfd4bfbb 100644 --- a/st.c +++ b/st.c @@ -103,11 +103,13 @@ #ifdef NOT_RUBY #include "regint.h" #include "st.h" +#include #elif defined RUBY_EXPORT #include "internal.h" #include "internal/bits.h" #include "internal/hash.h" #include "internal/sanitizers.h" +#include "ruby_assert.h" #endif #include @@ -115,7 +117,6 @@ #include #endif #include -#include #ifdef __GNUC__ #define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p) @@ -313,15 +314,20 @@ static const struct st_features features[] = { #define RESERVED_HASH_VAL (~(st_hash_t) 0) #define RESERVED_HASH_SUBSTITUTION_VAL ((st_hash_t) 0) +static inline st_hash_t +normalize_hash_value(st_hash_t hash) +{ + /* RESERVED_HASH_VAL is used for a deleted entry. Map it into + another value. Such mapping should be extremely rare. */ + return hash == RESERVED_HASH_VAL ? RESERVED_HASH_SUBSTITUTION_VAL : hash; +} + /* Return hash value of KEY for table TAB. */ static inline st_hash_t do_hash(st_data_t key, st_table *tab) { st_hash_t hash = (st_hash_t)(tab->type->hash)(key); - - /* RESERVED_HASH_VAL is used for a deleted entry. Map it into - another value. Such mapping should be extremely rare. */ - return hash == RESERVED_HASH_VAL ? RESERVED_HASH_SUBSTITUTION_VAL : hash; + return normalize_hash_value(hash); } /* Power of 2 defining the minimal number of allocated entries. */ @@ -779,6 +785,9 @@ rebuild_table_with(st_table *new_tab, st_table *tab) new_tab->num_entries++; ni++; } + + assert(new_tab->num_entries == tab->num_entries); + if (new_tab != tab) { tab->entry_power = new_tab->entry_power; tab->bin_power = new_tab->bin_power; @@ -1160,6 +1169,8 @@ st_add_direct_with_hash(st_table *tab, st_index_t ind; st_index_t bin_ind; + assert(hash != RESERVED_HASH_VAL); + rebuild_table_if_necessary(tab); ind = tab->entries_bound++; entry = &tab->entries[ind]; @@ -1177,7 +1188,7 @@ void rb_st_add_direct_with_hash(st_table *tab, st_data_t key, st_data_t value, st_hash_t hash) { - st_add_direct_with_hash(tab, key, value, hash); + st_add_direct_with_hash(tab, key, value, normalize_hash_value(hash)); } /* Insert (KEY, VALUE) into table TAB. The table should not have diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index a063518190..2d36556953 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -2352,4 +2352,18 @@ class TestHashOnly < Test::Unit::TestCase end end; end + + def test_ar_to_st_reserved_value + klass = Class.new do + attr_reader :hash + def initialize(val) = @hash = val + end + + values = 0.downto(-16).to_a + hash = {} + values.each do |val| + hash[klass.new(val)] = val + end + assert_equal values, hash.values, "[ruby-core:121239] [Bug #21170]" + end end