summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
authorJohn Hawthorn <john@hawthorn.email>2025-03-05 14:21:48 -0800
committernagachika <nagachika@ruby-lang.org>2025-03-08 16:43:58 +0900
commitef523984f0c16ec3ec1ea3b5b74ed24e6b03d561 (patch)
tree0d02afbf7ce016005a50a3cbbc8cf24a6ebedff9 /test/ruby
parentecb9f7ef372c70c3e4fa81a5002533814a94aa86 (diff)
Replace tombstone when converting AR to ST hash
[Bug #21170] st_table reserves -1 as a special hash value to indicate that an entry has been deleted. So that that's a valid value to be returned from the hash function, do_hash replaces -1 with 0 so that it is not mistaken for the sentinel. Previously, when upgrading an AR table to an ST table, rb_st_add_direct_with_hash was used which did not perform the same conversion, this could lead to a hash in a broken state where one if its entries which was supposed to exist being marked as a tombstone. The hash could then become further corrupted when the ST table required resizing as the falsely tombstoned entry would be skipped but it would be counted in num entries, leading to an uninitialized entry at index 15. In most cases this will be really rare, unless using a very poorly implemented custom hash function. This also adds two debug assertions, one that st_add_direct_with_hash does not receive the reserved hash value, and a second in rebuild_table_with, which ensures that after we rebuild/compact a table it contains the expected number of elements. Co-authored-by: Alan Wu <alanwu@ruby-lang.org>
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/test_hash.rb14
1 files changed, 14 insertions, 0 deletions
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