diff options
-rw-r--r-- | hash.c | 29 | ||||
-rw-r--r-- | test/ruby/test_hash.rb | 58 | ||||
-rw-r--r-- | version.h | 6 |
3 files changed, 87 insertions, 6 deletions
@@ -607,10 +607,14 @@ ar_try_convert_table(VALUE hash) { st_table *new_tab; ar_table_entry *entry; - const unsigned size = RHASH_AR_TABLE_SIZE(hash); + unsigned size; st_index_t i; - if (!RHASH_AR_TABLE_P(hash) || size < RHASH_AR_TABLE_MAX_SIZE) { + if (!RHASH_AR_TABLE_P(hash)) return; + + size = RHASH_AR_TABLE_SIZE(hash); + + if (size < RHASH_AR_TABLE_MAX_SIZE) { return; } @@ -824,6 +828,11 @@ ar_update(VALUE hash, st_data_t key, st_data_t value = 0, old_key; st_hash_t hash_value = do_hash(key); + if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) { + /* `#hash` changes ar_table -> st_table */ + return -1; + } + if (RHASH_AR_TABLE_SIZE(hash) > 0) { bin = find_entry(hash, hash_value, key); existing = (bin != RHASH_AR_TABLE_MAX_BOUND) ? TRUE : FALSE; @@ -872,6 +881,11 @@ ar_insert(VALUE hash, st_data_t key, st_data_t value) unsigned bin = RHASH_AR_TABLE_BOUND(hash); st_hash_t hash_value = do_hash(key); + if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) { + /* `#hash` changes ar_table -> st_table */ + return -1; + } + hash_ar_table(hash); /* prepare ltbl */ bin = find_entry(hash, hash_value, key); @@ -900,7 +914,12 @@ static int ar_lookup(VALUE hash, st_data_t key, st_data_t *value) { st_hash_t hash_value = do_hash(key); - unsigned bin = find_entry(hash, hash_value, key); + unsigned bin; + if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) { + /* `#hash` changes ar_table -> st_table */ + return st_lookup(RHASH_ST_TABLE(hash), key, value); + } + bin = find_entry(hash, hash_value, key); if (bin == RHASH_AR_TABLE_MAX_BOUND) { return 0; @@ -920,6 +939,10 @@ ar_delete(VALUE hash, st_data_t *key, st_data_t *value) unsigned bin; st_hash_t hash_value = do_hash(*key); + if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) { + /* `#hash` changes ar_table -> st_table */ + return st_delete(RHASH_ST_TABLE(hash), key, value); + } bin = find_entry(hash, hash_value, *key); diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index c934d1015e..e1b6e7257e 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1733,4 +1733,62 @@ class TestHash < Test::Unit::TestCase super end end + + def test_ar2st + # insert + obj = Object.new + obj.instance_variable_set(:@h, h = {}) + def obj.hash + 10.times{|i| @h[i] = i} + 0 + end + def obj.inspect + 'test' + end + h[obj] = true + assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect + + # delete + obj = Object.new + obj.instance_variable_set(:@h, h = {}) + def obj.hash + 10.times{|i| @h[i] = i} + 0 + end + def obj.inspect + 'test' + end + def obj.eql? other + other.class == Object + end + obj2 = Object.new + def obj2.hash + 0 + end + + h[obj2] = true + h.delete obj + assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9}', h.inspect + + # lookup + obj = Object.new + obj.instance_variable_set(:@h, h = {}) + def obj.hash + 10.times{|i| @h[i] = i} + 0 + end + def obj.inspect + 'test' + end + def obj.eql? other + other.class == Object + end + obj2 = Object.new + def obj2.hash + 0 + end + + h[obj2] = true + assert_equal true, h[obj] + end end @@ -1,10 +1,10 @@ #define RUBY_VERSION "2.6.6" -#define RUBY_RELEASE_DATE "2020-03-20" -#define RUBY_PATCHLEVEL 143 +#define RUBY_RELEASE_DATE "2020-03-30" +#define RUBY_PATCHLEVEL 144 #define RUBY_RELEASE_YEAR 2020 #define RUBY_RELEASE_MONTH 3 -#define RUBY_RELEASE_DAY 20 +#define RUBY_RELEASE_DAY 30 #include "ruby/version.h" |