Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save gstark/436761 to your computer and use it in GitHub Desktop.

Select an option

Save gstark/436761 to your computer and use it in GitHub Desktop.
From a49caebabb688fc83b01a03e3e970a0228de6f8a Mon Sep 17 00:00:00 2001
From: Gavin Stark <[email protected]>
Date: Sun, 13 Jun 2010 00:54:50 -0400
Subject: [PATCH 1/2] Fixing Hash constructor to not rehash keys.
---
kernel/common/hash.rb | 48 ++++++++++++++++++++++++++++++++++++++----------
1 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/kernel/common/hash.rb b/kernel/common/hash.rb
index 3ee4100..2108852 100644
--- a/kernel/common/hash.rb
+++ b/kernel/common/hash.rb
@@ -78,10 +78,8 @@ class Hash
def self.[](*args)
if args.size == 1
obj = args.first
- if obj.kind_of? Hash
- return new.replace(obj)
- elsif obj.respond_to? :to_hash
- return new.replace(Type.coerce_to(obj, Hash, :to_hash))
+ if obj.kind_of?(Hash) || obj.respond_to?(:to_hash)
+ return new.send(:replace_without_key_hashing,obj)
elsif obj.is_a?(Array) # See redmine # 1385
h = {}
args.first.each do |arr|
@@ -190,12 +188,7 @@ class Hash
end
end
- def []=(key, value)
- Ruby.check_frozen
-
- redistribute @entries if @size > @max_entries
-
- key_hash = key.hash
+ def __store_with_precomputed_hash__(key, value, key_hash)
index = key_hash & @mask # key_index key_hash
entry = @entries[index]
@@ -222,6 +215,17 @@ class Hash
value
end
+ private :__store_with_precomputed_hash__
+
+ def []=(key, value)
+ Ruby.check_frozen
+
+ redistribute @entries if @size > @max_entries
+
+ key_hash = key.hash
+
+ __store_with_precomputed_hash__(key,value,key_hash)
+ end
alias_method :store, :[]=
@@ -586,6 +590,30 @@ class Hash
end
end
+ def replace_without_key_hashing(other)
+ Ruby.check_frozen
+
+ other = Type.coerce_to other, Hash, :to_hash
+ return self if self.equal? other
+
+ setup
+ i = other.to_iter
+ while entry = i.next(entry)
+ __store_with_precomputed_hash__ entry.key, entry.value, entry.key_hash
+ end
+
+ if other.default_proc
+ @default = other.default_proc
+ @default_proc = true
+ else
+ @default = other.default
+ @default_proc = false
+ end
+
+ self
+ end
+ private :replace_without_key_hashing
+
# packed! [:@capacity, :@mask, :@max_entries, :@size, :@entries]
# Sets the underlying data structures.
--
1.7.1
From a9f3316af571b45511dfe1e8852ccef2c82f720c Mon Sep 17 00:00:00 2001
From: Gavin Stark <[email protected]>
Date: Sun, 13 Jun 2010 00:55:02 -0400
Subject: [PATCH 2/2] Adding new Hash constructor specs
---
spec/ruby/core/hash/constructor_spec.rb | 34 +++++++++++++++++++++++++++++++
1 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/spec/ruby/core/hash/constructor_spec.rb b/spec/ruby/core/hash/constructor_spec.rb
index 644b24a..9c2d727 100644
--- a/spec/ruby/core/hash/constructor_spec.rb
+++ b/spec/ruby/core/hash/constructor_spec.rb
@@ -2,6 +2,40 @@ require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
describe "Hash.[]" do
+ it "does not call #hash on elements when provided a hash" do
+ x = mock('x')
+ y = mock('y')
+
+ other = {x => 1, y => 2}
+
+ x.should_not_receive(:hash)
+ y.should_not_receive(:hash)
+
+ hash_class[other]
+ end
+
+ it "does not call #hash on elements when provided a non-Hash object responding to to_hash" do
+ x = mock('x')
+ y = mock('y')
+
+ existing_hash = {x => 1, y => 2}
+
+ x.should_not_receive(:hash)
+ y.should_not_receive(:hash)
+
+ class NotAHash
+ def initialize(existing_hash)
+ @existing_hash = existing_hash
+ end
+
+ def to_hash
+ @existing_hash
+ end
+ end
+
+ hash_class[NotAHash.new(existing_hash)]
+ end
+
it "creates a Hash; values can be provided as the argument list" do
hash_class[:a, 1, :b, 2].should == new_hash(:a => 1, :b => 2)
hash_class[].should == new_hash
--
1.7.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment