diff --git a/common.mk b/common.mk index 65dda40102..1f692286fb 100644 --- a/common.mk +++ b/common.mk @@ -17146,6 +17146,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 c05e6e853c..f41120ecaf 100644 --- a/st.c +++ b/st.c @@ -103,12 +103,14 @@ #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 "internal/st.h" +#include "ruby_assert.h" #endif #include @@ -116,7 +118,6 @@ #include #endif #include -#include #ifdef __GNUC__ #define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p) @@ -314,15 +315,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. */ @@ -784,6 +790,8 @@ rebuild_table_with(st_table *const new_tab, st_table *const tab) new_tab->num_entries++; ni++; } + + assert(new_tab->num_entries == tab->num_entries); } static void @@ -1173,6 +1181,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]; @@ -1190,7 +1200,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 87eb1912d9..1e1b37f981 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -2389,4 +2389,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