summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common.mk1
-rw-r--r--st.c22
-rw-r--r--test/ruby/test_hash.rb14
3 files changed, 31 insertions, 6 deletions
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 <assert.h>
#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 <stdio.h>
@@ -116,7 +118,6 @@
#include <stdlib.h>
#endif
#include <string.h>
-#include <assert.h>
#ifdef __GNUC__
#define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p)
@@ -314,17 +315,22 @@ static const struct st_features features[] = {
#define RESERVED_HASH_VAL (~(st_hash_t) 0)
#define RESERVED_HASH_SUBSTITUTION_VAL ((st_hash_t) 0)
-/* Return hash value of KEY for table TAB. */
static inline st_hash_t
-do_hash(st_data_t key, st_table *tab)
+normalize_hash_value(st_hash_t hash)
{
- 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 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);
+ return normalize_hash_value(hash);
+}
+
/* Power of 2 defining the minimal number of allocated entries. */
#define MINIMAL_POWER2 2
@@ -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